合并主分支

This commit is contained in:
monktan
2020-09-21 11:01:02 +08:00
48 changed files with 1679 additions and 463 deletions

View File

@@ -135,9 +135,10 @@ void HlsMaker::flushLastSegment(bool eof){
}
_seg_dur_list.push_back(std::make_tuple(seg_dur, std::move(_last_file_name)));
_last_file_name.clear();
delOldSegment();
makeIndexFile(eof);
_last_file_name.clear();
onFlushLastSegment(seg_dur);
}
bool HlsMaker::isLive() {

View File

@@ -79,14 +79,20 @@ protected:
virtual void onWriteHls(const char *data, int len) = 0;
/**
* 关闭上个ts切片并且写入m3u8索引
* @param timestamp 毫秒时间戳
* @param eof HLS直播是否已结束
* 上一个 ts 切片写入完成, 可在这里进行通知处理
* @param duration_ms 上一个 ts 切片的时长, 单位为毫秒
*/
void flushLastSegment(bool eof = false);
virtual void onFlushLastSegment(uint32_t duration_ms) {};
virtual void onWriteRecordM3u8(const char *header, int hlen,const char *body,int blen) = 0;
/**
* 关闭上个ts切片并且写入m3u8索引
* @param eof HLS直播是否已结束
*/
void flushLastSegment(bool eof);
private:
/**
* 生成m3u8文件

View File

@@ -8,6 +8,8 @@
* may be found in the AUTHORS file in the root of the source tree.
*/
#include <ctime>
#include <sys/stat.h>
#include "HlsMakerImp.h"
#include "Util/util.h"
#include "Util/uv_errno.h"
@@ -27,7 +29,9 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file,
_file_buf.reset(new char[bufSize],[](char *ptr){
delete[] ptr;
});
_ui64StartedTime = ::time(nullptr);
_info.folder = _path_prefix;
}
HlsMakerImp::~HlsMakerImp() {
@@ -70,14 +74,22 @@ string HlsMakerImp::onOpenSegment(int index) {
_segment_file_paths.emplace(index,segment_path);
}
}
_file = makeFile(segment_path, true);
if(!_file){
//保存本切片的元数据
_info.start_time = ::time(NULL);
_info.file_name = segment_name;
_info.file_path = segment_path;
_info.url = _info.app + "/" + _info.stream + "/" + segment_name;
if (!_file) {
WarnL << "create file failed," << segment_path << " " << get_uv_errmsg();
}
if(_params.empty()){
return std::move(segment_name);
if (_params.empty()) {
return segment_name;
}
return std::move(segment_name + "?" + _params);
return segment_name + "?" + _params;
}
void HlsMakerImp::onDelSegment(int index) {
@@ -122,7 +134,6 @@ std::shared_ptr<FILE> HlsMakerImp::makeFile(const string &file,bool setbuf) {
return ret;
}
void HlsMakerImp::onWriteRecordM3u8(const char *header, int hlen,const char *body,int blen){
bool exist = true;
string mode = "r+";
@@ -154,6 +165,18 @@ void HlsMakerImp::onWriteRecordM3u8(const char *header, int hlen,const char *bod
DebugL << "_path_hls " << _path_hls;
}
void HlsMakerImp::onFlushLastSegment(uint32_t duration_ms) {
GET_CONFIG(bool, broadcastRecordTs, Hls::kBroadcastRecordTs);
if (broadcastRecordTs) {
//关闭ts文件以便获取正确的文件大小
_file = nullptr;
_info.time_len = duration_ms / 1000.0;
struct stat fileData;
stat(_info.file_path.data(), &fileData);
_info.file_size = fileData.st_size;
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordTs, _info);
}
}
std::shared_ptr<FILE> HlsMakerImp::makeRecordM3u8(const string &file,const string &mode,bool setbuf) {
auto file_buf = _file_buf;
@@ -169,10 +192,11 @@ std::shared_ptr<FILE> HlsMakerImp::makeRecordM3u8(const string &file,const strin
return ret;
}
void HlsMakerImp::setMediaSource(const string &vhost, const string &app, const string &stream_id) {
_media_src = std::make_shared<HlsMediaSource>(vhost, app, stream_id);
_info.app = app;
_info.stream = stream_id;
_info.vhost = vhost;
}
HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const {

View File

@@ -16,6 +16,7 @@
#include <stdlib.h>
#include "HlsMaker.h"
#include "HlsMediaSource.h"
using namespace std;
namespace mediakit {
@@ -64,6 +65,7 @@ protected:
void onWriteSegment(const char *data, int len) override;
void onWriteHls(const char *data, int len) override;
void onWriteRecordM3u8(const char *header, int hlen, const char *body, int blen) override;
void onFlushLastSegment(uint32_t duration_ms) override;
private:
std::shared_ptr<FILE> makeFile(const string &file,bool setbuf = false);
@@ -73,6 +75,7 @@ private:
string _params;
string _path_hls;
string _path_prefix;
RecordInfo _info;
std::shared_ptr<FILE> _file;
std::shared_ptr<char> _file_buf;
HlsMediaSource::Ptr _media_src;

View File

@@ -13,34 +13,125 @@
#include "Util/File.h"
#include "Util/logger.h"
#include "Common/config.h"
#include "fmp4-writer.h"
using namespace toolkit;
namespace mediakit {
/////////////////////////////////////////////////mp4_writer_t/////////////////////////////////////////////////
struct mp4_writer_t {
int is_fmp4;
union {
fmp4_writer_t *fmp4;
mov_writer_t *mov;
} u;
};
mp4_writer_t* mp4_writer_create(int is_fmp4, const struct mov_buffer_t *buffer, void* param, int flags){
mp4_writer_t *mp4 = (mp4_writer_t *) malloc(sizeof(mp4_writer_t));
mp4->is_fmp4 = is_fmp4;
if (is_fmp4) {
mp4->u.fmp4 = fmp4_writer_create(buffer, param, flags);
} else {
mp4->u.mov = mov_writer_create(buffer, param, flags);
}
return mp4;
}
void mp4_writer_destroy(mp4_writer_t* mp4){
if (mp4->is_fmp4) {
fmp4_writer_destroy(mp4->u.fmp4);
} else {
mov_writer_destroy(mp4->u.mov);
}
free(mp4);
}
int mp4_writer_add_audio(mp4_writer_t* mp4, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size){
if (mp4->is_fmp4) {
return fmp4_writer_add_audio(mp4->u.fmp4, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size);
} else {
return mov_writer_add_audio(mp4->u.mov, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size);
}
}
int mp4_writer_add_video(mp4_writer_t* mp4, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size){
if (mp4->is_fmp4) {
return fmp4_writer_add_video(mp4->u.fmp4, object, width, height, extra_data, extra_data_size);
} else {
return mov_writer_add_video(mp4->u.mov, object, width, height, extra_data, extra_data_size);
}
}
int mp4_writer_add_subtitle(mp4_writer_t* mp4, uint8_t object, const void* extra_data, size_t extra_data_size){
if (mp4->is_fmp4) {
return fmp4_writer_add_subtitle(mp4->u.fmp4, object, extra_data, extra_data_size);
} else {
return mov_writer_add_subtitle(mp4->u.mov, object, extra_data, extra_data_size);
}
}
int mp4_writer_write(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags){
if (mp4->is_fmp4) {
return fmp4_writer_write(mp4->u.fmp4, track, data, bytes, pts, dts, flags);
} else {
return mov_writer_write(mp4->u.mov, track, data, bytes, pts, dts, flags);
}
}
int mp4_writer_write_l(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags, int add_nalu_size){
if (mp4->is_fmp4) {
return fmp4_writer_write_l(mp4->u.fmp4, track, data, bytes, pts, dts, flags, add_nalu_size);
} else {
return mov_writer_write_l(mp4->u.mov, track, data, bytes, pts, dts, flags, add_nalu_size);
}
}
int mp4_writer_save_segment(mp4_writer_t* mp4){
if (mp4->is_fmp4) {
return fmp4_writer_save_segment(mp4->u.fmp4);
} else {
return -1;
}
}
int mp4_writer_init_segment(mp4_writer_t* mp4){
if (mp4->is_fmp4) {
return fmp4_writer_init_segment(mp4->u.fmp4);
} else {
return -1;
}
}
/////////////////////////////////////////////////MP4FileIO/////////////////////////////////////////////////
static struct mov_buffer_t s_io = {
[](void* ctx, void* data, uint64_t bytes) {
MP4File *thiz = (MP4File *)ctx;
return thiz->onRead(data,bytes);
[](void *ctx, void *data, uint64_t bytes) {
MP4FileIO *thiz = (MP4FileIO *) ctx;
return thiz->onRead(data, bytes);
},
[](void* ctx, const void* data, uint64_t bytes){
MP4File *thiz = (MP4File *)ctx;
return thiz->onWrite(data,bytes);
[](void *ctx, const void *data, uint64_t bytes) {
MP4FileIO *thiz = (MP4FileIO *) ctx;
return thiz->onWrite(data, bytes);
},
[](void* ctx, uint64_t offset) {
MP4File *thiz = (MP4File *)ctx;
[](void *ctx, uint64_t offset) {
MP4FileIO *thiz = (MP4FileIO *) ctx;
return thiz->onSeek(offset);
},
[](void* ctx){
MP4File *thiz = (MP4File *)ctx;
[](void *ctx) {
MP4FileIO *thiz = (MP4FileIO *) ctx;
return thiz->onTell();
}
};
MP4File::Writer MP4File::createWriter(){
GET_CONFIG(bool, mp4FastStart, Record::kFastStart);
MP4FileIO::Writer MP4FileIO::createWriter(int flags, bool is_fmp4){
Writer writer;
writer.reset(mov_writer_create(&s_io,this,mp4FastStart ? MOV_FLAG_FASTSTART : 0),[](mov_writer_t *ptr){
Ptr self = shared_from_this();
//保存自己的强引用,防止提前释放
writer.reset(mp4_writer_create(is_fmp4, &s_io,this, flags),[self](mp4_writer_t *ptr){
if(ptr){
mov_writer_destroy(ptr);
mp4_writer_destroy(ptr);
}
});
if(!writer){
@@ -49,9 +140,11 @@ MP4File::Writer MP4File::createWriter(){
return writer;
}
MP4File::Reader MP4File::createReader(){
MP4FileIO::Reader MP4FileIO::createReader(){
Reader reader;
reader.reset(mov_reader_create(&s_io,this),[](mov_reader_t *ptr){
Ptr self = shared_from_this();
//保存自己的强引用,防止提前释放
reader.reset(mov_reader_create(&s_io,this),[self](mov_reader_t *ptr){
if(ptr){
mov_reader_destroy(ptr);
}
@@ -62,15 +155,17 @@ MP4File::Reader MP4File::createReader(){
return reader;
}
/////////////////////////////////////////////////////MP4FileDisk/////////////////////////////////////////////////////////
#if defined(_WIN32) || defined(_WIN64)
#define fseek64 _fseeki64
#define ftell64 _ftelli64
#define ftell64 _ftelli64
#else
#define fseek64 fseek
#define ftell64 ftell
#define fseek64 fseek
#define ftell64 ftell
#endif
void MP4File::openFile(const char *file,const char *mode) {
void MP4FileDisk::openFile(const char *file, const char *mode) {
//创建文件
auto fp = File::create_file(file, mode);
if(!fp){
@@ -98,28 +193,74 @@ void MP4File::openFile(const char *file,const char *mode) {
});
}
void MP4File::closeFile() {
void MP4FileDisk::closeFile() {
_file = nullptr;
}
int MP4File::onRead(void *data, uint64_t bytes) {
int MP4FileDisk::onRead(void *data, uint64_t bytes) {
if (bytes == fread(data, 1, bytes, _file.get())){
return 0;
}
return 0 != ferror(_file.get()) ? ferror(_file.get()) : -1 /*EOF*/;
}
int MP4File::onWrite(const void *data, uint64_t bytes) {
int MP4FileDisk::onWrite(const void *data, uint64_t bytes) {
return bytes == fwrite(data, 1, bytes, _file.get()) ? 0 : ferror(_file.get());
}
int MP4File::onSeek(uint64_t offset) {
int MP4FileDisk::onSeek(uint64_t offset) {
return fseek64(_file.get(), offset, SEEK_SET);
}
uint64_t MP4File::onTell() {
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

View File

@@ -23,27 +23,127 @@
using namespace std;
namespace mediakit {
class MP4File {
public:
friend struct mov_buffer_t;
typedef std::shared_ptr<mov_writer_t> Writer;
typedef std::shared_ptr<mov_reader_t> Reader;
MP4File() = default;
virtual ~MP4File() = default;
//以下是fmp4/mov的通用接口简单包装了ireader/media-server的接口
typedef struct mp4_writer_t mp4_writer_t;
mp4_writer_t* mp4_writer_create(int is_fmp4, const struct mov_buffer_t *buffer, void* param, int flags);
void mp4_writer_destroy(mp4_writer_t* mp4);
int mp4_writer_add_audio(mp4_writer_t* mp4, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size);
int mp4_writer_add_video(mp4_writer_t* mp4, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size);
int mp4_writer_add_subtitle(mp4_writer_t* mp4, uint8_t object, const void* extra_data, size_t extra_data_size);
int mp4_writer_write(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags);
int mp4_writer_write_l(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags, int add_nalu_size);
int mp4_writer_save_segment(mp4_writer_t* mp4);
int mp4_writer_init_segment(mp4_writer_t* mp4);
Writer createWriter();
Reader createReader();
void openFile(const char *file,const char *mode);
//mp4文件IO的抽象接口类
class MP4FileIO : public std::enable_shared_from_this<MP4FileIO> {
public:
using Ptr = std::shared_ptr<MP4FileIO>;
using Writer = std::shared_ptr<mp4_writer_t>;
using Reader = std::shared_ptr<mov_reader_t>;
MP4FileIO() = default;
virtual ~MP4FileIO() = default;
/**
* 创建mp4复用器
* @param flags 支持0、MOV_FLAG_FASTSTART、MOV_FLAG_SEGMENT
* @param is_fmp4 是否为fmp4还是普通mp4
* @return mp4复用器
*/
virtual Writer createWriter(int flags, bool is_fmp4 = false);
/**
* 创建mp4解复用器
* @return mp4解复用器
*/
virtual Reader createReader();
/**
* 获取文件读写位置
*/
virtual uint64_t onTell() = 0;
/**
* seek至文件某处
* @param offset 文件偏移量
* @return 是否成功(0成功)
*/
virtual int onSeek(uint64_t offset) = 0;
/**
* 从文件读取一定数据
* @param data 数据存放指针
* @param bytes 指针长度
* @return 是否成功(0成功)
*/
virtual int onRead(void *data, uint64_t bytes) = 0;
/**
* 写入文件一定数据
* @param data 数据指针
* @param bytes 数据长度
* @return 是否成功(0成功)
*/
virtual int onWrite(const void *data, uint64_t bytes) = 0;
};
//磁盘MP4文件类
class MP4FileDisk : public MP4FileIO {
public:
using Ptr = std::shared_ptr<MP4FileDisk>;
MP4FileDisk() = default;
~MP4FileDisk() override = default;
/**
* 打开磁盘文件
* @param file 文件路径
* @param mode fopen的方式
*/
void openFile(const char *file, const char *mode);
/**
* 关闭磁盘文件
*/
void closeFile();
int onRead(void* data, uint64_t bytes);
int onWrite(const void* data, uint64_t bytes);
int onSeek( uint64_t offset);
uint64_t onTell();
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:
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

View File

@@ -19,18 +19,20 @@
using namespace toolkit;
namespace mediakit {
MP4Demuxer::MP4Demuxer(const char *file) {
openFile(file,"rb+");
_mov_reader = createReader();
getAllTracks();
_duration_ms = mov_reader_getduration(_mov_reader.get());
}
MP4Demuxer::MP4Demuxer() {}
MP4Demuxer::~MP4Demuxer() {
_mov_reader = nullptr;
closeFile();
}
void MP4Demuxer::openMP4(const string &file){
openFile(file.data(),"rb+");
_mov_reader = createReader();
getAllTracks();
_duration_ms = mov_reader_getduration(_mov_reader.get());
}
int MP4Demuxer::getAllTracks() {
static mov_reader_trackinfo_t s_on_track = {
[](void *param, uint32_t track, uint8_t object, int width, int height, const void *extra, size_t bytes) {
@@ -158,6 +160,8 @@ struct Context{
BufferRaw::Ptr buffer;
};
#define DATA_OFFSET ADTS_HEADER_LEN
Frame::Ptr MP4Demuxer::readFrame(bool &keyFrame, bool &eof) {
keyFrame = false;
eof = false;
@@ -172,9 +176,9 @@ Frame::Ptr MP4Demuxer::readFrame(bool &keyFrame, bool &eof) {
static mov_onalloc mov_onalloc = [](void *param, int bytes) -> void * {
Context *ctx = (Context *) param;
ctx->buffer = ctx->thiz->_buffer_pool.obtain();
ctx->buffer->setCapacity(bytes + 1);
ctx->buffer->setSize(bytes);
return ctx->buffer->data();
ctx->buffer->setCapacity(bytes + DATA_OFFSET + 1);
ctx->buffer->setSize(bytes + DATA_OFFSET);
return ctx->buffer->data() + DATA_OFFSET;
};
Context ctx = {this, 0};
@@ -202,11 +206,11 @@ template <typename Parent>
class FrameWrapper : public Parent{
public:
~FrameWrapper() = default;
FrameWrapper(const Buffer::Ptr &buf, int64_t pts, int64_t dts, int prefix) : Parent(buf->data(), buf->size(), dts, pts, prefix){
FrameWrapper(const Buffer::Ptr &buf, int64_t pts, int64_t dts, int prefix, int offset) : Parent(buf->data() + offset, buf->size() - offset, dts, pts, prefix){
_buf = buf;
}
FrameWrapper(CodecId codec,const Buffer::Ptr &buf, int64_t pts, int64_t dts, int prefix) : Parent(codec, buf->data(), buf->size(), dts, pts, prefix){
FrameWrapper(const Buffer::Ptr &buf, int64_t pts, int64_t dts, int prefix, int offset, CodecId codec) : Parent(codec, buf->data() + offset, buf->size() - offset, dts, pts, prefix){
_buf = buf;
}
@@ -222,37 +226,44 @@ Frame::Ptr MP4Demuxer::makeFrame(uint32_t track_id, const Buffer::Ptr &buf, int6
if (it == _track_to_codec.end()) {
return nullptr;
}
auto numBytes = buf->size();
auto pBytes = buf->data();
auto bytes = buf->size() - DATA_OFFSET;
auto data = buf->data() + DATA_OFFSET;
auto codec = it->second->getCodecId();
switch (codec) {
case CodecH264 :
case CodecH265 : {
uint32_t iOffset = 0;
while (iOffset < numBytes) {
uint32_t iFrameLen;
memcpy(&iFrameLen, pBytes + iOffset, 4);
iFrameLen = ntohl(iFrameLen);
if (iFrameLen + iOffset + 4 > numBytes) {
uint32_t offset = 0;
while (offset < bytes) {
uint32_t frame_len;
memcpy(&frame_len, data + offset, 4);
frame_len = ntohl(frame_len);
if (frame_len + offset + 4 > bytes) {
return nullptr;
}
memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4);
iOffset += (iFrameLen + 4);
memcpy(data + offset, "\x0\x0\x0\x1", 4);
offset += (frame_len + 4);
}
if (codec == CodecH264) {
return std::make_shared<FrameWrapper<H264FrameNoCacheAble> >(buf, pts, dts, 4);
return std::make_shared<FrameWrapper<H264FrameNoCacheAble> >(buf, pts, dts, 4, DATA_OFFSET);
}
return std::make_shared<FrameWrapper<H265FrameNoCacheAble> >(buf, pts, dts, 4);
return std::make_shared<FrameWrapper<H265FrameNoCacheAble> >(buf, pts, dts, 4, DATA_OFFSET);
}
case CodecAAC: {
AACTrack::Ptr track = dynamic_pointer_cast<AACTrack>(it->second);
assert(track);
//加上adts头
dumpAacConfig(track->getAacCfg(), buf->size() - DATA_OFFSET, (uint8_t *) buf->data() + (DATA_OFFSET - ADTS_HEADER_LEN), ADTS_HEADER_LEN);
return std::make_shared<FrameWrapper<FrameFromPtr> >(buf, pts, dts, ADTS_HEADER_LEN, DATA_OFFSET - ADTS_HEADER_LEN, codec);
}
case CodecOpus:
case CodecAAC:
case CodecG711A:
case CodecG711U: {
return std::make_shared<FrameWrapper<FrameFromPtr> >(codec, buf, pts, dts, 0);
return std::make_shared<FrameWrapper<FrameFromPtr> >(buf, pts, dts, 0, DATA_OFFSET, codec);
}
default:
return nullptr;
default: return nullptr;
}
}

View File

@@ -16,24 +16,60 @@
#include "Util/ResourcePool.h"
namespace mediakit {
class MP4Demuxer : public MP4File, public TrackSource{
class MP4Demuxer : public MP4FileDisk, public TrackSource{
public:
typedef std::shared_ptr<MP4Demuxer> Ptr;
MP4Demuxer(const char *file);
/**
* 创建mp4解复用器
*/
MP4Demuxer();
~MP4Demuxer() override;
/**
* 打开文件
* @param file mp4文件路径
*/
void openMP4(const string &file);
/**
* 移动时间轴至某处
* @param stamp_ms 预期的时间轴位置,单位毫秒
* @return 时间轴位置
*/
int64_t seekTo(int64_t stamp_ms);
/**
* 读取一帧数据
* @param keyFrame 是否为关键帧
* @param eof 是否文件读取完毕
* @return 帧数据,可能为空
*/
Frame::Ptr readFrame(bool &keyFrame, bool &eof);
vector<Track::Ptr> getTracks(bool trackReady) const override ;
/**
* 获取所有Track信息
* @param trackReady 是否要求track为就绪状态
* @return 所有Track
*/
vector<Track::Ptr> getTracks(bool trackReady) const override;
/**
* 获取文件长度
* @return 文件长度,单位毫秒
*/
uint64_t getDurationMS() const;
private:
int getAllTracks();
void onVideoTrack(uint32_t track_id, uint8_t object, int width, int height, const void* extra, size_t bytes);
void onAudioTrack(uint32_t track_id, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void* extra, size_t bytes);
Frame::Ptr makeFrame(uint32_t track_id, const Buffer::Ptr &buf,int64_t pts, int64_t dts);
void onVideoTrack(uint32_t track_id, uint8_t object, int width, int height, const void *extra, size_t bytes);
void onAudioTrack(uint32_t track_id, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void *extra, size_t bytes);
Frame::Ptr makeFrame(uint32_t track_id, const Buffer::Ptr &buf, int64_t pts, int64_t dts);
private:
MP4File::Reader _mov_reader;
Reader _mov_reader;
uint64_t _duration_ms = 0;
map<int, Track::Ptr > _track_to_codec;
map<int, Track::Ptr> _track_to_codec;
ResourcePool<BufferRaw> _buffer_pool;
};

View File

@@ -14,33 +14,57 @@
#include "Extension/H264.h"
namespace mediakit{
MP4Muxer::MP4Muxer(const char *file) {
_file_name = file;
openMP4();
}
MP4Muxer::MP4Muxer() {}
MP4Muxer::~MP4Muxer() {
closeMP4();
}
void MP4Muxer::openMP4(){
void MP4Muxer::openMP4(const string &file){
closeMP4();
openFile(_file_name.data(), "wb+");
_mov_writter = createWriter();
_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);
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();
_started = false;
_have_video = false;
openMP4();
MP4MuxerInterface::resetTracks();
openMP4(_file_name);
}
void MP4Muxer::inputFrame(const Frame::Ptr &frame) {
/////////////////////////////////////////// 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;
_mov_writter = nullptr;
_frameCached.clear();
_codec_to_trackid.clear();
}
void MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) {
auto it = _codec_to_trackid.find(frame->getCodecId());
if(it == _codec_to_trackid.end()){
//该Track不存在或初始化失败
@@ -84,24 +108,23 @@ void MP4Muxer::inputFrame(const Frame::Ptr &frame) {
merged.append((char *) &nalu_size, 4);
merged.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
});
mov_writer_write_l(_mov_writter.get(),
mp4_writer_write(_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*/);
back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0);
} else {
//缓存中只有一帧视频
mov_writer_write_l(_mov_writter.get(),
mp4_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*/);
1/*需要生成头4个字节的MP4格式start code*/);
}
_frameCached.clear();
}
@@ -111,14 +134,13 @@ void MP4Muxer::inputFrame(const Frame::Ptr &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的字节*/);
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);
}
break;
}
@@ -136,7 +158,7 @@ static uint8_t getObject(CodecId codecId){
}
}
void MP4Muxer::stampSync(){
void MP4MuxerInterface::stampSync(){
if(_codec_to_trackid.size() < 2){
return;
}
@@ -156,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();
@@ -178,7 +203,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
return;
}
auto track_id = mov_writer_add_audio(_mov_writter.get(),
auto track_id = mp4_writer_add_audio(_mov_writter.get(),
mp4_object,
audio_track->getAudioChannel(),
audio_track->getAudioSampleBit() * audio_track->getAudioChannel(),
@@ -199,7 +224,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
return;
}
auto track_id = mov_writer_add_audio(_mov_writter.get(),
auto track_id = mp4_writer_add_audio(_mov_writter.get(),
mp4_object,
audio_track->getAudioChannel(),
audio_track->getAudioSampleBit() * audio_track->getAudioChannel(),
@@ -232,7 +257,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
return;
}
auto track_id = mov_writer_add_video(_mov_writter.get(),
auto track_id = mp4_writer_add_video(_mov_writter.get(),
mp4_object,
h264_track->getVideoWidth(),
h264_track->getVideoHeight(),
@@ -267,7 +292,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
return;
}
auto track_id = mov_writer_add_video(_mov_writter.get(),
auto track_id = mp4_writer_add_video(_mov_writter.get(),
mp4_object,
h265_track->getVideoWidth(),
h265_track->getVideoHeight(),
@@ -289,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

View File

@@ -23,17 +23,16 @@
namespace mediakit{
class MP4Muxer : public MediaSinkInterface, public MP4File{
class MP4MuxerInterface : public MediaSinkInterface {
public:
typedef std::shared_ptr<MP4Muxer> Ptr;
MP4Muxer(const char *file);
~MP4Muxer() override;
MP4MuxerInterface() = default;
~MP4MuxerInterface() override = default;
/**
* 添加已经ready状态的track
*/
void addTrack(const Track::Ptr & track) override;
void addTrack(const Track::Ptr &track) override;
/**
* 输入帧
*/
@@ -42,30 +41,112 @@ public:
/**
* 重置所有track
*/
void resetTracks() override ;
void resetTracks() override;
/**
* 是否包含视频
*/
bool haveVideo() const;
/**
* 保存fmp4分片
*/
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;
};
List<Frame::Ptr> _frameCached;
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();
private:
void openMP4();
void stampSync();
protected:
MP4FileIO::Writer createWriter() override;
private:
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;
MP4File::Writer _mov_writter;
string _file_name;
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

View File

@@ -29,7 +29,8 @@ MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string &
strFileName = File::absolutePath(strFileName,recordPath);
}
_demuxer = std::make_shared<MP4Demuxer>(strFileName.data());
_demuxer = std::make_shared<MP4Demuxer>();
_demuxer->openMP4(strFileName);
_mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost, strApp, strId, _demuxer->getDurationMS() / 1000.0, true, true, false, false));
auto tracks = _demuxer->getTracks(false);
if(tracks.empty()){

View File

@@ -35,6 +35,7 @@ public:
* 意思是在文件流化结束之前或中断之前,MP4Reader对象是不会被销毁的(不管有没有被外部对象持有)
*/
void startReadMP4();
private:
//MediaSourceEvent override
bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override;
@@ -45,15 +46,16 @@ private:
uint32_t getCurrentStamp();
void setCurrentStamp(uint32_t ui32Stamp);
bool seekTo(uint32_t ui32Stamp);
private:
recursive_mutex _mtx;
MultiMediaSourceMuxer::Ptr _mediaMuxer;
bool _have_video = false;
uint32_t _seek_to;
recursive_mutex _mtx;
Ticker _seek_ticker;
Timer::Ptr _timer;
EventPoller::Ptr _poller;
MP4Demuxer::Ptr _demuxer;
bool _have_video = false;
MultiMediaSourceMuxer::Ptr _mediaMuxer;
};
} /* namespace mediakit */

View File

@@ -25,10 +25,10 @@ MP4Recorder::MP4Recorder(const string& strPath,
const string &strStreamId) {
_strPath = strPath;
/////record 业务逻辑//////
_info.strAppName = strApp;
_info.strStreamId = strStreamId;
_info.strVhost = strVhost;
_info.strFolder = strPath;
_info.app = strApp;
_info.stream = strStreamId;
_info.vhost = strVhost;
_info.folder = strPath;
}
MP4Recorder::~MP4Recorder() {
closeFile();
@@ -42,26 +42,27 @@ void MP4Recorder::createFile() {
auto strFile = _strPath + strDate + "/" + strTime + ".mp4";
/////record 业务逻辑//////
_info.ui64StartedTime = ::time(NULL);
_info.strFileName = strTime + ".mp4";
_info.strFilePath = strFile;
_info.start_time = ::time(NULL);
_info.file_name = strTime + ".mp4";
_info.file_path = strFile;
GET_CONFIG(string,appName,Record::kAppName);
_info.strUrl = appName + "/"
+ _info.strAppName + "/"
+ _info.strStreamId + "/"
+ strDate + "/"
+ strTime + ".mp4";
_info.url = appName + "/"
+ _info.app + "/"
+ _info.stream + "/"
+ strDate + "/"
+ strTime + ".mp4";
try {
_muxer = std::make_shared<MP4Muxer>(strFileTmp.data());
for(auto &track :_tracks){
_muxer = std::make_shared<MP4Muxer>();
_muxer->openMP4(strFileTmp);
for (auto &track :_tracks) {
//添加track
_muxer->addTrack(track);
}
_strFileTmp = strFileTmp;
_strFile = strFile;
_createFileTicker.resetTime();
}catch(std::exception &ex) {
} catch (std::exception &ex) {
WarnL << ex.what();
}
}
@@ -73,7 +74,7 @@ void MP4Recorder::asyncClose() {
auto info = _info;
WorkThreadPool::Instance().getExecutor()->async([muxer,strFileTmp,strFile,info]() {
//获取文件录制时间放在关闭mp4之前是为了忽略关闭mp4执行时间
const_cast<MP4Info&>(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime;
const_cast<RecordInfo&>(info).time_len = ::time(NULL) - info.start_time;
//关闭mp4非常耗时所以要放在后台线程执行
muxer->closeMP4();
//临时文件名改成正式文件名防止mp4未完成时被访问
@@ -81,7 +82,7 @@ void MP4Recorder::asyncClose() {
//获取文件大小
struct stat fileData;
stat(strFile.data(), &fileData);
const_cast<MP4Info&>(info).ui64FileSize = fileData.st_size;
const_cast<RecordInfo&>(info).file_size = fileData.st_size;
/////record 业务逻辑//////
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info);
});

View File

@@ -20,24 +20,11 @@
#include "Util/TimeTicker.h"
#include "Common/MediaSink.h"
#include "MP4Muxer.h"
using namespace toolkit;
namespace mediakit {
class MP4Info {
public:
time_t ui64StartedTime; //GMT标准时间单位秒
time_t ui64TimeLen;//录像长度,单位秒
off_t ui64FileSize;//文件大小单位BYTE
string strFilePath;//文件路径
string strFileName;//文件名称
string strFolder;//文件夹路径
string strUrl;//播放路径
string strAppName;//应用名称
string strStreamId;//流ID
string strVhost;//vhost
};
#ifdef ENABLE_MP4
class MP4Recorder : public MediaSinkInterface{
public:
@@ -72,7 +59,7 @@ private:
string _strFile;
string _strFileTmp;
Ticker _createFileTicker;
MP4Info _info;
RecordInfo _info;
bool _haveVideo = false;
MP4Muxer::Ptr _muxer;
list<Track::Ptr> _tracks;

View File

@@ -16,6 +16,20 @@ using namespace std;
namespace mediakit {
class MediaSinkInterface;
class RecordInfo {
public:
time_t start_time; // GMT 标准时间,单位秒
float time_len; // 录像长度,单位秒
off_t file_size; // 文件大小,单位 BYTE
string file_path; // 文件路径
string file_name; // 文件名称
string folder; // 文件夹路径
string url; // 播放路径
string app; // 应用名称
string stream; // 流 ID
string vhost; // 虚拟主机
};
class Recorder{
public:
typedef enum {