草庐IT

FFmpeg4入门13:h264编码为mp4

幽迷狂 2023-06-07 原文

上一篇将yuv源视频文件编码为*.h264的由libx264实现压缩的文件,将源文件从55M编码为620KB,但是h264文件只有视频数据,而且使用范围不太广。那么就需要进一步的封装,在此选用最常用的mp4格式为例。

随便选一个mp4格式文件,用FFmpeg4入门4:解析视频并输出视频信息或者ffprobe查看一下数据。

迈克尔杰克逊的beat it.mp4,输出如下:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'beat.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf57.73.100
  Duration: 00:05:00.88, start: 0.000000, bitrate: 683 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 852x480, 612 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 64 kb/s (default)
    Metadata:
      handler_name    : SoundHandler

有两条流,一条h264的视频流,一条aac的音频流。现在我们有h264的流,创建一个mp4文件,新建一条流并将h264流插入进去。(暂时没有音频部分)

转换流程图为:

h264->mp4

打开输入文件(h264)

//======================输入部分============================//
//打开输入文件
if(avformat_open_input(&inVFmtCtx,inVFileName,NULL,NULL)<0){
    printf("Cannot open input file.\n");
    break;
}

//查找输入文件中的流
if(avformat_find_stream_info(inVFmtCtx,NULL)<0){
    printf("Cannot find stream info in input file.\n");
    break;
}

//查找视频流在文件中的位置
for(size_t i=0;i<inVFmtCtx->nb_streams;i++){
    if(inVFmtCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){
        inVStreamIndex=(int)i;
        break;
    }
}

AVCodecParameters *codecPara = inVFmtCtx->streams[inVStreamIndex]->codecpar;//输入视频流的编码参数

printf("===============Input information========>\n");
av_dump_format(inVFmtCtx, 0, inVFileName, 0);
printf("===============Input information========<\n");

可以看到和正常打开视频文件一样,或者说除了原始视频文件(yuv格式)其他格式打开代码差不多。

打开输出文件(mp4)

//=====================输出部分=========================//
//打开输出文件并填充格式数据
if(avformat_alloc_output_context2(&outFmtCtx,NULL,NULL,outFileName)<0){
    printf("Cannot alloc output file context.\n");
    break;
}

//打开输出文件并填充数据
if(avio_open(&outFmtCtx->pb,outFileName,AVIO_FLAG_READ_WRITE)<0){
    printf("output file open failed.\n");
    break;
}

//在输出的mp4文件中创建一条视频流
AVStream *outVStream = avformat_new_stream(outFmtCtx,NULL);
if(!outVStream){
    printf("Failed allocating output stream.\n");
    break;
}
outVStream->time_base.den=25;
outVStream->time_base.num=1;
outVStreamIndex=outVStream->index;

//查找编码器
AVCodec *outCodec = avcodec_find_encoder(codecPara->codec_id);
if(outCodec==NULL){
    printf("Cannot find any encoder.\n");
    break;
}

//从输入的h264编码器数据复制一份到输出文件的编码器中
AVCodecContext *outCodecCtx=avcodec_alloc_context3(outCodec);
AVCodecParameters *outCodecPara = outFmtCtx->streams[outVStream->index]->codecpar;
if(avcodec_parameters_copy(outCodecPara,codecPara)<0){
    printf("Cannot copy codec para.\n");
    break;
}
if(avcodec_parameters_to_context(outCodecCtx,outCodecPara)<0){
    printf("Cannot alloc codec ctx from para.\n");
    break;
}
outCodecCtx->time_base.den=25;
outCodecCtx->time_base.num=1;

//打开输出文件需要的编码器
if(avcodec_open2(outCodecCtx,outCodec,NULL)<0){
    printf("Cannot open output codec.\n");
    break;
}

printf("============Output Information=============>\n");
av_dump_format(outFmtCtx,0,outFileName,1);
printf("============Output Information=============<\n");

和上一篇类似。

编码部分

源文件是h264,MP4中的流也是h264,也就是说实际上并没有真正意义上的编码过程。

