ffmpeg-实现视频 metadata(moov) 前置

章节

  • 问题
  • 分析
  • 解决方案
  • 结果

1、问题

1.1 问题描述

基于原生 ffmpeg api 封装而成的 libvideo_util 库最终经解码->帧上绘图->编码、生成的视频上传至 rgw 之后,出现线上拖拽访问卡顿的问题。

如下图所示:


image.png

左边 为经过 libvideo_util 库处理生成的视频文件,可以看到加载非常慢,且没面加载数据量大约为 4KB左右。
右边 为经过 ffmpeg 命令行处理过的视频文件,拖拽访问无问题,即可以实现快速预加载。
命令行如下所示:

ffmpeg -i bad.mp4 -c copy good.mp4

如果左边的视频推送给用户观看,可想而知当用户拖拽浏览视频时,体验效果是非常差的。

2、分析

2.1 是什么导致了上述问题?

我将问题描述给了导师,他说昨天正好去听了视频播放原理的分享,分享中有提到:视频的存储在文件中的逻辑存储方式是 box 形式存在,这些 box 分类如下(mp4info.exe 工具):

image.png

ftyp、free、mdat(视频实质内容)、moov(视频meta 信息)

分享中阐述到:如果 视频的meta信息存储在视频文件尾部,那么浏览器通过网络传输协议加载远程资源服务器视频流时,浏览器不能迅速得到视频 meta信息(全局信息),这会导致浏览器不能很好的对视频进行预加载处理,所以就会出现拖拽访问卡顿的问题。

解决办法是在生成目标视频时,将metadata信息(moov)提前至视频第一帧的前面。

我在网上找到关于 短视频秒开 的优化原理,其中的一段话让我坚定 将 moov 数据提前至视频第一帧是可行的方案。如下图所示:


image.png

3、解决方案

代码片段如下所示:

3.1 VideoWriter->新增 AVDictionary *dict;

/// 输出流 视频
typedef struct {
    AVFormatContext *ofmt_ctx;
    AVPacket avpkt;
    CodecContext *arr_codec_ctx;
    int video_stream;          //视频流的流下标
    int audio_stream;
    uint8_t is_init;
    struct SwsContext *scxt;
    AVDictionary *dict;
    AVFrame *RGBFrame;
    AVFrame *YUVFrame;
    uint8_t *yuv_buff;
    uint8_t *out_buff;
    int outbuff_len;
    int error_code;
    char error_msg[128];
} VideoWriter;

3.2 打开输出流-> 设置 moov 信息前置

/// 打开要写入的视频文件
int VideoWriter_OpenVideo(void *writer, void *reader, const char *target_file,
                          int width, int height) {
    VideoWriter *w = (VideoWriter *) writer;
    VideoReader *r = (VideoReader *) reader;
    AVCodecContext *video_dec_ctx = r->arr_codec_ctx[r->video_stream].ctx;
    if (width <= 0 || height <= 0) {
        width = video_dec_ctx->width;
        height = video_dec_ctx->height;
    }

    w->error_code = avformat_alloc_output_context2(&(w->ofmt_ctx), NULL, NULL,
                                                   target_file);
    if (w->error_code < 0) {
        snprintf(w->error_msg, sizeof(w->error_msg),
                 "avformat_alloc_output_context2 fail");
        return -1;
    }

    if (_open_output_file(r, w, width, height) < 0) {
        return -1;
    }

    //Open output file
    if (!(w->ofmt_ctx->flags & AVFMT_NOFILE)) {
        w->error_code = avio_open(&(w->ofmt_ctx->pb), target_file,
                                  AVIO_FLAG_WRITE);
        if (w->error_code < 0) {
            snprintf(w->error_msg, sizeof(w->error_msg), "avio_open fail");
            return -7;
        }
    }
    // 移动 moov 至第一帧前面
    // _set_mov_moov_ahead(w);
    av_dict_set(&(w->dict),"moveflags","rtphint+faststart",0);

    //打印 dict 信息
//    if (av_dict_count(w->dict) > 0) {
//        printf("Using muxer settings:");
//
//        AVDictionaryEntry *entry = NULL;
//        while ((entry = av_dict_get(w->dict, "", entry,
//            AV_DICT_IGNORE_SUFFIX)))
//            printf("\n\t%s=%s", entry->key, entry->value);
//
//        printf("\n");
//    }

    w->error_code = avformat_write_header(w->ofmt_ctx, &(w->dict));
    if (w->error_code < 0) {
        snprintf(w->error_msg, sizeof(w->error_msg),
                 "avformat_write_header fail");
        return -7;
    }
    //AV_PIX_FMT_YUV420P
    w->scxt = sws_getContext(width, height, r->pix_fmt, width, height,
                             video_dec_ctx->pix_fmt, SWS_POINT, NULL, NULL, NULL);
    if (NULL == w->scxt) {
        w->error_code = -1;
        snprintf(w->error_msg, sizeof(w->error_msg), "sws_getContext fail");
        return -1;
    }
    w->RGBFrame = av_frame_alloc();
    w->YUVFrame = av_frame_alloc();
    w->yuv_buff = (uint8_t *) malloc((width * height * 3));
    w->outbuff_len = (width * height * 3);
    w->out_buff = (uint8_t *) malloc(w->outbuff_len);
    w->is_init = 0;
    return 0;
}

注意:

  av_dict_set(&(w->dict),"moveflags","rtphint+faststart",0);

要放置在 avformat_write_header() 之前

3.3 调用 avformat_write_header() 函数:

    w->error_code = avformat_write_header(w->ofmt_ctx, &(w->dict));
    if (w->error_code < 0) {
        snprintf(w->error_msg, sizeof(w->error_msg),
                 "avformat_write_header fail");
        return -7;
    }

4、结果

image.png

新版的libvideo_util ffmpeg 封装库 实现了类似边下边播功能,视频可以实现快速预加载、拖拽访问无卡顿。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • ### YUV颜色空间 视频是由一帧一帧的数据连接而成,而一帧视频数据其实就是一张图片。 yuv是一种图片储存格式...
    天使君阅读 3,247评论 0 4
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 苦难的意义 文章一开始讲到二战期间,受到上级命令,隐匿在丛林中与美国军队作战的日本人Onoda, 三十年不忘初心,...
    慢慢树阅读 104评论 0 1
  • 所谓感同身受,从来多只是说说而已。除非对方经历过与你一样的喜怒哀乐,否则“我理解你的感受”这句话的真实性多少都会打...
    殷悦Melody阅读 10,386评论 0 5
  • 在易效能的亲子时间管理课上,老师推荐了一个小工具——佳明儿童运动手环,用了一段时间后感觉非常不错,分享一下我家的使...
    胡豆儿阅读 2,008评论 2 4