Commit d971eccf by xiongziliang

新增支持HTTP-fMP4 WebSocket-fMP4直播

parent 4ce1a25f
......@@ -13,7 +13,7 @@
## 项目特点
- 基于C++11开发,避免使用裸指针,代码稳定可靠,性能优越。
- 支持多种协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV/GB28181/HTTP-TS/Websocket-TS/MP4),支持协议互转。
- 支持多种协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV/GB28181/HTTP-TS/Websocket-TS/HTTP-fMP4/Websocket-fMP4/MP4),支持协议互转。
- 使用多路复用/多线程/异步网络IO模式开发,并发性能优越,支持海量客户端连接。
- 代码经过长期大量的稳定性、性能测试,已经在线上商用验证已久。
- 支持linux、macos、ios、android、windows全平台。
......@@ -63,6 +63,10 @@
- TS
- 支持http[s]-ts直播
- 支持ws[s]-ts直播
- fMP4
- 支持http[s]-fmp4直播
- 支持ws[s]-fmp4直播
- HTTP[S]与WebSocket
- 服务器支持`目录索引生成`,`文件下载`,`表单提交请求`
......
......@@ -11,7 +11,7 @@
## Why ZLMediaKit?
- Developed based on C++ 11, the code is stable and reliable, avoiding the use of raw pointers, cross-platform porting is simple and convenient, and the code is clear and concise.
- Support rich streaming media protocols(`RTSP/RTMP/HLS/HTTP-FLV/WebSocket-flv/HTTP-TS/WebSocket-TS`),and support Inter-protocol conversion.
- Support rich streaming media protocols(`RTSP/RTMP/HLS/HTTP-FLV/WebSocket-flv/HTTP-TS/WebSocket-TS/HTTP-fMP4/Websocket-fMP4/MP4`),and support Inter-protocol conversion.
- Multiplexing asynchronous network IO based on epoll and multi thread,extreme performance.
- Well performance and stable test,can be used commercially.
- Support linux, macos, ios, android, Windows Platforms.
......@@ -44,6 +44,9 @@
- TS
- Support HTTP-TS/WebSocket-TS sever.
- fMP4
- Support HTTP-fMP4/WebSocket-fMP4 sever.
- HTTP[S]
- HTTP server,suppor directory meun、RESTful http api.
......
......@@ -34,6 +34,7 @@ MultiMuxerPrivate::MultiMuxerPrivate(const string &vhost, const string &app, con
}
_ts = std::make_shared<TSMediaSourceMuxer>(vhost, app, stream);
_fmp4 = std::make_shared<FMP4MediaSourceMuxer>(vhost, app, stream);
}
void MultiMuxerPrivate::resetTracks() {
......@@ -46,6 +47,9 @@ void MultiMuxerPrivate::resetTracks() {
if (_ts) {
_ts->resetTracks();
}
if (_fmp4) {
_fmp4->resetTracks();
}
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
auto hls = _hls;
......@@ -70,6 +74,9 @@ void MultiMuxerPrivate::setMediaListener(const std::weak_ptr<MediaSourceEvent> &
if (_ts) {
_ts->setListener(listener);
}
if (_fmp4) {
_fmp4->setListener(listener);
}
auto hls = _hls;
if (hls) {
hls->setListener(listener);
......@@ -81,7 +88,8 @@ int MultiMuxerPrivate::totalReaderCount() const {
return (_rtsp ? _rtsp->readerCount() : 0) +
(_rtmp ? _rtmp->readerCount() : 0) +
(_ts ? _ts->readerCount() : 0) +
(hls ? hls->readerCount() : 0) ;
(_fmp4 ? _fmp4->readerCount() : 0) +
(hls ? hls->readerCount() : 0);
}
static std::shared_ptr<MediaSinkInterface> makeRecorder(const vector<Track::Ptr> &tracks, Recorder::type type, const string &custom_path, MediaSource &sender){
......@@ -159,6 +167,9 @@ void MultiMuxerPrivate::onTrackReady(const Track::Ptr &track) {
if (_ts) {
_ts->addTrack(track);
}
if (_fmp4) {
_fmp4->addTrack(track);
}
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
auto hls = _hls;
......@@ -176,6 +187,7 @@ bool MultiMuxerPrivate::isEnabled(){
return (_rtmp ? _rtmp->isEnabled() : false) ||
(_rtsp ? _rtsp->isEnabled() : false) ||
(_ts ? _ts->isEnabled() : false) ||
(_fmp4 ? _fmp4->isEnabled() : false) ||
(hls ? hls->isEnabled() : false) || _mp4;
}
......@@ -189,6 +201,9 @@ void MultiMuxerPrivate::onTrackFrame(const Frame::Ptr &frame) {
if (_ts) {
_ts->inputFrame(frame);
}
if (_fmp4) {
_fmp4->inputFrame(frame);
}
//拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题
//此处使用智能指针拷贝来确保线程安全,比互斥锁性能更优
......@@ -239,6 +254,9 @@ void MultiMuxerPrivate::onAllTrackReady() {
if (_rtsp) {
_rtsp->onAllTrackReady();
}
if (_fmp4) {
_fmp4->onAllTrackReady();
}
if (_track_listener) {
_track_listener->onAllTrackReady();
}
......
......@@ -19,6 +19,7 @@
#include "Rtsp/RtspMediaSourceMuxer.h"
#include "Rtmp/RtmpMediaSourceMuxer.h"
#include "TS/TSMediaSourceMuxer.h"
#include "FMP4/FMP4MediaSourceMuxer.h"
namespace mediakit{
......@@ -58,6 +59,7 @@ private:
HlsRecorder::Ptr _hls;
MediaSinkInterface::Ptr _mp4;
TSMediaSourceMuxer::Ptr _ts;
FMP4MediaSourceMuxer::Ptr _fmp4;
std::weak_ptr<MediaSourceEvent> _listener;
};
......
......@@ -48,6 +48,7 @@ bool loadIniConfig(const char *ini_path = nullptr);
#define RTMP_SCHEMA "rtmp"
#define HLS_SCHEMA "hls"
#define TS_SCHEMA "ts"
#define FMP4_SCHEMA "fmp4"
#define DEFAULT_VHOST "__defaultVhost__"
////////////广播名称///////////
......
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* 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.
*/
#ifndef ZLMEDIAKIT_FMP4MEDIASOURCE_H
#define ZLMEDIAKIT_FMP4MEDIASOURCE_H
#include "Common/MediaSource.h"
using namespace toolkit;
#define FMP4_GOP_SIZE 512
namespace mediakit {
//FMP4直播数据包
class FMP4Packet : public BufferString{
public:
using Ptr = std::shared_ptr<FMP4Packet>;
template<typename ...ARGS>
FMP4Packet(ARGS && ...args) : BufferString(std::forward<ARGS>(args)...) {};
~FMP4Packet() override = default;
public:
uint32_t time_stamp = 0;
};
//FMP4直播合并写策略类
class FMP4FlushPolicy : public FlushPolicy{
public:
FMP4FlushPolicy() = default;
~FMP4FlushPolicy() = default;
uint32_t getStamp(const FMP4Packet::Ptr &packet) {
return packet->time_stamp;
}
};
//FMP4直播源
class FMP4MediaSource : public MediaSource, public RingDelegate<FMP4Packet::Ptr>, public PacketCache<FMP4Packet, FMP4FlushPolicy>{
public:
using Ptr = std::shared_ptr<FMP4MediaSource>;
using RingDataType = std::shared_ptr<List<FMP4Packet::Ptr> >;
using RingType = RingBuffer<RingDataType>;
FMP4MediaSource(const string &vhost,
const string &app,
const string &stream_id,
int ring_size = FMP4_GOP_SIZE) : MediaSource(FMP4_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {}
~FMP4MediaSource() override = default;
/**
* 获取媒体源的环形缓冲
*/
const RingType::Ptr &getRing() const {
return _ring;
}
/**
* 获取fmp4 init segment
*/
const string &getInitSegment() const{
return _init_segment;
}
/**
* 设置fmp4 init segment
* @param str init segment
*/
void setInitSegment(string str) {
_init_segment = std::move(str);
if (_ring) {
regist();
}
}
/**
* 获取播放器个数
*/
int readerCount() override {
return _ring ? _ring->readerCount() : 0;
}
/**
* 输入FMP4包
* @param packet FMP4包
* @param key 是否为关键帧第一个包
*/
void onWrite(const FMP4Packet::Ptr &packet, bool key) override {
if (!_ring) {
createRing();
}
if (key) {
_have_video = true;
}
PacketCache<FMP4Packet, FMP4FlushPolicy>::inputPacket(true, packet, key);
}
/**
* 情况GOP缓存
*/
void clearCache() override {
PacketCache<FMP4Packet, FMP4FlushPolicy>::clearCache();
_ring->clearCache();
}
private:
void createRing(){
weak_ptr<FMP4MediaSource> weak_self = dynamic_pointer_cast<FMP4MediaSource>(shared_from_this());
_ring = std::make_shared<RingType>(_ring_size, [weak_self](int size) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->onReaderChanged(size);
});
onReaderChanged(0);
if (!_init_segment.empty()) {
regist();
}
}
/**
* 合并写回调
* @param packet_list 合并写缓存列队
* @param key_pos 是否包含关键帧
*/
void onFlush(std::shared_ptr<List<FMP4Packet::Ptr> > &packet_list, bool key_pos) override {
//如果不存在视频,那么就没有存在GOP缓存的意义,所以确保一直清空GOP缓存
_ring->write(packet_list, _have_video ? key_pos : true);
}
private:
bool _have_video = false;
int _ring_size;
string _init_segment;
RingType::Ptr _ring;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_FMP4MEDIASOURCE_H
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* 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.
*/
#ifndef ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
#define ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
#include "FMP4MediaSource.h"
#include "Record/MP4Muxer.h"
namespace mediakit {
class FMP4MediaSourceMuxer : public MP4MuxerMemory, public MediaSourceEventInterceptor,
public std::enable_shared_from_this<FMP4MediaSourceMuxer> {
public:
using Ptr = std::shared_ptr<FMP4MediaSourceMuxer>;
FMP4MediaSourceMuxer(const string &vhost,
const string &app,
const string &stream_id) {
_media_src = std::make_shared<FMP4MediaSource>(vhost, app, stream_id);
}
~FMP4MediaSourceMuxer() override = default;
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
_listener = listener;
_media_src->setListener(shared_from_this());
}
int readerCount() const{
return _media_src->readerCount();
}
void onReaderChanged(MediaSource &sender, int size) override {
_enabled = size;
if (!size) {
_clear_cache = true;
}
MediaSourceEventInterceptor::onReaderChanged(sender, size);
}
void inputFrame(const Frame::Ptr &frame) override {
if (_clear_cache) {
_clear_cache = false;
_media_src->clearCache();
}
if (_enabled) {
MP4MuxerMemory::inputFrame(frame);
}
}
bool isEnabled() {
//缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存
return _clear_cache ? true : _enabled;
}
void onAllTrackReady() {
_media_src->setInitSegment(getInitSegment());
}
protected:
void onSegmentData(const string &string, uint32_t stamp, bool key_frame) override {
if (string.empty()) {
return;
}
FMP4Packet::Ptr packet = std::make_shared<FMP4Packet>(std::move(string));
packet->time_stamp = stamp;
_media_src->onWrite(packet, key_frame);
}
private:
bool _enabled = true;
bool _clear_cache = false;
FMP4MediaSource::Ptr _media_src;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
......@@ -93,7 +93,7 @@ void HttpSession::onError(const SockException& err) {
if(_is_live_stream){
uint64_t duration = _ticker.createdTime()/1000;
//flv/ts播放器
WarnP(this) << "FLV/TS播放器("
WarnP(this) << "FLV/TS/FMP4播放器("
<< _mediaInfo._vhost << "/"
<< _mediaInfo._app << "/"
<< _mediaInfo._streamid
......@@ -156,6 +156,12 @@ bool HttpSession::checkWebSocket(){
return true;
}
//判断是否为websocket-fmp4
if (checkLiveStreamFMP4(res_cb)) {
//这里是websocket-fmp4直播请求
return true;
}
//这是普通的websocket连接
if (!onWebSocketConnect(_parser)) {
sendResponse("501 Not Implemented", true, nullptr, headerOut);
......@@ -234,15 +240,55 @@ bool HttpSession::checkLiveStream(const string &schema, const string &url_suffi
return true;
}
//http-fmp4 链接格式:http://vhost-url:port/app/streamid.live.mp4?key1=value1&key2=value2
bool HttpSession::checkLiveStreamFMP4(const function<void()> &cb){
return checkLiveStream(FMP4_SCHEMA, ".live.mp4", [this, cb](const MediaSource::Ptr &src) {
auto fmp4_src = dynamic_pointer_cast<FMP4MediaSource>(src);
assert(fmp4_src);
if (!cb) {
//找到源,发送http头,负载后续发送
sendResponse("200 OK", false, HttpFileManager::getContentType(".mp4").data(), KeyValue(), nullptr, true);
} else {
//自定义发送http头
cb();
}
//直播牺牲延时提升发送性能
setSocketFlags();
onWrite(std::make_shared<BufferString>(fmp4_src->getInitSegment()), true);
weak_ptr<HttpSession> weak_self = dynamic_pointer_cast<HttpSession>(shared_from_this());
_fmp4_reader = fmp4_src->getRing()->attach(getPoller());
_fmp4_reader->setDetachCB([weak_self]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
//本对象已经销毁
return;
}
strong_self->shutdown(SockException(Err_shutdown, "fmp4 ring buffer detached"));
});
_fmp4_reader->setReadCB([weak_self](const FMP4MediaSource::RingDataType &fmp4_list) {
auto strong_self = weak_self.lock();
if (!strong_self) {
//本对象已经销毁
return;
}
int i = 0;
int size = fmp4_list->size();
fmp4_list->for_each([&](const FMP4Packet::Ptr &ts) {
strong_self->onWrite(ts, ++i == size);
});
});
});
}
//http-ts 链接格式:http://vhost-url:port/app/streamid.live.ts?key1=value1&key2=value2
//如果url(除去?以及后面的参数)后缀是.ts,那么表明该url是一个http-ts直播。
bool HttpSession::checkLiveStreamTS(const function<void()> &cb){
return checkLiveStream(TS_SCHEMA, ".live.ts", [this, cb](const MediaSource::Ptr &src) {
auto ts_src = dynamic_pointer_cast<TSMediaSource>(src);
assert(ts_src);
if (!cb) {
//找到源,发送http头,负载后续发送
sendResponse("200 OK", false, "video/mp2t", KeyValue(), nullptr, true);
sendResponse("200 OK", false, HttpFileManager::getContentType(".ts").data(), KeyValue(), nullptr, true);
} else {
//自定义发送http头
cb();
......@@ -276,14 +322,13 @@ bool HttpSession::checkLiveStreamTS(const function<void()> &cb){
}
//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2
//如果url(除去?以及后面的参数)后缀是.flv,那么表明该url是一个http-flv直播。
bool HttpSession::checkLiveStreamFlv(const function<void()> &cb){
return checkLiveStream(RTMP_SCHEMA, ".flv", [this, cb](const MediaSource::Ptr &src) {
auto rtmp_src = dynamic_pointer_cast<RtmpMediaSource>(src);
assert(rtmp_src);
if (!cb) {
//找到源,发送http头,负载后续发送
sendResponse("200 OK", false, "video/x-flv", KeyValue(), nullptr, true);
sendResponse("200 OK", false, HttpFileManager::getContentType(".flv").data(), KeyValue(), nullptr, true);
} else {
//自定义发送http头
cb();
......@@ -325,6 +370,11 @@ void HttpSession::Handle_Req_GET_l(int64_t &content_len, bool sendBody) {
return;
}
if (checkLiveStreamFMP4()) {
//拦截http-fmp4播放器
return;
}
bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
HttpFileManager::onAccessPath(*this, _parser, [weakSelf, bClose](const string &status_code, const string &content_type,
......
......@@ -20,6 +20,7 @@
#include "HttpCookieManager.h"
#include "HttpFileManager.h"
#include "TS/TSMediaSource.h"
#include "FMP4/FMP4MediaSource.h"
using namespace std;
using namespace toolkit;
......@@ -109,6 +110,7 @@ private:
bool checkLiveStreamFlv(const function<void()> &cb = nullptr);
bool checkLiveStreamTS(const function<void()> &cb = nullptr);
bool checkLiveStreamFMP4(const function<void()> &fmp4_list = nullptr);
bool checkWebSocket();
bool emitHttpEvent(bool doInvoke);
......@@ -131,6 +133,7 @@ private:
Ticker _ticker;
MediaInfo _mediaInfo;
TSMediaSource::RingType::RingReader::Ptr _ts_reader;
FMP4MediaSource::RingType::RingReader::Ptr _fmp4_reader;
//处理content数据的callback
function<bool (const char *data,uint64_t len) > _contentCallBack;
};
......
......@@ -216,5 +216,51 @@ uint64_t MP4FileDisk::onTell() {
return ftell64(_file.get());
}
/////////////////////////////////////////////////////MP4FileMemory/////////////////////////////////////////////////////////
string MP4FileMemory::getAndClearMemory(){
string ret;
ret.swap(_memory);
_offset = 0;
return ret;
}
uint64_t MP4FileMemory::fileSize() const{
return _memory.size();
}
uint64_t MP4FileMemory::onTell(){
return _offset;
}
int MP4FileMemory::onSeek(uint64_t offset){
if (offset > _memory.size()) {
return -1;
}
_offset = offset;
return 0;
}
int MP4FileMemory::onRead(void *data, uint64_t bytes){
if (_offset >= _memory.size()) {
//EOF
return -1;
}
bytes = MIN(bytes, _memory.size() - _offset);
memcpy(data, _memory.data(), bytes);
_offset += bytes;
return 0;
}
int MP4FileMemory::onWrite(const void *data, uint64_t bytes){
if (_offset + bytes > _memory.size()) {
//需要扩容
_memory.resize(_offset + bytes);
}
memcpy((uint8_t *) _memory.data() + _offset, data, bytes);
_offset += bytes;
return 0;
}
}//namespace mediakit
#endif //NABLE_MP4RECORD
......@@ -117,6 +117,33 @@ private:
std::shared_ptr<FILE> _file;
};
class MP4FileMemory : public MP4FileIO{
public:
using Ptr = std::shared_ptr<MP4FileMemory>;
MP4FileMemory() = default;
~MP4FileMemory() override = default;
/**
* 获取文件大小
*/
uint64_t fileSize() const;
/**
* 获取并清空文件缓存
*/
string getAndClearMemory();
protected:
uint64_t onTell() override;
int onSeek(uint64_t offset) override;
int onRead(void *data, uint64_t bytes) override;
int onWrite(const void *data, uint64_t bytes) override;
private:
uint64_t _offset = 0;
string _memory;
};
}//namespace mediakit
#endif //NABLE_MP4RECORD
#endif //ZLMEDIAKIT_MP4_H
......@@ -21,26 +21,50 @@ MP4Muxer::~MP4Muxer() {
}
void MP4Muxer::openMP4(const string &file){
_file_name = file;
closeMP4();
openFile(_file_name.data(), "wb+");
_file_name = file;
_mp4_file = std::make_shared<MP4FileDisk>();
_mp4_file->openFile(_file_name.data(), "wb+");
}
MP4FileIO::Writer MP4Muxer::createWriter(){
GET_CONFIG(bool, mp4FastStart, Record::kFastStart);
_mov_writter = createWriter(mp4FastStart ? MOV_FLAG_FASTSTART : 0, false);
return _mp4_file->createWriter(mp4FastStart ? MOV_FLAG_FASTSTART : 0, false);
}
void MP4Muxer::closeMP4(){
_mov_writter = nullptr;
closeFile();
MP4MuxerInterface::resetTracks();
_mp4_file = nullptr;
}
void MP4Muxer::resetTracks() {
_codec_to_trackid.clear();
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() {
_started = false;
_have_video = false;
openMP4(_file_name);
_mov_writter = nullptr;
_frameCached.clear();
_codec_to_trackid.clear();
}
void MP4Muxer::inputFrame(const Frame::Ptr &frame) {
void MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) {
auto it = _codec_to_trackid.find(frame->getCodecId());
if(it == _codec_to_trackid.end()){
//该Track不存在或初始化失败
......@@ -134,7 +158,7 @@ static uint8_t getObject(CodecId codecId){
}
}
void MP4Muxer::stampSync(){
void MP4MuxerInterface::stampSync(){
if(_codec_to_trackid.size() < 2){
return;
}
......@@ -154,7 +178,10 @@ void MP4Muxer::stampSync(){
}
}
void MP4Muxer::addTrack(const Track::Ptr &track) {
void MP4MuxerInterface::addTrack(const Track::Ptr &track) {
if (!_mov_writter) {
_mov_writter = createWriter();
}
auto mp4_object = getObject(track->getCodecId());
if (!mp4_object) {
WarnL << "MP4录制不支持该编码格式:" << track->getCodecName();
......@@ -287,5 +314,54 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
stampSync();
}
/////////////////////////////////////////// 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);
}
}//namespace mediakit
#endif//#ifdef ENABLE_MP4
......@@ -23,17 +23,16 @@
namespace mediakit{
class MP4Muxer : public MediaSinkInterface, public MP4FileDisk{
class MP4MuxerInterface : public MediaSinkInterface {
public:
typedef std::shared_ptr<MP4Muxer> Ptr;
MP4Muxer();
~MP4Muxer() override;
MP4MuxerInterface() = default;
~MP4MuxerInterface() override = default;
/**
* 添加已经ready状态的track
*/
void addTrack(const Track::Ptr & track) override;
void addTrack(const Track::Ptr &track) override;
/**
* 输入帧
*/
......@@ -42,35 +41,112 @@ public:
/**
* 重置所有track
*/
void resetTracks() override ;
void resetTracks() override;
/**
* 打开mp4
* @param file 文件完整路径
* 是否包含视频
*/
void openMP4(const string &file);
bool haveVideo() const;
/**
* 手动关闭文件(对象析构时会自动关闭)
* 保存fmp4分片
*/
void closeMP4();
void saveSegment();
/**
* 创建新切片
*/
void initSegment();
protected:
virtual MP4FileIO::Writer createWriter() = 0;
private:
void stampSync();
private:
bool _started = false;
bool _have_video = false;
MP4FileIO::Writer _mov_writter;
struct track_info {
int track_id = -1;
Stamp stamp;
};
unordered_map<int, track_info> _codec_to_trackid;
List<Frame::Ptr> _frameCached;
bool _started = false;
bool _have_video = false;
unordered_map<int, track_info> _codec_to_trackid;
};
class MP4Muxer : public MP4MuxerInterface{
public:
typedef std::shared_ptr<MP4Muxer> Ptr;
MP4Muxer();
~MP4Muxer() override;
/**
* 重置所有track
*/
void resetTracks() override;
/**
* 打开mp4
* @param file 文件完整路径
*/
void openMP4(const string &file);
/**
* 手动关闭文件(对象析构时会自动关闭)
*/
void closeMP4();
protected:
MP4FileIO::Writer createWriter() override;
private:
string _file_name;
Writer _mov_writter;
MP4FileDisk::Ptr _mp4_file;
};
class MP4MuxerMemory : public MP4MuxerInterface{
public:
MP4MuxerMemory();
~MP4MuxerMemory() override = default;
/**
* 重置所有track
*/
void resetTracks() override;
/**
* 输入帧
*/
void inputFrame(const Frame::Ptr &frame) override;
/**
* 获取fmp4 init segment
*/
const string &getInitSegment();
protected:
/**
* 输出fmp4切片回调函数
* @param string 切片内容
* @param stamp 切片末尾时间戳
* @param key_frame 是否有关键帧
*/
virtual void onSegmentData(const string &string, uint32_t stamp, bool key_frame) = 0;
protected:
MP4FileIO::Writer createWriter() override;
private:
bool _key_frame = false;
Ticker _ticker;
string _init_segment;
MP4FileMemory::Ptr _memory_file;
};
}//namespace mediakit
#endif//#ifdef ENABLE_MP4
#endif //ZLMEDIAKIT_MP4MUXER_H
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论