MP4Muxer.cpp 13.1 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
xiongziliang committed
3
 *
4
 * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
xiongziliang committed
5
 *
xiongziliang committed
6 7 8
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
xiongziliang committed
9 10
 */

xiongziliang committed
11
#ifdef ENABLE_MP4
xiongziliang committed
12 13
#include "MP4Muxer.h"
#include "Util/File.h"
14
#include "Extension/H264.h"
xiongziliang committed
15 16
namespace mediakit{

17
MP4Muxer::MP4Muxer() {}
xiongziliang committed
18

xiongziliang committed
19 20 21 22
MP4Muxer::~MP4Muxer() {
    closeMP4();
}

23
void MP4Muxer::openMP4(const string &file){
xiongziliang committed
24
    closeMP4();
25 26 27 28 29 30
    _file_name = file;
    _mp4_file = std::make_shared<MP4FileDisk>();
    _mp4_file->openFile(_file_name.data(), "wb+");
}

MP4FileIO::Writer MP4Muxer::createWriter(){
xiongziliang committed
31
    GET_CONFIG(bool, mp4FastStart, Record::kFastStart);
32
    return _mp4_file->createWriter(mp4FastStart ? MOV_FLAG_FASTSTART : 0, false);
xiongziliang committed
33
}
34

xiongziliang committed
35
void MP4Muxer::closeMP4(){
36 37
    MP4MuxerInterface::resetTracks();
    _mp4_file = nullptr;
xiongziliang committed
38 39
}

xiongziliang committed
40
void MP4Muxer::resetTracks() {
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
    MP4MuxerInterface::resetTracks();
    openMP4(_file_name);
}

/////////////////////////////////////////// MP4MuxerInterface /////////////////////////////////////////////

void MP4MuxerInterface::saveSegment(){
    mp4_writer_save_segment(_mov_writter.get());
}

void MP4MuxerInterface::initSegment(){
    mp4_writer_init_segment(_mov_writter.get());
}

bool MP4MuxerInterface::haveVideo() const{
    return _have_video;
}

void MP4MuxerInterface::resetTracks() {
xiongziliang committed
60
    _started = false;
61
    _have_video = false;
62 63 64
    _mov_writter = nullptr;
    _frameCached.clear();
    _codec_to_trackid.clear();
xiongziliang committed
65
}
xiongziliang committed
66

67
void MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) {
xiongziliang committed
68 69
    auto it = _codec_to_trackid.find(frame->getCodecId());
    if(it == _codec_to_trackid.end()){
70
        //该Track不存在或初始化失败
xiongziliang committed
71 72 73
        return;
    }

74
    if (!_started) {
xiongziliang committed
75 76 77 78
        //该逻辑确保含有视频时,第一帧为关键帧
        if (_have_video && !frame->keyFrame()) {
            //含有视频,但是不是关键帧,那么前面的帧丢弃
            return;
79
        }
xiongziliang committed
80 81
        //开始写文件
        _started = true;
82 83
    }

84 85
    //mp4文件时间戳需要从0开始
    auto &track_info = it->second;
86
    int64_t dts_out, pts_out;
87

88
    switch (frame->getCodecId()) {
89 90 91 92 93 94
        case CodecH264: {
            int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
            if(type == H264Frame::NAL_SEI){
                break;
            }
        }
95 96 97 98 99 100 101 102 103
        case CodecH265: {
            //这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理,
            if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) {
                Frame::Ptr back = _frameCached.back();
                //求相对时间戳
                track_info.stamp.revise(back->dts(), back->pts(), dts_out, pts_out);

                if (_frameCached.size() != 1) {
                    //缓存中有多帧,需要按照mp4格式合并一起
104 105
                    BufferLikeString merged;
                    merged.reserve(back->size() + 1024);
106
                    _frameCached.for_each([&](const Frame::Ptr &frame) {
107
                        uint32_t nalu_size = (uint32_t)(frame->size() - frame->prefixSize());
108 109 110 111
                        nalu_size = htonl(nalu_size);
                        merged.append((char *) &nalu_size, 4);
                        merged.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
                    });
xiongziliang committed
112
                    mp4_writer_write(_mov_writter.get(),
113 114 115 116 117
                                       track_info.track_id,
                                       merged.data(),
                                       merged.size(),
                                       pts_out,
                                       dts_out,
xiongziliang committed
118
                                       back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0);
119 120
                } else {
                    //缓存中只有一帧视频
xiongziliang committed
121
                    mp4_writer_write_l(_mov_writter.get(),
122 123 124 125 126 127
                                       track_info.track_id,
                                       back->data() + back->prefixSize(),
                                       back->size() - back->prefixSize(),
                                       pts_out,
                                       dts_out,
                                       back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0,
xiongziliang committed
128
                                       1/*需要生成头4个字节的MP4格式start code*/);
129 130 131 132 133 134 135 136 137
                }
                _frameCached.clear();
            }
            //缓存帧,时间戳相同的帧合并一起写入mp4
            _frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
        }
            break;
        default: {
            track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out);
xiongziliang committed
138 139 140 141 142 143 144
            mp4_writer_write(_mov_writter.get(),
                             track_info.track_id,
                             frame->data() + frame->prefixSize(),
                             frame->size() - frame->prefixSize(),
                             pts_out,
                             dts_out,
                             frame->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0);
145 146 147
        }
            break;
    }
