mp4视频文件moov前置

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前置

  1. 使用命令

ffmpeg -i src.mp4 -movflags faststart -c copy dst.mp4

  1. 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");
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值