MP4Reader.cpp 14.4 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * MIT License
xzl committed
3
 *
xiongziliang committed
4
 * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
xiongziliang committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
xzl committed
25 26
 */

xiongziliang committed
27
#include "MP4Reader.h"
xiongziliang committed
28
#include "Common/config.h"
xiongzilaing committed
29
#include "Util/mini.h"
xiongziliang committed
30
#include "Util/File.h"
xzl committed
31
#include "Http/HttpSession.h"
xiongziliang committed
32 33
#include "Extension/AAC.h"
#include "Extension/H264.h"
34
#include "Thread/WorkThreadPool.h"
xiongziliang committed
35

xiongziliang committed
36
using namespace toolkit;
xzl committed
37

xiongziliang committed
38
namespace mediakit {
xzl committed
39

40
#ifdef ENABLE_MP4V2
xiongziliang committed
41
MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string &strId,const string &filePath ) {
42
    _poller = WorkThreadPool::Instance().getPoller();
xiongziliang committed
43 44
    auto strFileName = filePath;
    if(strFileName.empty()){
45
        GET_CONFIG(string,recordPath,Record::kFilePath);
46 47
        GET_CONFIG(bool,enableVhost,General::kEnableVhost);
        if(enableVhost){
48
            strFileName = strVhost + "/" + strApp + "/" + strId;
49
        }else{
50
            strFileName = strApp + "/" + strId;
51
        }
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
        strFileName = File::absolutePath(strFileName,recordPath);
    }

    _hMP4File = MP4Read(strFileName.data());
    if(_hMP4File == MP4_INVALID_FILE_HANDLE){
        throw runtime_error(StrPrinter << "打开MP4文件失败:" << strFileName << endl);
    }
    _video_trId = MP4FindTrackId(_hMP4File, 0, MP4_VIDEO_TRACK_TYPE, 0);
    if(_video_trId != MP4_INVALID_TRACK_ID){
         if(strcmp(MP4GetTrackMediaDataName(_hMP4File, _video_trId),"avc1") ==0){
            auto _video_timescale 		= MP4GetTrackTimeScale(_hMP4File, _video_trId);
            auto _video_duration 		= MP4GetTrackDuration(_hMP4File, _video_trId);
            _video_num_samples     = MP4GetTrackNumberOfSamples(_hMP4File, _video_trId);
            _video_sample_max_size = MP4GetTrackMaxSampleSize(_hMP4File, _video_trId);
            _video_width 			= MP4GetTrackVideoWidth(_hMP4File, _video_trId);
            _video_height 			= MP4GetTrackVideoHeight(_hMP4File, _video_trId);
            _video_framerate       = MP4GetTrackVideoFrameRate(_hMP4File, _video_trId);
            _pcVideoSample = std::shared_ptr<uint8_t> (new uint8_t[_video_sample_max_size],[](uint8_t *ptr){
                delete [] ptr;
            });
            uint8_t **seqheader;
            uint8_t **pictheader;
            uint32_t *pictheadersize;
            uint32_t *seqheadersize;
            uint32_t ix;
            if(MP4GetTrackH264SeqPictHeaders(_hMP4File, _video_trId, &seqheader, &seqheadersize, &pictheader, &pictheadersize)){
                for (ix = 0; seqheadersize[ix] != 0; ix++) {
                    _strSps.assign((char *)(seqheader[ix]), seqheadersize[ix]);
                    float framerate;
                    getAVCInfo(_strSps, (int &)_video_width, (int &)_video_height, framerate);
                    _video_framerate = framerate;
                    _strSps = string("\x0\x0\x0\x1",4) + _strSps;
                    MP4Free(seqheader[ix]);
                }
                MP4Free(seqheader);
                MP4Free(seqheadersize);
                for (ix = 0; pictheadersize[ix] != 0; ix++) {
                    _strPps.assign("\x0\x0\x0\x1",4);
                    _strPps.append((char *)(pictheader[ix]), pictheadersize[ix]);
                    MP4Free(pictheader[ix]);
                }
                MP4Free(pictheader);
                MP4Free(pictheadersize);
            }
            _video_ms = 1000.0 * _video_duration / _video_timescale;
            /*InfoL 	<< "\r\n"
                    << _video_ms << "\r\n"
                    << _video_num_samples << "\r\n"
                    << _video_framerate << "\r\n"
                    << _video_width << "\r\n"
                    << _video_height << "\r\n";*/
        } else {
            //如果不是h264,则忽略
            _video_trId = MP4_INVALID_TRACK_ID;
        }
xiongziliang committed
107
    }
xzl committed
108

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153

    _audio_trId = MP4FindTrackId(_hMP4File, 0, MP4_AUDIO_TRACK_TYPE, 0);
    if (_audio_trId != MP4_INVALID_TRACK_ID) {
        if (strcmp(MP4GetTrackMediaDataName(_hMP4File, _audio_trId), "mp4a") == 0) {
            _audio_sample_rate = MP4GetTrackTimeScale(_hMP4File, _audio_trId);
            auto _audio_duration = MP4GetTrackDuration(_hMP4File, _audio_trId);
            _audio_num_samples = MP4GetTrackNumberOfSamples(_hMP4File,_audio_trId);
            _audio_num_channels = MP4GetTrackAudioChannels(_hMP4File, _audio_trId);
            _audio_sample_max_size = MP4GetTrackMaxSampleSize(_hMP4File,_audio_trId);
            uint8_t *ppConfig;
            uint32_t pConfigSize;
            if(MP4GetTrackESConfiguration(_hMP4File,_audio_trId,&ppConfig,&pConfigSize)){
                _strAacCfg.assign((char *)ppConfig, pConfigSize);
                makeAdtsHeader(_strAacCfg, _adts);
                writeAdtsHeader(_adts,_adts.buffer);
                getAACInfo(_adts, (int &)_audio_sample_rate, (int &)_audio_num_channels);
                MP4Free(ppConfig);
            }
            _audio_ms = 1000.0 * _audio_duration / _audio_sample_rate;
            /*InfoL 	<< "\r\n"
                    << _audio_ms << "\r\n"
                    << _audio_num_samples << "\r\n"
                    << _audio_num_channels << "\r\n"
                    << _audio_sample_rate << "\r\n";*/
        }else{
            _audio_trId = MP4_INVALID_TRACK_ID;
        }
    }
    if(_audio_trId == MP4_INVALID_TRACK_ID && _video_trId == MP4_INVALID_TRACK_ID){
        MP4Close(_hMP4File);
        _hMP4File = MP4_INVALID_FILE_HANDLE;
        throw runtime_error(StrPrinter << "该MP4文件音视频格式不支持:" << strFileName << endl);
    }

    _iDuration	= MAX(_video_ms,_audio_ms);
    _mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost, strApp, strId, _iDuration / 1000.0, true, true, false, false));
    if (_audio_trId != MP4_INVALID_TRACK_ID) {
        AACTrack::Ptr track = std::make_shared<AACTrack>(_strAacCfg);
        _mediaMuxer->addTrack(track);
    }

    if (_video_trId != MP4_INVALID_TRACK_ID) {
        H264Track::Ptr track = std::make_shared<H264Track>(_strSps,_strPps);
        _mediaMuxer->addTrack(track);
    }