xiongziliang committed
148 149
}

xiongziliang committed
150 151 152 153 154 155 156 157 158 159 160 161
static uint8_t getObject(CodecId codecId){
    switch (codecId){
        case CodecG711A : return MOV_OBJECT_G711a;
        case CodecG711U : return MOV_OBJECT_G711u;
        case CodecOpus : return MOV_OBJECT_OPUS;
        case CodecAAC : return MOV_OBJECT_AAC;
        case CodecH264 : return MOV_OBJECT_H264;
        case CodecH265 : return MOV_OBJECT_HEVC;
        default : return 0;
    }
}

162
void MP4MuxerInterface::stampSync(){
xiongziliang committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
    if(_codec_to_trackid.size() < 2){
        return;
    }

    Stamp *audio = nullptr, *video = nullptr;
    for(auto &pr : _codec_to_trackid){
        switch (getTrackType((CodecId) pr.first)){
            case TrackAudio : audio = &pr.second.stamp; break;
            case TrackVideo : video = &pr.second.stamp; break;
            default : break;
        }
    }

    if(audio && video){
        //音频时间戳同步于视频,因为音频时间戳被修改后不影响播放
        audio->syncTo(*video);
    }
}

182 183 184 185
void MP4MuxerInterface::addTrack(const Track::Ptr &track) {
    if (!_mov_writter) {
        _mov_writter = createWriter();
    }
xiongziliang committed
186 187 188 189 190 191 192 193 194 195 196
    auto mp4_object = getObject(track->getCodecId());
    if (!mp4_object) {
        WarnL << "MP4录制不支持该编码格式:" << track->getCodecName();
        return;
    }

    if (!track->ready()) {
        WarnL << "Track[" << track->getCodecName() << "]未就绪";
        return;
    }

xiongziliang committed
197
    switch (track->getCodecId()) {
198
        case CodecG711A:
xiongziliang committed
199 200 201
        case CodecG711U:
        case CodecOpus: {
            auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
202
            if (!audio_track) {
xiongziliang committed
203
                WarnL << "不是音频Track:" << track->getCodecName();
204 205
                return;
            }
xiongziliang committed
206

xiongziliang committed
207
            auto track_id = mp4_writer_add_audio(_mov_writter.get(),
xiongziliang committed
208
                                                 mp4_object,
209 210 211 212 213
                                                 audio_track->getAudioChannel(),
                                                 audio_track->getAudioSampleBit() * audio_track->getAudioChannel(),
                                                 audio_track->getAudioSampleRate(),
                                                 nullptr, 0);
            if (track_id < 0) {
xiongziliang committed
214
                WarnL << "添加Track[" << track->getCodecName() << "]失败:" << track_id;
215 216 217 218 219 220
                return;
            }
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
        }
            break;

xiongziliang committed
221
        case CodecAAC: {
222 223
            auto audio_track = dynamic_pointer_cast<AACTrack>(track);
            if (!audio_track) {
xiongziliang committed
224 225 226
                WarnL << "不是AAC Track";
                return;
            }
xiongziliang committed
227

xiongziliang committed
228
            auto track_id = mp4_writer_add_audio(_mov_writter.get(),
xiongziliang committed
229
                                                 mp4_object,
230 231 232
                                                 audio_track->getAudioChannel(),
                                                 audio_track->getAudioSampleBit() * audio_track->getAudioChannel(),
                                                 audio_track->getAudioSampleRate(),
233 234
                                                 audio_track->getAacCfg().data(),
                                                 audio_track->getAacCfg().size());
235 236 237 238
            if(track_id < 0){
                WarnL << "添加AAC Track失败:" << track_id;
                return;
            }
xiongziliang committed
239
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
xiongziliang committed
240 241 242 243 244 245 246 247 248
        }
            break;
        case CodecH264: {
            auto h264_track = dynamic_pointer_cast<H264Track>(track);
            if (!h264_track) {
                WarnL << "不是H264 Track";
                return;
            }

249
            struct mpeg4_avc_t avc = {0};
xiongziliang committed
250 251
            string sps_pps = string("\x00\x00\x00\x01", 4) + h264_track->getSps() +
                             string("\x00\x00\x00\x01", 4) + h264_track->getPps();
252
            h264_annexbtomp4(&avc, sps_pps.data(), (int)sps_pps.size(), NULL, 0, NULL, NULL);
xiongziliang committed
253 254 255 256 257 258 259 260

            uint8_t extra_data[1024];
            int extra_data_size = mpeg4_avc_decoder_configuration_record_save(&avc, extra_data, sizeof(extra_data));
            if (extra_data_size == -1) {
                WarnL << "生成H264 extra_data 失败";
                return;
            }

xiongziliang committed
261
            auto track_id = mp4_writer_add_video(_mov_writter.get(),
xiongziliang committed
262
                                                 mp4_object,
xiongziliang committed
263 264 265 266
                                                 h264_track->getVideoWidth(),
                                                 h264_track->getVideoHeight(),
                                                 extra_data,
                                                 extra_data_size);
267 268 269 270 271

            if(track_id < 0){
                WarnL << "添加H264 Track失败:" << track_id;
                return;
            }
xiongziliang committed
272
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
273
            _have_video = true;
xiongziliang committed
274 275 276 277 278 279 280
        }
            break;
        case CodecH265: {
            auto h265_track = dynamic_pointer_cast<H265Track>(track);
            if (!h265_track) {
                WarnL << "不是H265 Track";
                return;
281
            }
xiongziliang committed
282

283
            struct mpeg4_hevc_t hevc = {0};
xiongziliang committed
284 285 286
            string vps_sps_pps = string("\x00\x00\x00\x01", 4) + h265_track->getVps() +
                                 string("\x00\x00\x00\x01", 4) + h265_track->getSps() +
                                 string("\x00\x00\x00\x01", 4) + h265_track->getPps();
287
            h265_annexbtomp4(&hevc, vps_sps_pps.data(), (int)vps_sps_pps.size(), NULL, 0, NULL, NULL);
xiongziliang committed
288 289 290 291 292 293 294 295

            uint8_t extra_data[1024];
            int extra_data_size = mpeg4_hevc_decoder_configuration_record_save(&hevc, extra_data, sizeof(extra_data));
            if (extra_data_size == -1) {
                WarnL << "生成H265 extra_data 失败";
                return;
            }

xiongziliang committed
296
            auto track_id = mp4_writer_add_video(_mov_writter.get(),
xiongziliang committed
297
                                                 mp4_object,
xiongziliang committed
298 299 300 301
                                                 h265_track->getVideoWidth(),
                                                 h265_track->getVideoHeight(),
                                                 extra_data,
                                                 extra_data_size);
302 303 304 305
            if(track_id < 0){
                WarnL << "添加H265 Track失败:" << track_id;
                return;
            }
xiongziliang committed
306
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
307
            _have_video = true;
xiongziliang committed
308 309
        }
            break;
xiongziliang committed
310 311

        default: WarnL << "MP4录制不支持该编码格式:" << track->getCodecName(); break;
xiongziliang committed
312
    }
xiongziliang committed
313 314 315

    //尝试音视频同步
    stampSync();
xiongziliang committed
316 317
}

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
/////////////////////////////////////////// MP4MuxerMemory /////////////////////////////////////////////

