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

#include "FlvMuxer.h"
28 29
#include "Util/File.h"
#include "Rtmp/utils.h"
xiongziliang committed
30 31 32

#define FILE_BUF_SIZE (64 * 1024)

xiongziliang committed
33
namespace mediakit {
xiongziliang committed
34 35 36 37 38 39 40


FlvMuxer::FlvMuxer() {
}
FlvMuxer::~FlvMuxer() {
}

41
void FlvMuxer::start(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &media) {
xiongziliang committed
42 43 44
    if(!media){
        throw std::runtime_error("RtmpMediaSource 无效");
    }
xiongziliang committed
45 46
    if(!poller->isCurrentThread()){
        weak_ptr<FlvMuxer> weakSelf = getSharedPtr();
47 48
        //延时两秒启动录制,目的是为了等待config帧收集完毕
        poller->doDelayTask(2000,[weakSelf,poller,media](){
xiongziliang committed
49 50 51 52
            auto strongSelf = weakSelf.lock();
            if(strongSelf){
                strongSelf->start(poller,media);
            }
53
            return 0;
xiongziliang committed
54 55 56
        });
        return;
    }
xiongziliang committed
57 58 59 60

    onWriteFlvHeader(media);

    std::weak_ptr<FlvMuxer> weakSelf = getSharedPtr();
61
    _ring_reader = media->getRing()->attach(poller);
xiongziliang committed
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
    _ring_reader->setDetachCB([weakSelf](){
        auto strongSelf = weakSelf.lock();
        if(!strongSelf){
            return;
        }
        strongSelf->onDetach();
    });
    _ring_reader->setReadCB([weakSelf](const RtmpPacket::Ptr &pkt){
        auto strongSelf = weakSelf.lock();
        if(!strongSelf){
            return;
        }
        strongSelf->onWriteRtmp(pkt);
    });
}

void FlvMuxer::onWriteFlvHeader(const RtmpMediaSource::Ptr &mediaSrc) {
    //发送flv文件头
    char flv_file_header[] = "FLV\x1\x5\x0\x0\x0\x9"; // have audio and have video
    bool is_have_audio = false,is_have_video = false;

    mediaSrc->getConfigFrame([&](const RtmpPacket::Ptr &pkt){
        if(pkt->typeId == MSG_VIDEO){
            is_have_video = true;
        }
        if(pkt->typeId == MSG_AUDIO){
            is_have_audio = true;
        }
    });

    if (is_have_audio && is_have_video) {
        flv_file_header[4] = 0x05;
    } else if (is_have_audio && !is_have_video) {
        flv_file_header[4] = 0x04;
    } else if (!is_have_audio && is_have_video) {
        flv_file_header[4] = 0x01;
    } else {
        flv_file_header[4] = 0x00;
    }

    //flv header
103 104 105 106 107 108
    onWrite(std::make_shared<BufferRaw>(flv_file_header, sizeof(flv_file_header) - 1));

    auto size = htonl(0);
    //PreviousTagSize0 Always 0
    onWrite(std::make_shared<BufferRaw>((char *)&size,4));

109 110 111 112 113 114 115 116 117

    auto &metadata = mediaSrc->getMetaData();
    if(metadata){
        //在有metadata的情况下才发送metadata
        //其实metadata没什么用,有些推流器不产生metadata
        AMFEncoder invoke;
        invoke << "onMetaData" << metadata;
        onWriteFlvTag(MSG_DATA, std::make_shared<BufferString>(invoke.data()), 0);
    }
118

xiongziliang committed
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
    //config frame
    mediaSrc->getConfigFrame([&](const RtmpPacket::Ptr &pkt){
        onWriteRtmp(pkt);
    });
}



#if defined(_WIN32)
#pragma pack(push, 1)
#endif // defined(_WIN32)

class RtmpTagHeader {
public:
    uint8_t type = 0;
    uint8_t data_size[3] = {0};
    uint8_t timestamp[3] = {0};
    uint8_t timestamp_ex = 0;
    uint8_t streamid[3] = {0}; /* Always 0. */
}PACKED;

#if defined(_WIN32)
#pragma pack(pop)
#endif // defined(_WIN32)

void FlvMuxer::onWriteFlvTag(const RtmpPacket::Ptr &pkt, uint32_t ui32TimeStamp) {
145
    onWriteFlvTag(pkt->typeId,pkt,ui32TimeStamp);
xiongziliang committed
146 147
}

148
void FlvMuxer::onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp) {
xiongziliang committed
149 150
    RtmpTagHeader header;
    header.type = ui8Type;
151
    set_be24(header.data_size, buffer->size());
xiongziliang committed
152 153
    header.timestamp_ex = (uint8_t) ((ui32TimeStamp >> 24) & 0xff);
    set_be24(header.timestamp,ui32TimeStamp & 0xFFFFFF);
154 155 156 157 158 159 160
    //tag header
    onWrite(std::make_shared<BufferRaw>((char *)&header, sizeof(header)));
    //tag data
    onWrite(buffer);
    auto size = htonl((buffer->size() + sizeof(header)));
    //PreviousTagSize
    onWrite(std::make_shared<BufferRaw>((char *)&size,4));
xiongziliang committed
161 162 163
}

