mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-07-06 11:38:11 +08:00
合并主分支
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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文件
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
128
src/Record/MP4.h
128
src/Record/MP4.h
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()){
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user