2020-05-18 15:31:49 +08:00
|
|
|
|
/*
|
2020-04-04 20:30:09 +08:00
|
|
|
|
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
2020-04-03 20:46:55 +08:00
|
|
|
|
*
|
2021-01-17 18:31:50 +08:00
|
|
|
|
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
|
2020-04-03 20:46:55 +08:00
|
|
|
|
*
|
2020-04-04 20:30:09 +08:00
|
|
|
|
* 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.
|
2020-04-03 20:46:55 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifdef ENABLE_MP4
|
|
|
|
|
#include "MP4.h"
|
|
|
|
|
#include "Util/File.h"
|
|
|
|
|
#include "Util/logger.h"
|
|
|
|
|
#include "Common/config.h"
|
2020-09-20 19:44:20 +08:00
|
|
|
|
#include "fmp4-writer.h"
|
|
|
|
|
|
2020-04-03 20:46:55 +08:00
|
|
|
|
using namespace toolkit;
|
|
|
|
|
namespace mediakit {
|
|
|
|
|
|
2020-09-20 19:45:04 +08:00
|
|
|
|
/////////////////////////////////////////////////mp4_writer_t/////////////////////////////////////////////////
|
|
|
|
|
|
2020-09-20 19:44:20 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-20 19:45:04 +08:00
|
|
|
|
/////////////////////////////////////////////////MP4FileIO/////////////////////////////////////////////////
|
|
|
|
|
|
2020-04-03 20:46:55 +08:00
|
|
|
|
static struct mov_buffer_t s_io = {
|
2020-09-20 19:44:20 +08:00
|
|
|
|
[](void *ctx, void *data, uint64_t bytes) {
|
2020-09-20 19:45:04 +08:00
|
|
|
|
MP4FileIO *thiz = (MP4FileIO *) ctx;
|
2020-09-20 19:44:20 +08:00
|
|
|
|
return thiz->onRead(data, bytes);
|
2020-04-03 20:46:55 +08:00
|
|
|
|
},
|
2020-09-20 19:44:20 +08:00
|
|
|
|
[](void *ctx, const void *data, uint64_t bytes) {
|
2020-09-20 19:45:04 +08:00
|
|
|
|
MP4FileIO *thiz = (MP4FileIO *) ctx;
|
2020-09-20 19:44:20 +08:00
|
|
|
|
return thiz->onWrite(data, bytes);
|
2020-04-03 20:46:55 +08:00
|
|
|
|
},
|
2020-09-20 19:44:20 +08:00
|
|
|
|
[](void *ctx, uint64_t offset) {
|
2020-09-20 19:45:04 +08:00
|
|
|
|
MP4FileIO *thiz = (MP4FileIO *) ctx;
|
2020-04-03 20:46:55 +08:00
|
|
|
|
return thiz->onSeek(offset);
|
|
|
|
|
},
|
2020-09-20 19:44:20 +08:00
|
|
|
|
[](void *ctx) {
|
2020-09-20 19:45:04 +08:00
|
|
|
|
MP4FileIO *thiz = (MP4FileIO *) ctx;
|
2020-04-03 20:46:55 +08:00
|
|
|
|
return thiz->onTell();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-09-20 19:45:04 +08:00
|
|
|
|
MP4FileIO::Writer MP4FileIO::createWriter(int flags, bool is_fmp4){
|
2020-04-03 20:46:55 +08:00
|
|
|
|
Writer writer;
|
2020-09-20 19:45:04 +08:00
|
|
|
|
Ptr self = shared_from_this();
|
|
|
|
|
//保存自己的强引用,防止提前释放
|
|
|
|
|
writer.reset(mp4_writer_create(is_fmp4, &s_io,this, flags),[self](mp4_writer_t *ptr){
|
2020-04-03 20:46:55 +08:00
|
|
|
|
if(ptr){
|
2020-09-20 19:44:20 +08:00
|
|
|
|
mp4_writer_destroy(ptr);
|
2020-04-03 20:46:55 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if(!writer){
|
|
|
|
|
throw std::runtime_error("写入mp4文件失败!");
|
|
|
|
|
}
|
|
|
|
|
return writer;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-20 19:45:04 +08:00
|
|
|
|
MP4FileIO::Reader MP4FileIO::createReader(){
|
2020-04-03 20:46:55 +08:00
|
|
|
|
Reader reader;
|
2020-09-20 19:45:04 +08:00
|
|
|
|
Ptr self = shared_from_this();
|
|
|
|
|
//保存自己的强引用,防止提前释放
|
|
|
|
|
reader.reset(mov_reader_create(&s_io,this),[self](mov_reader_t *ptr){
|
2020-04-03 20:46:55 +08:00
|
|
|
|
if(ptr){
|
|
|
|
|
mov_reader_destroy(ptr);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if(!reader){
|
|
|
|
|
throw std::runtime_error("读取mp4文件失败!");
|
|
|
|
|
}
|
|
|
|
|
return reader;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-20 19:45:04 +08:00
|
|
|
|
/////////////////////////////////////////////////////MP4FileDisk/////////////////////////////////////////////////////////
|
|
|
|
|
|
2020-04-03 20:46:55 +08:00
|
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
|
|
|
#define fseek64 _fseeki64
|
2020-09-20 19:45:04 +08:00
|
|
|
|
#define ftell64 _ftelli64
|
2020-04-03 20:46:55 +08:00
|
|
|
|
#else
|
2020-09-20 19:45:04 +08:00
|
|
|
|
#define fseek64 fseek
|
|
|
|
|
#define ftell64 ftell
|
2020-04-03 20:46:55 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
2020-09-20 19:45:04 +08:00
|
|
|
|
void MP4FileDisk::openFile(const char *file, const char *mode) {
|
2020-04-03 20:46:55 +08:00
|
|
|
|
//创建文件
|
2020-04-24 12:39:22 +08:00
|
|
|
|
auto fp = File::create_file(file, mode);
|
2020-04-03 20:46:55 +08:00
|
|
|
|
if(!fp){
|
|
|
|
|
throw std::runtime_error(string("打开文件失败:") + file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GET_CONFIG(uint32_t,mp4BufSize,Record::kFileBufSize);
|
|
|
|
|
|
|
|
|
|
//新建文件io缓存
|
|
|
|
|
std::shared_ptr<char> file_buf(new char[mp4BufSize],[](char *ptr){
|
|
|
|
|
if(ptr){
|
|
|
|
|
delete [] ptr;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if(file_buf){
|
|
|
|
|
//设置文件io缓存
|
|
|
|
|
setvbuf(fp, file_buf.get(), _IOFBF, mp4BufSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//创建智能指针
|
|
|
|
|
_file.reset(fp,[file_buf](FILE *fp) {
|
2020-07-16 10:40:30 +08:00
|
|
|
|
fflush(fp);
|
2020-04-03 20:46:55 +08:00
|
|
|
|
fclose(fp);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-20 19:45:04 +08:00
|
|
|
|
void MP4FileDisk::closeFile() {
|
2020-04-03 20:46:55 +08:00
|
|
|
|
_file = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
int MP4FileDisk::onRead(void *data, size_t bytes) {
|
2020-04-03 20:46:55 +08:00
|
|
|
|
if (bytes == fread(data, 1, bytes, _file.get())){
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 0 != ferror(_file.get()) ? ferror(_file.get()) : -1 /*EOF*/;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
int MP4FileDisk::onWrite(const void *data, size_t bytes) {
|
2020-04-03 20:46:55 +08:00
|
|
|
|
return bytes == fwrite(data, 1, bytes, _file.get()) ? 0 : ferror(_file.get());
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
int MP4FileDisk::onSeek(size_t offset) {
|
2020-04-03 20:46:55 +08:00
|
|
|
|
return fseek64(_file.get(), offset, SEEK_SET);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
size_t MP4FileDisk::onTell() {
|
2020-04-03 20:46:55 +08:00
|
|
|
|
return ftell64(_file.get());
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-20 19:45:37 +08:00
|
|
|
|
/////////////////////////////////////////////////////MP4FileMemory/////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
string MP4FileMemory::getAndClearMemory(){
|
|
|
|
|
string ret;
|
|
|
|
|
ret.swap(_memory);
|
|
|
|
|
_offset = 0;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
size_t MP4FileMemory::fileSize() const{
|
2020-09-20 19:45:37 +08:00
|
|
|
|
return _memory.size();
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
size_t MP4FileMemory::onTell(){
|
2020-09-20 19:45:37 +08:00
|
|
|
|
return _offset;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
int MP4FileMemory::onSeek(size_t offset){
|
2020-09-20 19:45:37 +08:00
|
|
|
|
if (offset > _memory.size()) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_offset = offset;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
int MP4FileMemory::onRead(void *data, size_t bytes){
|
2020-09-20 19:45:37 +08:00
|
|
|
|
if (_offset >= _memory.size()) {
|
|
|
|
|
//EOF
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
bytes = MIN(bytes, _memory.size() - _offset);
|
|
|
|
|
memcpy(data, _memory.data(), bytes);
|
|
|
|
|
_offset += bytes;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
int MP4FileMemory::onWrite(const void *data, size_t bytes){
|
2020-09-20 19:45:37 +08:00
|
|
|
|
if (_offset + bytes > _memory.size()) {
|
|
|
|
|
//需要扩容
|
|
|
|
|
_memory.resize(_offset + bytes);
|
|
|
|
|
}
|
|
|
|
|
memcpy((uint8_t *) _memory.data() + _offset, data, bytes);
|
|
|
|
|
_offset += bytes;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-03 20:46:55 +08:00
|
|
|
|
}//namespace mediakit
|
|
|
|
|
#endif //NABLE_MP4RECORD
|