mp4 moov前置
原理mp4box结构
mp4信息查看工具(Mp4box.js, github) mp4box.js
struct BoxHeader {
uint32_t size; //4byte,表示本box的长度(header+body),需要进行大小端转换
uint32_t type; //4byte, 是一个类型字符串非0结尾[ftyp,mdat,moov,....]
// uint64_t largesize; //当size==1时有效,测试视频没遇到这情况.....
uint8_t body[]; //内容
};
当size==1时需要使用8byte的扩展长度largesize
size包含自身的长度4
依据 type来确定其表示的类型
ffmpeg 将 mp4进行moov前置
- 使用命令
ffmpeg -i src.mp4 -movflags faststart -c copy dst.mp4
- ffmpeg代码使用
av_dict_set(&dict, “movflags”, “faststart”, 0);
AVFormatContext *ofmt_ctx = NULL;
AVDictionary *dict = NULL;
av_dict_set(&dict, "movflags", "faststart", 0);
... 在写文件头的时候增加
ret = avformat_write_header(ofmt_ctx, &dict);
... 你需要释放dict哦! av_dict_free(&dict);
c/c++ 描述
/// mp4 moov前置 c/c++
typedef struct {
uint32_t size;
uint32_t type;
uint64_t largesize;
int bodysize;
} mp4_header_largesize;
//从mp4文件中查找位置
// mdat位置变化后,moov下面的stco(chunk offset)也要改成新的offset
int mp4_find_box(const char* name, const char* type, int* pos, int* pos_end) {
*pos = 0;
*pos_end = 0;
FILE* fp = fopen(name, "rb");
if (NULL == fp) return -1;
mp4_header_largesize mp4boxlar;
int _pos = 0, _pos_start = 0;
while (!feof(fp)) {
_pos_start = _pos;
int ret = fread(&mp4boxlar, 1, 8, fp);
if (ret == 0) break;
mp4boxlar.bodysize = 0;
_pos += 8;
char namestr[5];
memcpy(namestr, &mp4boxlar.type, 4);
namestr[4] = 0;
if (htonl(mp4boxlar.size) == 1) { //没视频来验证.....
printf("largesize\n");
ret = fread(&mp4boxlar.largesize, 1, 8, fp);
if (ret == 0) break;
_pos += 8;
mp4boxlar.bodysize = htonl(mp4boxlar.largesize) - 8 - 8;
}
else {
mp4boxlar.bodysize = htonl(mp4boxlar.size) - 8;
}
ret = fseek(fp, mp4boxlar.bodysize, SEEK_CUR);
_pos += mp4boxlar.bodysize;
if (strcmp(namestr, type) == 0) {
*pos = _pos_start;
*pos_end = _pos;
break;
}
}
fclose(fp);
return 0;
}
int mp4_box_find(const void* data, int size, const char type[4], int* pos, int* pos_end) {
const uint8_t* ptr = (const uint8_t*)data;
const uint8_t* end = ptr + size;
const uint8_t* _pend = NULL;
mp4_header_largesize mp4boxlar;
while (ptr < end - 8) {
memset(&mp4boxlar, 0, sizeof(mp4boxlar));
memcpy(&mp4boxlar, ptr, 8);
if (ptr >= end) return -1;
if (memcmp(type, &mp4boxlar.type, 4) == 0) {
*pos = ptr - data;
_pend = ptr + ntohl(mp4boxlar.size);
if (_pend > end) _pend = end; //一般不会发生
*pos_end = _pend - data;
break;
}
ptr++;
}
return 0;
}
void mp4_box_info(const uint8_t* data, int size) {
mp4_header_largesize mp4boxlar;
const uint8_t *ptr = data, *end = data + size;
while (ptr < end - 8) {
memset(&mp4boxlar, 0, sizeof(mp4boxlar));
memcpy(&mp4boxlar, ptr, 8);
ptr += 8;
if (ptr >= end) return;
mp4boxlar.bodysize = htonl(mp4boxlar.size) - 8;
char namestr[5];
memcpy(namestr, &mp4boxlar.type, 4);
namestr[4] = 0;
printf("name=[%s] box_size=%d body_size=%d\n", namestr, htonl(mp4boxlar.size), mp4boxlar.bodysize);
if (mp4boxlar.bodysize <= 0) continue;
if (strcmp(namestr, "moov") == 0) {
continue;
}
if (strcmp(namestr, "mvhd") == 0) {
}
if (strcmp(namestr, "trak") == 0) {
mp4_box_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
if (strcmp(namestr, "edts") == 0) {
mp4_box_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
if (strcmp(namestr, "elst") == 0) {
// mp4_moov_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
if (strcmp(namestr, "mdia") == 0) {
mp4_box_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
if (strcmp(namestr, "minf") == 0) {
mp4_box_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
if (strcmp(namestr, "dinf") == 0) {
mp4_box_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
if (strcmp(namestr, "dref") == 0) {
// mp4_moov_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
if (strcmp(namestr, "stbl") == 0) {
mp4_box_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
if (strcmp(namestr, "stsd") == 0) {
// mp4_box_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
if (strcmp(namestr, "avc1") == 0) {
// mp4_box_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
if (strcmp(namestr, "udta") == 0) {
mp4_box_info(ptr, mp4boxlar.bodysize); //里面还有内容
}
ptr += mp4boxlar.bodysize; //下一个box
}
}
typedef struct {
int pos_start;
int pos_end;
} box_pos;
int file_read(const char* name, int pos, void* data, int size) {
int ret;
FILE* fp = fopen(name, "rb");
if (NULL == fp) return -1;
fseek(fp, pos, SEEK_CUR);
ret = fread(data, 1, size, fp);
fclose(fp);
return ret;
}
//来自网络的
void changeStco(unsigned char* buf, int len, int chunkDeta) {
#define SWAP_EDIAN_U32(X) (((0x000000ff & (X)) << 24) | ((0x0000ff00 & (X)) << 8) | ((0x00ff0000 & (X)) >> 8) | ((0xff000000 & (X)) >> 24))
int i = 0;
unsigned int* pchunkOffset = 0;
for (i = 16; i < len; i += 4) {
pchunkOffset = (unsigned int*)(buf + i);
*pchunkOffset = SWAP_EDIAN_U32(SWAP_EDIAN_U32(*pchunkOffset) + chunkDeta);
}
}
//从fp1读取size写到fp2
int file_copy(FILE* fp1, FILE* fp2, int size) {
char buff[1024]; //1k
int pos = 0;
int wlen = 0;
while (!feof(fp1) && pos < size) {
if (pos + sizeof(buff) < size) {
wlen = sizeof(buff);
}
else if (pos < size) {
wlen = size - pos;
}
int rlen = fread(buff, 1, wlen, fp1);
fwrite(buff, 1, wlen, fp2);
pos += rlen;
}
return pos;
}
//如何将mp4文件进行moov前置
void mp4filedown()
{
const char* filepath = "D:\\src\\src.mp4";
box_pos moov, mdat;
memset(&moov, 0, sizeof(moov));
memset(&mdat, 0, sizeof(mdat));
mp4_find_box(filepath, "moov", &moov.pos_start, &moov.pos_end);
mp4_find_box(filepath, "mdat", &mdat.pos_start, &mdat.pos_end);
printf("moov pos: %d, %d, size=%d\n", moov.pos_start, moov.pos_end, moov.pos_end - moov.pos_start);
printf("mdat pos: %d, %d, size=%d\n", mdat.pos_start, mdat.pos_end, mdat.pos_end - mdat.pos_start);
//判断pos_start位置, mdat位置变化后,moov下面的stco(chunk offset)也要改成新的offset,否则无法正常寻址播放!
// moov的开始位置小于mdat的开始位置,需要重新构建mp4发送内容
if (moov.pos_start > mdat.pos_start) {
int moov_size = moov.pos_end - moov.pos_start;
uint8_t* moov_data = (uint8_t*)malloc((moov_size) + 1);
file_read(filepath, moov.pos_start, moov_data, moov_size);
mp4_box_info(moov_data, moov.pos_end - moov.pos_start);
box_pos stco;
memset(&stco, 0, sizeof(stco));
mp4_box_find(moov_data, moov_size, "stco", &stco.pos_start, &stco.pos_end);
printf("stco pos: %d-%d, size=%d\n", stco.pos_start, stco.pos_end, stco.pos_end - stco.pos_start);
printf("stco pos: %d-%d, size=%d\n",
stco.pos_start + moov.pos_start, //
stco.pos_end + moov.pos_start, //
stco.pos_end - stco.pos_start);
//修改stco的位置
{
uint8_t* stco_ptr = moov_data + stco.pos_start;
int stco_size = stco.pos_end - stco.pos_start;
//stco_ptr +=4*4; //size,type,flags,version
//stco_size -= 4 * 4;
changeStco(stco_ptr, stco_size, moov_size);
}
//写到另外的文件中
FILE* fpsrc = fopen(filepath, "rb");
FILE* fpdst = fopen("D:\\src-lx\\dst.mp4", "wb");
//写到mdat.pos_start位置
file_copy(fpsrc, fpdst, mdat.pos_start); //写到mdat的开始位置
fseek(fpsrc, moov.pos_start, SEEK_SET); //跳转位置到moov
// file_copy(fpsrc, fpdst, moov.pos_end - moov.pos_start); //写moov
fwrite(moov_data, 1, moov_size, fpdst); //修改moov
fseek(fpsrc, mdat.pos_start, SEEK_SET); //跳转位置到mdat
file_copy(fpsrc, fpdst, mdat.pos_end - mdat.pos_start); //写mdat
fclose(fpsrc);
fclose(fpdst);
free(moov_data);
printf("\r\n");
}
}