154 155 156

    //添加完毕所有track,防止单track情况下最大等待3秒
    _mediaMuxer->addTrackCompleted();
xzl committed
157 158 159
}


xiongziliang committed
160
MP4Reader::~MP4Reader() {
161 162 163 164
    if (_hMP4File != MP4_INVALID_FILE_HANDLE) {
        MP4Close(_hMP4File);
        _hMP4File = MP4_INVALID_FILE_HANDLE;
    }
xzl committed
165 166 167
}


xiongziliang committed
168
void MP4Reader::startReadMP4() {
169
    auto strongSelf = shared_from_this();
170
    GET_CONFIG(uint32_t,sampleMS,Record::kSampleMS);
171

172 173 174
    _timer = std::make_shared<Timer>(sampleMS / 1000.0f,[strongSelf](){
        return strongSelf->readSample(0,false);
    }, _poller);
175

176
    //先读sampleMS毫秒的数据用于产生MediaSouce
177 178
    readSample(sampleMS, false);
    _mediaMuxer->setListener(strongSelf);
179
}
xiongziliang committed
180
 bool MP4Reader::seekTo(MediaSource &sender,uint32_t ui32Stamp){
181 182
     seek(ui32Stamp);
     return true;
183
}
xiongziliang committed
184
bool MP4Reader::close(MediaSource &sender,bool force){
185
    if(!_mediaMuxer || (!force && _mediaMuxer->totalReaderCount())){
186 187
        return false;
    }
188
    _timer.reset();
xiongziliang committed
189
    WarnL << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force;
190 191
    return true;
}
xzl committed
192