MP4MuxerMemory::MP4MuxerMemory() {
    _memory_file = std::make_shared<MP4FileMemory>();
}

MP4FileIO::Writer MP4MuxerMemory::createWriter() {
    return _memory_file->createWriter(MOV_FLAG_SEGMENT, true);
}

const string &MP4MuxerMemory::getInitSegment(){
    if (_init_segment.empty()) {
        initSegment();
        saveSegment();
        _init_segment = _memory_file->getAndClearMemory();
    }
    return _init_segment;
}

void MP4MuxerMemory::resetTracks(){
    MP4MuxerInterface::resetTracks();
    _memory_file = std::make_shared<MP4FileMemory>();
    _init_segment.clear();
}

void MP4MuxerMemory::inputFrame(const Frame::Ptr &frame){
    if (_init_segment.empty()) {
        //尚未生成init segment
        return;
    }

    bool key_frame = frame->keyFrame();
    if (_ticker.elapsedTime() > 50 || key_frame) {
        //遇到关键帧或者超过50ms则切片
        _ticker.resetTime();
        //flush切片
        saveSegment();
        //输出切片数据
        onSegmentData(_memory_file->getAndClearMemory(), frame->dts(), _key_frame);
        _key_frame = false;
    }

    if (key_frame) {
        _key_frame = true;
    }
    MP4MuxerInterface::inputFrame(frame);
}


xiongziliang committed
367
}//namespace mediakit
xiongziliang committed
368
#endif//#ifdef ENABLE_MP4