TsMuxer.cpp 5.68 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
3 4 5
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
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.
9 10 11
 */

#include "TsMuxer.h"
12
#if defined(ENABLE_HLS)
13
#include "mpeg-ts-proto.h"
14
#include "mpeg-ts.h"
15
#include "Extension/H264.h"
16 17 18 19 20 21 22 23 24 25 26

namespace mediakit {

TsMuxer::TsMuxer() {
    init();
}

TsMuxer::~TsMuxer() {
    uninit();
}

xiongziliang committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
void TsMuxer::stampSync(){
    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);
    }
}

47
void TsMuxer::addTrack(const Track::Ptr &track) {
48
    switch (track->getCodecId()) {
49
        case CodecH264: {
50
            _have_video = true;
xiongziliang committed
51
            _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H264, nullptr, 0);
52
            break;
xiongziliang committed
53 54
        }

55
        case CodecH265: {
56
            _have_video = true;
xiongziliang committed
57
            _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H265, nullptr, 0);
58
            break;
xiongziliang committed
59 60
        }

61
        case CodecAAC: {
xiongziliang committed
62
            _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_AAC, nullptr, 0);
xiongziliang committed
63
            break;
64
        }
xiongziliang committed
65 66 67

        case CodecG711A: {
            _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_AUDIO_G711A, nullptr, 0);
68
            break;
xiongziliang committed
69
        }
70 71

        case CodecG711U: {
xiongziliang committed
72
            _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_AUDIO_G711U, nullptr, 0);
73
            break;
xiongziliang committed
74
        }
75

xiongziliang committed
76
        default: WarnL << "mpeg-ts 不支持该编码格式,已忽略:" << track->getCodecName(); break;
77
    }
xiongziliang committed
78 79 80

    //尝试音视频同步
    stampSync();
81 82 83
}

void TsMuxer::inputFrame(const Frame::Ptr &frame) {
84 85
    auto it = _codec_to_trackid.find(frame->getCodecId());
    if(it == _codec_to_trackid.end()){
86 87
        return;
    }
88 89 90
    //mp4文件时间戳需要从0开始
    auto &track_info = it->second;
    int64_t dts_out, pts_out;
91
    _is_idr_fast_packet = !_have_video;
92 93
    switch (frame->getCodecId()){
        case CodecH264: {
94 95 96 97 98 99
            int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
            if(type == H264Frame::NAL_SEI){
                break;
            }
        }
        case CodecH265: {
100 101 102 103 104 105 106 107 108 109 110 111 112
            //这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理,
            if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) {
                Frame::Ptr back = _frameCached.back();
                Buffer::Ptr merged_frame = back;
                if(_frameCached.size() != 1){
                    string merged;
                    _frameCached.for_each([&](const Frame::Ptr &frame){
                        if(frame->prefixSize()){
                            merged.append(frame->data(),frame->size());
                        } else{
                            merged.append("\x00\x00\x00\x01",4);
                            merged.append(frame->data(),frame->size());
                        }
113 114 115
                        if(frame->keyFrame()){
                            _is_idr_fast_packet = true;
                        }
116 117
                    });
                    merged_frame = std::make_shared<BufferString>(std::move(merged));
118
                }
119 120 121 122
                track_info.stamp.revise(back->dts(),back->pts(),dts_out,pts_out);
                _timestamp = dts_out;
                mpeg_ts_write(_context, track_info.track_id, back->keyFrame() ? 0x0001 : 0, pts_out * 90LL, dts_out * 90LL, merged_frame->data(),  merged_frame->size());
                _frameCached.clear();
123
            }
124
            _frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
125 126 127
        }
            break;
        default: {
128 129 130
            track_info.stamp.revise(frame->dts(),frame->pts(),dts_out,pts_out);
            _timestamp = dts_out;
            mpeg_ts_write(_context, track_info.track_id, frame->keyFrame() ? 0x0001 : 0, pts_out * 90LL, dts_out * 90LL, frame->data(), frame->size());
131 132 133 134 135 136
        }
            break;
    }
}

void TsMuxer::resetTracks() {
137
    _have_video = false;
138 139
    //通知片段中断
    onTs(nullptr, 0, 0, 0);
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
    uninit();
    init();
}

void TsMuxer::init() {
    static mpeg_ts_func_t s_func= {
            [](void* param, size_t bytes){
                TsMuxer *muxer = (TsMuxer *)param;
                assert(sizeof(TsMuxer::_tsbuf) >= bytes);
                return (void *)muxer->_tsbuf;
            },
            [](void* param, void* packet){
                //do nothing
            },
            [](void* param, const void* packet, size_t bytes){
                TsMuxer *muxer = (TsMuxer *)param;
156 157
                muxer->onTs(packet, bytes,muxer->_timestamp,muxer->_is_idr_fast_packet);
                muxer->_is_idr_fast_packet = false;
158 159 160 161 162 163 164 165 166 167 168 169
            }
    };
    if(_context == nullptr){
        _context = mpeg_ts_create(&s_func,this);
    }
}

void TsMuxer::uninit() {
    if(_context){
        mpeg_ts_destroy(_context);
        _context = nullptr;
    }
170
    _codec_to_trackid.clear();
171 172
}

173 174 175
}//namespace mediakit

#endif// defined(ENABLE_HLS)