193
int MP4Reader::totalReaderCount(MediaSource &sender) {
194
    return _mediaMuxer ? _mediaMuxer->totalReaderCount() : sender.readerCount();
195 196
}

xiongziliang committed
197
bool MP4Reader::readSample(int iTimeInc,bool justSeekSyncFrame) {
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
    TimeTicker();
    lock_guard<recursive_mutex> lck(_mtx);
    auto bFlag0 = readVideoSample(iTimeInc,justSeekSyncFrame);//数据没读完
    auto bFlag1 = readAudioSample(iTimeInc,justSeekSyncFrame);//数据没读完
    auto bFlag2 = _mediaMuxer->totalReaderCount() > 0;//读取者大于0
    if((bFlag0 || bFlag1) && bFlag2){
        _alive.resetTime();
    }
    //重头开始循环读取
    GET_CONFIG(bool,fileRepeat,Record::kFileRepeat);
    if (fileRepeat && !bFlag0 && !bFlag1) {
        seek(0);
    }
    //DebugL << "alive ...";
    //3秒延时关闭
    return  _alive.elapsedTime() <  3 * 1000;
xzl committed
214
}
xiongziliang committed
215
inline bool MP4Reader::readVideoSample(int iTimeInc,bool justSeekSyncFrame) {
216 217 218 219 220 221 222 223
    if (_video_trId != MP4_INVALID_TRACK_ID) {
        auto iNextSample = getVideoSampleId(iTimeInc);
        MP4SampleId iIdx = _video_current;
        for (; iIdx < iNextSample; iIdx++) {
            uint8_t *pBytes = _pcVideoSample.get();
            uint32_t numBytes = _video_sample_max_size;
            MP4Duration pRenderingOffset;
            if(MP4ReadSample(_hMP4File, _video_trId, iIdx + 1, &pBytes, &numBytes,NULL,NULL,&pRenderingOffset,&_bSyncSample)){
224 225 226 227
                if (!justSeekSyncFrame) {
                    uint32_t dts = (double) _video_ms * iIdx / _video_num_samples;
                    uint32_t pts = dts + pRenderingOffset / 90;
                    uint32_t iOffset = 0;
228 229 230 231
                    while (iOffset < numBytes) {
                        uint32_t iFrameLen;
                        memcpy(&iFrameLen,pBytes + iOffset,4);
                        iFrameLen = ntohl(iFrameLen);
xzl committed
232 233 234
                        if(iFrameLen + iOffset + 4> numBytes){
                            break;
                        }
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
                        memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4);
                        writeH264(pBytes + iOffset, iFrameLen + 4, dts, pts);
                        iOffset += (iFrameLen + 4);
                    }
                }else if(_bSyncSample){
                    break;
                }
            }else{
                ErrorL << "读取视频失败:" << iIdx + 1;
            }
        }
        _video_current = iIdx;
        return _video_current < _video_num_samples;
    }
    return false;
xzl committed
250 251
}

xiongziliang committed
252
inline bool MP4Reader::readAudioSample(int iTimeInc,bool justSeekSyncFrame) {
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
    if (_audio_trId != MP4_INVALID_TRACK_ID) {
        auto iNextSample = getAudioSampleId(iTimeInc);
        for (auto i = _audio_current; i < iNextSample; i++) {
            uint32_t numBytes = _audio_sample_max_size;
            uint8_t *pBytes = _adts.buffer + 7;
            if(MP4ReadSample(_hMP4File, _audio_trId, i + 1, &pBytes, &numBytes)){
                if (!justSeekSyncFrame) {
                    uint32_t dts = (double) _audio_ms * i / _audio_num_samples;
                    _adts.aac_frame_length = 7 + numBytes;
                    writeAdtsHeader(_adts, _adts.buffer);
                    writeAAC(_adts.buffer, _adts.aac_frame_length, dts);
                }
            }else{
                ErrorL << "读取音频失败:" << i+ 1;
            }
        }
        _audio_current = iNextSample;
        return _audio_current < _audio_num_samples;
    }
    return false;
xzl committed
273 274
}