//写入文件头
if(avformat_write_header(outFmtCtx,NULL)<0){
    printf("Cannot write header to file.\n");
    return -1;
}

//===============编码部分===============//

AVStream *inVStream = inVFmtCtx->streams[inVStreamIndex];
while(av_read_frame(inVFmtCtx,pkt)>=0){//循环读取每一帧直到读完
    if(pkt->stream_index==inVStreamIndex){//确保处理的是视频流
        //FIXME:No PTS (Example: Raw H.264)
        //Simple Write PTS
        //如果当前处理帧的显示时间戳为0或者没有等等不是正常值
        if(pkt->pts==AV_NOPTS_VALUE){
            printf("frame_index:%d\n", frame_index);
            //Write PTS
            AVRational time_base1 = inVStream->time_base;
            //Duration between 2 frames (us)
            int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(inVStream->r_frame_rate);
            //Parameters
            pkt->pts = (double)(frame_index*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
            pkt->dts = pkt->pts;
            pkt->duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
            frame_index++;
        }
        //Convert PTS/DTS
        pkt->pts = av_rescale_q_rnd(pkt->pts, inVStream->time_base, outVStream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt->dts = av_rescale_q_rnd(pkt->dts, inVStream->time_base, outVStream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt->duration = av_rescale_q(pkt->duration, inVStream->time_base, outVStream->time_base);
        pkt->pos = -1;
        pkt->stream_index = outVStreamIndex;
        printf("Write 1 Packet. size:%5d\tpts:%ld\n", pkt->size, pkt->pts);
        //Write
        if (av_interleaved_write_frame(outFmtCtx, pkt) < 0) {
            printf("Error muxing packet\n");
            break;
        }
        av_packet_unref(pkt);
    }
}

av_write_trailer(outFmtCtx);

只是一些时间戳方面的处理。

软件输出

===============Input information========&gt;
Input #0, h264, from 'result.h264':
  Duration: N/A, bitrate: N/A
    Stream #0:0: Video: h264 (High), yuv420p(progressive), 352x288, 25 fps, 25 tbr, 1200k tbn, 50 tbc
===============Input information========&lt;
Cannot open libmwv206dec.so, libmwv206dec.so: cannot open shared object file: No such file or directory
[libx264 @ 0x162cd00] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0x162cd00] profile High, level 1.3
============Output Information=============&gt;
Output #0, mp4, to 'result.mp4':
    Stream #0:0: Video: h264 (High), yuv420p(progressive), 352x288, q=2-31, 25 tbn
============Output Information=============&lt;
OutVIndex=0,pkt.stream_index=0
frame_index==0
Write 1 Packet. size: 6899      pts:0
...共300个

Cannot open libmwv206dec.so ...是deepin linux系统错误。

执行完毕后得到只有视频没有音频的mp4文件。

完整代码在ffmpeg_Binner13.video_encode_h2642mp4里面。

本文首发于:FFmpeg4入门13:h264编码为mp4

有关FFmpeg4入门13:h264编码为mp4的更多相关文章

  1. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  2. ruby - 用逗号、双引号和编码解析 csv - 2

    我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

  3. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  4. ruby - 安装libv8(3.11.8.13)出错,Bundler无法继续 - 2

    运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin

  5. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  6. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  7. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  8. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  9. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  10. ruby - 无法在 Ruby 中将 ffmpeg 作为子进程运行 - 2

    我正在尝试使用以下代码通过将ffmpeg实用程序作为子进程运行并获取其输出并解析它来确定视频分辨率:IO.popen'ffmpeg-i'+path_to_filedo|ffmpegIO|#myparsegoeshereend...但是ffmpeg输出仍然连接到标准输出并且ffmepgIO.readlines是空的。ffmpeg实用程序是否需要一些特殊处理?或者还有其他方法可以获得ffmpeg输出吗?我在WinXP和FedoraLinux下测试了这段代码-结果是一样的。 最佳答案 要跟进mouviciel的评论,您需要使用类似pope

随机推荐