MP4Muxer.cpp 9.97 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 * MIT License
 *
 * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
 *
 * 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.
 */

xiongziliang committed
27
#ifdef ENABLE_MP4
xiongziliang committed
28 29 30 31
#include "MP4Muxer.h"
#include "Util/File.h"
namespace mediakit{

xiongziliang committed
32 33 34 35
MP4Muxer::MP4Muxer(const char *file) {
    _file_name = file;
    openMP4();
}
xiongziliang committed
36

xiongziliang committed
37 38 39 40 41 42 43 44 45 46 47 48
MP4Muxer::~MP4Muxer() {
    closeMP4();
}

void MP4Muxer::openMP4(){
    closeMP4();
    openFile(_file_name.data(), "wb+");
    _mov_writter = createWriter();
}
void MP4Muxer::closeMP4(){
    _mov_writter = nullptr;
    closeFile();
xiongziliang committed
49 50
}

xiongziliang committed
51 52 53
void MP4Muxer::resetTracks() {
    _codec_to_trackid.clear();
    _started = false;
54
    _have_video = false;
xiongziliang committed
55
    openMP4();
xiongziliang committed
56
}
xiongziliang committed
57

xiongziliang committed
58
void MP4Muxer::inputFrame(const Frame::Ptr &frame) {
xiongziliang committed
59 60
    auto it = _codec_to_trackid.find(frame->getCodecId());
    if(it == _codec_to_trackid.end()){
61
        //该Track不存在或初始化失败
xiongziliang committed
62 63 64
        return;
    }

65
    if (!_started) {
xiongziliang committed
66
        //还没开始
67 68 69 70 71 72 73 74 75
        if (!_have_video) {
            _started = true;
        } else {
            if (frame->getTrackType() != TrackVideo || !frame->keyFrame()) {
                //如果首帧是音频或者是视频但是不是i帧,那么不能开始写文件
                return;
            }
            //开始写文件
            _started = true;
76 77 78
        }
    }

79 80
    //mp4文件时间戳需要从0开始
    auto &track_info = it->second;
81
    int64_t dts_out, pts_out;
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 107 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
    switch (frame->getCodecId()) {
        case CodecH264:
        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格式合并一起
                    string merged;
                    _frameCached.for_each([&](const Frame::Ptr &frame) {
                        uint32_t nalu_size = frame->size() - frame->prefixSize();
                        nalu_size = htonl(nalu_size);
                        merged.append((char *) &nalu_size, 4);
                        merged.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
                    });
                    mov_writer_write_l(_mov_writter.get(),
                                       track_info.track_id,
                                       merged.data(),
                                       merged.size(),
                                       pts_out,
                                       dts_out,
                                       back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0,
                                       1/*我们合并时已经生成了4个字节的MP4格式start code*/);
                } else {
                    //缓存中只有一帧视频
                    mov_writer_write_l(_mov_writter.get(),
                                       track_info.track_id,
                                       back->data() + back->prefixSize(),
                                       back->size() - back->prefixSize(),
                                       pts_out,
                                       dts_out,
                                       back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0,
                                       0/*需要生成头4个字节的MP4格式start code*/);
                }
                _frameCached.clear();
            }
            //缓存帧,时间戳相同的帧合并一起写入mp4
            _frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
        }
            break;
        default: {
            track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out);
            mov_writer_write_l(_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,
                               1/*aac或其他类型frame不用添加4个nalu_size的字节*/);
        }
            break;
    }
xiongziliang committed
139 140
}

xiongziliang committed
141
void MP4Muxer::addTrack(const Track::Ptr &track) {
xiongziliang committed
142 143 144 145 146 147 148
    switch (track->getCodecId()) {
        case CodecAAC: {
            auto aac_track = dynamic_pointer_cast<AACTrack>(track);
            if (!aac_track) {
                WarnL << "不是AAC Track";
                return;
            }
149 150 151 152
            if(!aac_track->ready()){
                WarnL << "AAC Track未就绪";
                return;
            }
xiongziliang committed
153 154 155
            auto track_id = mov_writer_add_audio(_mov_writter.get(),
                                                 MOV_OBJECT_AAC,
                                                 aac_track->getAudioChannel(),
156
                                                 aac_track->getAudioSampleBit() * aac_track->getAudioChannel(),
xiongziliang committed
157 158
                                                 aac_track->getAudioSampleRate(),
                                                 aac_track->getAacCfg().data(), 2);
159 160 161 162
            if(track_id < 0){
                WarnL << "添加AAC Track失败:" << track_id;
                return;
            }
xiongziliang committed
163
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
xiongziliang committed
164 165 166 167 168 169 170 171
        }
            break;
        case CodecH264: {
            auto h264_track = dynamic_pointer_cast<H264Track>(track);
            if (!h264_track) {
                WarnL << "不是H264 Track";
                return;
            }
172 173 174 175
            if(!h264_track->ready()){
                WarnL << "H264 Track未就绪";
                return;
            }
xiongziliang committed
176

177
            struct mpeg4_avc_t avc = {0};
xiongziliang committed
178 179
            string sps_pps = string("\x00\x00\x00\x01", 4) + h264_track->getSps() +
                             string("\x00\x00\x00\x01", 4) + h264_track->getPps();
xiongziliang committed
180
            h264_annexbtomp4(&avc, sps_pps.data(), sps_pps.size(), NULL, 0, NULL, NULL);
xiongziliang committed
181 182 183 184 185 186 187 188 189 190 191 192 193 194

            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;
            }

            auto track_id = mov_writer_add_video(_mov_writter.get(),
                                                 MOV_OBJECT_H264,
                                                 h264_track->getVideoWidth(),
                                                 h264_track->getVideoHeight(),
                                                 extra_data,
                                                 extra_data_size);
195 196 197 198 199

            if(track_id < 0){
                WarnL << "添加H264 Track失败:" << track_id;
                return;
            }
xiongziliang committed
200
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
201
            _have_video = true;
xiongziliang committed
202 203 204 205 206 207 208
        }
            break;
        case CodecH265: {
            auto h265_track = dynamic_pointer_cast<H265Track>(track);
            if (!h265_track) {
                WarnL << "不是H265 Track";
                return;
209 210 211 212
            }
            if(!h265_track->ready()){
                WarnL << "H265 Track未就绪";
                return;
xiongziliang committed
213 214
            }

215
            struct mpeg4_hevc_t hevc = {0};
xiongziliang committed
216 217 218
            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();
xiongziliang committed
219
            h265_annexbtomp4(&hevc, vps_sps_pps.data(), vps_sps_pps.size(), NULL, 0, NULL, NULL);
xiongziliang committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233

            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;
            }

            auto track_id = mov_writer_add_video(_mov_writter.get(),
                                                 MOV_OBJECT_HEVC,
                                                 h265_track->getVideoWidth(),
                                                 h265_track->getVideoHeight(),
                                                 extra_data,
                                                 extra_data_size);
234 235 236 237
            if(track_id < 0){
                WarnL << "添加H265 Track失败:" << track_id;
                return;
            }
xiongziliang committed
238
            _codec_to_trackid[track->getCodecId()].track_id = track_id;
239
            _have_video = true;
xiongziliang committed
240 241 242
        }
            break;
        default:
243
            WarnL << "MP4录制不支持该编码格式:" << track->getCodecName();
xiongziliang committed
244 245 246 247 248
            break;
    }
}

}//namespace mediakit
xiongziliang committed
249
#endif//#ifdef ENABLE_MP4