zoukankan      html  css  js  c++  java
  • ffmpeg architecture(下)

    ffmpeg architecture(下)

     3-转码

    TLDR;给我看代码和执行。

    $ make run_transcoding

    我们将跳过一些细节,但是请放心:源代码可在github上找到

    在本章中,我们将创建一个用C编写的极简代码转换器,可以使用FFmpeg / libav库(尤其是libavcodec,libavformat和libavutil)将H264编码的视频转换为H265。

     只是快速回顾一下:AVFormatContext是媒体文件格式的抽象,又名容器(例如:MKV,MP4,WEBM,TS)。的AV表示每种类型的用于给定格式(例如:音频,视频,字幕,元数据)的数据。所述AVPacket是从所获得的压缩数据的切片AVStream可以由一个解码avcodec:产生称为原始数据(例如AV1,H264,VP9,HEVC)AVFrame

    转码

    让我们从简单的转换操作开始,然后我们可以基于此代码构建,第一步是加载输入文件

    //分配一个AVFormatContext
    avfc = avformat_alloc_context();
    //打开输入流并读取标题。
    avformat_open_input(avfc,in_filename, NULL, NULL);
    //读取媒体文件的数据包以获取流信息。
    avformat_find_stream_info(avfc, NULL);

    现在,我们要设置解码器,它AVFormatContext将使我们能够访问所有AVStream组件,并且对于每个组件,我们都可以获取它们AVCodec并创建特定组件AVCodecContext,最后我们可以打开给定的编解码器,以便继续进行解码处理。

    所述AVCodecContext持有如比特率,帧速率,采样率,信道,高度,和许多其它有关的媒体数据的配置等。

    对于(int i = 0 ; i <avfc-> nb_streams; i ++)
    {
      AVStream * avs = avfc-> 流 [i];
      AVCodec * avc = avcodec_find_decoder(avs-> codecpar- > codec_id);
      AVCodecContext * avcc = avcodec_alloc_context3(* avc);
      avcodec_parameters_to_context(* avcc,avs-> codecpar);
      avcodec_open2(* avcc,* avc,NULL);
    }

    我们还需要准备输出媒体文件以进行多路传输,我们首先为输出分配内存AVFormatContext。我们以输出格式创建每个流。为了正确打包流,我们从解码器复制编解码器参数

    我们设置标志 AV_CODEC_FLAG_GLOBAL_HEADER告诉编码器它可以使用全局头文件,最后打开输出文件进行写入并保留头文件。

    avformat_alloc_output_context2(&encoder_avfc,NULL,NULL,out_filename);
    AVStream * avs = avformat_new_stream(encoder_avfc,NULL);
    avcodec_parameters_copy(avs-> codecpar,解码器_avs-> codecpar);
    如果(encoder_avfc-> oformat-> flags和AVFMT_GLOBALHEADER)
      encoder_avfc-> flags | = AV_CODEC_FLAG_GLOBAL_HEADER;
    avio_open(&encoder_avfc-> pb,编码器->文件名,AVIO_FLAG_WRITE);
    avformat_write_header(encoder- > avfc,&muxer_opts);

    我们AVPacket从解码器获取,调整时间戳,并将数据包正确写入输出文件。即使函数av_interleaved_write_frame说“写帧”,我们仍在存储数据包。通过将流预告片写入文件中,我们完成了转换过程。

    AVFrame * input_frame = av_frame_alloc();
    AVPacket * input_packet = av_packet_alloc();
    而(av_read_frame(decoder_avfc,input_packet)> = 0)
    {
      av_packet_rescale_ts(input_packet,解码器_视频_avs-> time_base,编码器 _视频_avs- > time_base);
      av_interleaved_write_frame(* avfc,input_packet)< 0));
    }
    av_write_trailer(encoder_avfc);

    转码

    上一节展示了一个简单的transmuxer程序,现在我们将添加对文件进行编码的功能,尤其是使它能够将视频从转换h264h265

    在准备好解码器之后,但在安排输出媒体文件之前,我们将设置编码器。

    AVRational input_framerate = av_guess_frame_rate(decoder_avfc,decoder_video_avs,NULL);
    AVStream * video_avs = avformat_new_stream(encoder_avfc,NULL);
    char * codec_name = “ libx265 ” ;
    char * codec_priv_key = “ x265-params ” ;
    //我们将为x265使用内部选项
    // //禁用场景更改检测,然后修复
    // 60帧的GOP。
    char * codec_priv_value = “ keyint = 60:min-keyint = 60:scenecut = 0 ” ;
    AVCodec * video_avc = avcodec_find_encoder_by_name(codec_name);
    AVCodecContext * video_avcc = avcodec_alloc_context3(video_avc);
    //编码器编解码器参数
    av_opt_set(sc-> video_avcc-> priv_data,codec_priv_key,codec_priv_value, 0);
    video_avcc-> height =解码器_ctx-> height;
    video_avcc-> width =解码器_ctx-> width;
    video_avcc-> pix_fmt = video_avc-> pix_fmts [ 0 ];
    //控制率 
    video_avcc-> bit_rate = 2 * 1000 * 1000 ;
    video_avcc-> rc_buffer_size = 4 * 1000 * 1000 ;
    video_avcc-> rc_max_rate = 2 * 1000 * 1000 ;
    video_avcc-> rc_min_rate = 2.5 * 1000 * 1000 ;
    //时基
    video_avcc-> time_base = av_inv_q(input_framerate);
    video_avs-> time_base = sc-> video_avcc-> time_base;
    avcodec_open2(sc-> video_avcc,sc-> video_avc,NULL);
    avcodec_parameters_from_context(sc-> video_avs-> codecpar,sc-> video_avcc);

    我们需要扩展解码循环以进行视频流转码:

    AVFrame * input_frame = av_frame_alloc();
    AVPacket * input_packet = av_packet_alloc();
    而(av_read_frame(decoder_avfc,input_packet)> = 0)
    {
      int响应= avcodec_send_packet(decoder_video_avcc,input_packet);
      while(响应> = 0){
        响应= avcodec_receive_frame(decoder_video_avcc,input_frame);
        if(响应== AVERROR(EAGAIN)|| response == AVERROR_EOF){
           中断 ;
        } else  if(response < 0){
           返回响应;
        }
        if(响应> = 0){
           编码(encoder_avfc,decoder_video_avs,encoder_video_avs,decoder_video_avcc,input_packet-> stream_index);
        }
        av_frame_unref(input_frame);
      }
      av_packet_unref(input_packet);
    }
    av_write_trailer(encoder_avfc);
    //使用的函数
    int  编码(AVFormatContext * avfc,AVStream * dec_video_avs,AVStream * enc_video_avs,AVCodecContext video_avcc int索引){
      AVPacket * output_packet = av_packet_alloc();
      int response = avcodec_send_frame(video_avcc,input_frame);
      while(响应> = 0){
        响应= avcodec_receive_packet(video_avcc,output_packet);
        if(响应== AVERROR(EAGAIN)|| response == AVERROR_EOF){
           中断 ;
        } else  if(响应< 0){
           return - 1 ;
        }
        output_packet-> stream_index = index ;
        output_packet-> 持续时间 = enc_video_avs-> 那么time_base。den / enc_video_avs-> time_base。num / dec_video_avs-> avg_frame_rate。num * dec_video_avs-> avg_frame_rate。巢穴 ;
        av_packet_rescale_ts(output_packet,dec_video_avs-> time_base,enc_video_avs-> time_base);
        响应= av_interleaved_write_frame(avfc,output_packet);
      }
      av_packet_unref(output_packet);
      av_packet_free(&output_packet);
      返回 0 ;
    }

    我们将媒体流从转换h264h265,正如预期的那样h265,媒体文件的版本小于,h264创建的程序能够:

      / *
        * H264-> H265 
       *音频->重混合(未触摸)
       * MP4-MP4 
    * / 
      StreamingParams sp = { 0 };   
      sp.copy_audio = 1 ;
      sp.copy_video = 0 ;
      sp.video_codec = “ libx265 ”;
      sp.codec_priv_key = “ x265-params ” ;
      sp.codec_priv_value = “ keyint = 60:min-keyint = 60:scenecut = 0 ” ;
      / *
        * H264-> H264(固定gop)
       *音频->重混合(未触摸)
       * MP4-MP4 
    * / 
      StreamingParams sp = { 0 };   
      sp.copy_audio = 1 ;
      sp.copy_video = 0 ;
      sp.video_codec = “ libx264 ”;
      sp.codec_priv_key = “ x264参数” ;
      sp.codec_priv_value = “ keyint = 60:min-keyint = 60:scenecut = 0:force-cfr = 1 ” ;
      / *
        * H264-> H264(固定gop)
       *音频->重混合(
    未修改)* MP4-    片段MP4 
    * / 
      StreamingParams sp = { 0 };   
      sp.copy_audio = 1 ;
      sp.copy_video = 0 ;
      sp.video_codec = “ libx264 ”;
      sp.codec_priv_key = “ x264参数” ;
      sp.codec_priv_value = “ keyint = 60:min-keyint = 60:scenecut = 0:force-cfr = 1 ” ;
      sp.muxer_opt_key = “ movflags ”;
      sp.muxer_opt_value = “ frag_keyframe + empty_moov + default_base_moof ” ;
      / *
        * H264-> H264(固定gop)
       *音频-> AAC 
       * MP4-MPEG-TS 
    * / 
      StreamingParams sp = { 0 };   
      sp.copy_audio = 0 ;
      sp.copy_video = 0 ;
      sp.video_codec = “ libx264 ”;
      sp.codec_priv_key = “ x264参数” ;
      sp.codec_priv_value = “ keyint = 60:min-keyint = 60:scenecut = 0:force-cfr = 1 ” ;
      sp.audio_codec = “ aac ” ;
      sp.output_extension = “ .ts ” ;
      / * WIP:P->它不在VLC上播放,最终比特率很高
       * H264-> VP9 
       *音频-> Vorbis 
       * MP4-WebM 
    * / // StreamingParams sp = {0}; // sp.copy_audio = 0; // sp.copy_video = 0; // sp.video_codec =“ libvpx-vp9”; // sp.audio_codec =“ libvorbis”; // sp.output_extension =“ .webm”;   
    现在,说实话,这是难度比我想这会是我不得不挖掘到FFmpeg的命令行的源代码和测试了很多,我觉得我失去了一些东西,因为我不得不执行force-cfrh264工作而且我仍然看到一些警告消息,例如warning messages (forced frame type (5) at 80 was changed to frame type (3))
  • 相关阅读:
    深入了解Element Form表单动态验证问题 转载
    使用 Element UI Select 组件的 value-key 属性,让绑定值可以为一个对象
    vue 中 使用 element-ui 发送请求前 校验全部表单,报警告: [Element Warn][Form]model is required for validate to work!
    vue element InfiniteScroll 无限滚动 入坑记录
    nginx实现跨域访问并支持(GET, POST,PUT,DELETE, OPTIONS)
    正则表达式记录
    element ui图片上传方法
    laravel 跨域解决方案
    vagrant的box哪里下?镜像在哪儿找?教你在vagrant官网下载各种最新.box资源
    Saas系统架构的思考,多租户Saas架构设计分析
  • 原文地址:https://www.cnblogs.com/wujianming-110117/p/13291377.html
Copyright © 2011-2022 走看看