xiongziliang committed
275
inline void MP4Reader::writeH264(uint8_t *pucData,int iLen,uint32_t dts,uint32_t pts) {
276
    _mediaMuxer->inputFrame(std::make_shared<H264FrameNoCacheAble>((char*)pucData,iLen,dts,pts));
xzl committed
277 278
}

xiongziliang committed
279
inline void MP4Reader::writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp) {
280
    _mediaMuxer->inputFrame(std::make_shared<AACFrameNoCacheAble>((char*)pucData,iLen,uiStamp));
xzl committed
281 282
}

xiongziliang committed
283
inline MP4SampleId MP4Reader::getVideoSampleId(int iTimeInc ) {
284 285 286
    MP4SampleId video_current = (double)_video_num_samples *  (_iSeekTime + _ticker.elapsedTime() + iTimeInc) / _video_ms;
    video_current = MAX(0,MIN(_video_num_samples, video_current));
    return video_current;
xzl committed
287 288 289

}

xiongziliang committed
290
inline MP4SampleId MP4Reader::getAudioSampleId(int iTimeInc) {
291 292 293
    MP4SampleId audio_current = (double)_audio_num_samples * (_iSeekTime + _ticker.elapsedTime() + iTimeInc)  / _audio_ms ;
    audio_current = MAX(0,MIN(_audio_num_samples,audio_current));
    return audio_current;
xzl committed
294
}
xiongziliang committed
295
inline void MP4Reader::setSeekTime(uint32_t iSeekTime){
296 297 298 299 300 301 302 303
    _iSeekTime = MAX(0, MIN(iSeekTime,_iDuration));
    _ticker.resetTime();
    if (_audio_trId != MP4_INVALID_TRACK_ID) {
        _audio_current = getAudioSampleId();
    }
    if (_video_trId != MP4_INVALID_TRACK_ID) {
        _video_current = getVideoSampleId();
    }
xzl committed
304 305
}

xiongziliang committed
306
inline uint32_t MP4Reader::getVideoCurrentTime(){
307
    return (double)_video_current * _video_ms /_video_num_samples;
xzl committed
308
}
xiongziliang committed
309
void MP4Reader::seek(uint32_t iSeekTime,bool bReStart){
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    lock_guard<recursive_mutex> lck(_mtx);
    if(iSeekTime == 0 || _video_trId == MP4_INVALID_TRACK_ID){
        setSeekTime(iSeekTime);
    }else{
        setSeekTime(iSeekTime - 5000);
        //在之后的10秒查找关键帧
        readVideoSample(10000, true);
        if (_bSyncSample) {
            //找到关键帧
            auto iIdr =  _video_current;
            setSeekTime(getVideoCurrentTime());
            _video_current = iIdr;
        }else{
            //未找到关键帧
            setSeekTime(iSeekTime);
        }
    }
    _mediaMuxer->setTimeStamp(_iSeekTime);

    if(bReStart){
        _timer.reset();
        startReadMP4();
    }
xzl committed
333 334
}

335
#endif //ENABLE_MP4V2
xzl committed
336 337 338



xiongziliang committed
339
MediaSource::Ptr MP4Reader::onMakeMediaSource(const string &strSchema,
340 341 342 343 344
                                                const string &strVhost,
                                                const string &strApp,
                                                const string &strId,
                                                const string &filePath,
                                                bool checkApp ){
345
#ifdef ENABLE_MP4V2
346
    GET_CONFIG(string,appName,Record::kAppName);
xiongziliang committed
347
    if (checkApp && strApp != appName) {
348 349 350 351 352 353 354 355 356 357
        return nullptr;
    }
    try {
        MP4Reader::Ptr pReader(new MP4Reader(strVhost,strApp, strId,filePath));
        pReader->startReadMP4();
        return MediaSource::find(strSchema,strVhost,strApp, strId, false);
    } catch (std::exception &ex) {
        WarnL << ex.what();
        return nullptr;
    }
xzl committed
358
#else
359
    return nullptr;
360
#endif //ENABLE_MP4V2
xzl committed
361 362 363 364
}



xiongziliang committed
365
} /* namespace mediakit */