MP4Muxer.cpp 11.6 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
    _mov_writter = nullptr;
xia-chu committed
63
    _frame_merger.clear();
64
    _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;
            }
        }
xia-chu committed
95

96 97
        case CodecH265: {
            //这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理,
xia-chu committed
98 99 100 101 102 103 104 105 106 107
            _frame_merger.inputFrame(frame, [&](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer, bool have_idr) {
                track_info.stamp.revise(dts, pts, dts_out, pts_out);
                mp4_writer_write(_mov_writter.get(),
                                 track_info.track_id,
                                 buffer->data(),
                                 buffer->size(),
                                 pts_out,
                                 dts_out,
                                 have_idr ? MOV_AV_FLAG_KEYFREAME : 0);
            });
108
            break;
xia-chu committed
109 110
        }

111 112
        default: {
            track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out);
xiongziliang committed
113 114 115 116 117 118 119
            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);
120
            break;
xia-chu committed
121 122
        }

123
    }
xiongziliang committed
124 125
}

xiongziliang committed
126 127 128 129 130 131 132 133 134 135 136 137
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;
    }
}

138
void MP4MuxerInterface::stampSync(){
xiongziliang committed
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
    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);
    }
}

158 159 160 161
void MP4MuxerInterface::addTrack(const Track::Ptr &track) {
    if (!_mov_writter) {
        _mov_writter = createWriter();
    }
xiongziliang committed
162 163 164 165 166 167 168 169 170 171 172
    auto mp4_object = getObject(track->getCodecId());
    if (!mp4_object) {
        WarnL << "MP4录制不支持该编码格式:" << track->getCodecName();
        return;
    }

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

xiongziliang committed
173
    switch (track->getCodecId()) {
174
        case CodecG711A:
xiongziliang committed
175 176 177
        case CodecG711U:
        case CodecOpus: {
            auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
178
            if (!audio_track) {
xiongziliang committed
179
                WarnL << "不是音频Track:" << track->getCodecName();
180 181
                return;
            }
xiongziliang committed
182

xiongziliang committed
183
            auto track_id = mp4_writer_add_audio(_mov_writter.get(),
xiongziliang committed
184
                                                 mp4_object,
185 186 187 188 189
                                                 audio_track->getAudioChannel(),
                                                 audio_track->getAudioSampleBit() * audio_track->getAudioChannel(),
                                                 audio_track->getAudioSampleRate(),
                                                 nullptr, 0);
            if (track_id < 0) {
xiongziliang committed
190
                WarnL << "添加Track[" << track->getCodecName() << "]失败:" << track_id;
191 192 193 194 195 196
                return;
            }
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
        }
            break;

xiongziliang committed
197
        case CodecAAC: {
198 199
            auto audio_track = dynamic_pointer_cast<AACTrack>(track);
            if (!audio_track) {
xiongziliang committed
200 201 202
                WarnL << "不是AAC Track";
                return;
            }
xiongziliang committed
203

xiongziliang committed
204
            auto track_id = mp4_writer_add_audio(_mov_writter.get(),
xiongziliang committed
205
                                                 mp4_object,
206 207 208
                                                 audio_track->getAudioChannel(),
                                                 audio_track->getAudioSampleBit() * audio_track->getAudioChannel(),
                                                 audio_track->getAudioSampleRate(),
209 210
                                                 audio_track->getAacCfg().data(),
                                                 audio_track->getAacCfg().size());
211 212 213 214
            if(track_id < 0){
                WarnL << "添加AAC Track失败:" << track_id;
                return;
            }
xiongziliang committed
215
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
xiongziliang committed
216 217 218 219 220 221 222 223 224
        }
            break;
        case CodecH264: {
            auto h264_track = dynamic_pointer_cast<H264Track>(track);
            if (!h264_track) {
                WarnL << "不是H264 Track";
                return;
            }

225
            struct mpeg4_avc_t avc = {0};
xiongziliang committed
226 227
            string sps_pps = string("\x00\x00\x00\x01", 4) + h264_track->getSps() +
                             string("\x00\x00\x00\x01", 4) + h264_track->getPps();
228
            h264_annexbtomp4(&avc, sps_pps.data(), (int)sps_pps.size(), NULL, 0, NULL, NULL);
xiongziliang committed
229 230 231 232 233 234 235 236

            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
237
            auto track_id = mp4_writer_add_video(_mov_writter.get(),
xiongziliang committed
238
                                                 mp4_object,
xiongziliang committed
239 240 241 242
                                                 h264_track->getVideoWidth(),
                                                 h264_track->getVideoHeight(),
                                                 extra_data,
                                                 extra_data_size);
243 244 245 246 247

            if(track_id < 0){
                WarnL << "添加H264 Track失败:" << track_id;
                return;
            }
xiongziliang committed
248
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
249
            _have_video = true;
xiongziliang committed
250 251 252 253 254 255 256
        }
            break;
        case CodecH265: {
            auto h265_track = dynamic_pointer_cast<H265Track>(track);
            if (!h265_track) {
                WarnL << "不是H265 Track";
                return;
257
            }
xiongziliang committed
258

259
            struct mpeg4_hevc_t hevc = {0};
xiongziliang committed
260 261 262
            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();
263
            h265_annexbtomp4(&hevc, vps_sps_pps.data(), (int)vps_sps_pps.size(), NULL, 0, NULL, NULL);
xiongziliang committed
264 265 266 267 268 269 270 271

            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
272
            auto track_id = mp4_writer_add_video(_mov_writter.get(),
xiongziliang committed
273
                                                 mp4_object,
xiongziliang committed
274 275 276 277
                                                 h265_track->getVideoWidth(),
                                                 h265_track->getVideoHeight(),
                                                 extra_data,
                                                 extra_data_size);
278 279 280 281
            if(track_id < 0){
                WarnL << "添加H265 Track失败:" << track_id;
                return;
            }
xiongziliang committed
282
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
283
            _have_video = true;
xiongziliang committed
284 285
        }
            break;
xiongziliang committed
286 287

        default: WarnL << "MP4录制不支持该编码格式:" << track->getCodecName(); break;
xiongziliang committed
288
    }
xiongziliang committed
289 290 291

    //尝试音视频同步
    stampSync();
xiongziliang committed
292 293
}

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 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
/////////////////////////////////////////// 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
343
}//namespace mediakit
xiongziliang committed
344
#endif//#ifdef ENABLE_MP4