void FlvMuxer::onWriteRtmp(const RtmpPacket::Ptr &pkt) {
164 165 166
    int64_t dts_out;
    _stamp[pkt->typeId % 2].revise(pkt->timeStamp, 0, dts_out, dts_out);
    onWriteFlvTag(pkt, dts_out);
xiongziliang committed
167 168 169 170 171 172 173 174 175 176
}

void FlvMuxer::stop() {
    if(_ring_reader){
        _ring_reader.reset();
        onDetach();
    }
}

///////////////////////////////////////////////////////FlvRecorder/////////////////////////////////////////////////////
177 178
void FlvRecorder::startRecord(const EventPoller::Ptr &poller,const string &vhost, const string &app, const string &stream,const string &file_path) {
    startRecord(poller,dynamic_pointer_cast<RtmpMediaSource>(MediaSource::find(RTMP_SCHEMA,vhost,app,stream,false)),file_path);
xiongziliang committed
179 180
}

181
void FlvRecorder::startRecord(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &media, const string &file_path) {
xiongziliang committed
182
    stop();
183
    lock_guard<recursive_mutex> lck(_file_mtx);
xiongziliang committed
184 185 186 187 188 189 190
    //开辟文件写缓存
    std::shared_ptr<char> fileBuf(new char[FILE_BUF_SIZE],[](char *ptr){
        if(ptr){
            delete [] ptr;
        }
    });
    //新建文件
191
    _file.reset(File::createfile_file(file_path.data(),"wb"),[fileBuf](FILE *fp){
xiongziliang committed
192 193 194 195 196 197 198 199 200 201 202
        if(fp){
            fflush(fp);
            fclose(fp);
        }
    });
    if (!_file){
        throw std::runtime_error( StrPrinter << "打开文件失败:" << file_path);
    }

    //设置文件写缓存
    setvbuf( _file.get(), fileBuf.get(),_IOFBF, FILE_BUF_SIZE);
203
    start(poller,media);
xiongziliang committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
}

void FlvRecorder::onWrite(const Buffer::Ptr &data) {
    lock_guard<recursive_mutex> lck(_file_mtx);
    if(_file){
        fwrite(data->data(),data->size(),1,_file.get());
    }
}

void FlvRecorder::onDetach() {
    lock_guard<recursive_mutex> lck(_file_mtx);
    _file.reset();
}

std::shared_ptr<FlvMuxer> FlvRecorder::getSharedPtr() {
    return  shared_from_this();
}

222 223 224 225 226 227
FlvRecorder::FlvRecorder() {
}

FlvRecorder::~FlvRecorder() {
}

xiongziliang committed
228

xiongziliang committed
229
}//namespace mediakit