• ffmpeg-4.2.2:视频编码流程(yuv编码h.264)


    这是基于FFMPEG的视频编码器,可以将yuv视频元数据编码成h264压缩编码数据。
    主要是记录一下自己学习FFMPEG时总结的视频编码流程。
    ffmpeg版本:ffmpeg-4.2.2
    libx264版本:x264-snapshot-20191023-2245-stable

    流程图

    简单介绍下各个函数的功能:

    avcodec_find_encoder_by_name():通过编码器的名字查找编码器
    avcodec_alloc_context3():初始化AVCodecContext
    avcodec_open2():打开编码器
    av_packet_alloc():初始化AVPacket
    av_frame_alloc():初始化AVFrame
    av_frame_get_buffer():为AVFrame->data等分配内存
    av_frame_make_writable():检查AVFrame->data是否可写
    avcodec_send_frame():编码视频:将一帧视频元数据发送给编码器
    avcodec_receive_packet():编码视频:接收编码完成的AVPacket数据包


    代码

    [plain] view plaincopy
    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <string.h>  
    4. #include <libavcodec/avcodec.h>  
    5. #include <libavutil/opt.h>  
    6. #include <libavutil/imgutils.h>  
    7.   
    8. static void encode(AVCodecContext *cdc_ctx, AVFrame *frame, AVPacket *pkt, FILE *fp_out)  
    9. {  
    10. int ret = 0;  
    11.   
    12. if (frame != NULL)  
    13. printf("Send %d frame. ", frame->pts);  
    14.   
    15. if ((ret = avcodec_send_frame(cdc_ctx, frame)) < 0)  
    16. {  
    17. fprintf(stderr, "avcodec_send_frame failed. ");  
    18. exit(1);  
    19. }  
    20.   
    21. while ((ret = avcodec_receive_packet(cdc_ctx, pkt)) >= 0)  
    22. {  
    23. printf("Write %d packet. ", pkt->pts);  
    24. fwrite(pkt->data, 1, pkt->size, fp_out);  
    25. av_packet_unref(pkt);  
    26. }  
    27.   
    28. if ((ret != AVERROR(EAGAIN)) && (ret != AVERROR_EOF))  
    29. {  
    30. fprintf(stderr, "avcodec_receive_packet failed. ");  
    31. exit(1);  
    32. }  
    33. }  
    34.   
    35. void encode_video(const char *input_file, const char *output_file, const char *encoder_name)  
    36. {  
    37. int ret = 0;  
    38. int i = 0;  
    39. AVCodec *codec = NULL;  
    40. AVCodecContext *cdc_ctx = NULL;  
    41. AVPacket *pkt = NULL;  
    42. AVFrame *frame = NULL;  
    43. FILE *fp_in, *fp_out;  
    44.   
    45. if ((codec = avcodec_find_encoder_by_name(encoder_name)) == NULL)  
    46. {  
    47. fprintf(stderr, "avcodec_find_encoder_by_name failed. ");  
    48. goto ret1;  
    49. }  
    50.   
    51. if ((cdc_ctx = avcodec_alloc_context3(codec)) == NULL)  
    52. {  
    53. fprintf(stderr, "avcodec_alloc_context3 failed. ");  
    54. goto ret1;  
    55. }  
    56. cdc_ctx->bit_rate = 400000;  
    57. cdc_ctx->width = 352;  
    58. cdc_ctx->height = 288;  
    59. cdc_ctx->time_base = (AVRational){1,25};  
    60. cdc_ctx->framerate = (AVRational){25,1};  
    61. cdc_ctx->gop_size = 10;  
    62. cdc_ctx->max_b_frames = 1;  
    63. cdc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;  
    64. if (codec->id == AV_CODEC_ID_H264)  
    65. av_opt_set(cdc_ctx->priv_data, "preset""slow"0);  
    66.   
    67. if ((ret = avcodec_open2(cdc_ctx, codec, NULL)) < 0)  
    68. {  
    69. fprintf(stderr, "avcodec_open2 failed. ");  
    70. goto ret2;  
    71. }  
    72.   
    73. if ((pkt = av_packet_alloc()) == NULL)  
    74. {  
    75. fprintf(stderr, "av_packet_alloc failed. ");  
    76. goto ret3;  
    77. }  
    78.   
    79. if ((frame = av_frame_alloc()) == NULL)  
    80. {  
    81. fprintf(stderr, "av_frame_alloc failed. ");  
    82. goto ret4;  
    83. }  
    84. frame->format = cdc_ctx->pix_fmt;  
    85. frame->width = cdc_ctx->width;  
    86. frame->height = cdc_ctx->height;  
    87.   
    88. if ((ret = av_frame_get_buffer(frame, 0)) < 0)  
    89. {  
    90. fprintf(stderr, "av_frame_get_buffer failed. ");  
    91. goto ret5;  
    92. }  
    93.   
    94. if ((fp_in = fopen(input_file, "rb")) == NULL)  
    95. {  
    96. fprintf(stderr, "fopen %s failed. ", input_file);  
    97. goto ret5;  
    98. }  
    99. if ((fp_out = fopen(output_file, "wb")) == NULL)  
    100. {  
    101. fprintf(stderr, "fopen %s failed. ", output_file);  
    102. goto ret6;  
    103. }  
    104.   
    105. while (feof(fp_in) == 0)  
    106. {  
    107. int y = 0;  
    108.   
    109. if ((ret = av_frame_make_writable(frame)) < 0)  
    110. {  
    111. fprintf(stderr, "frame is not writable. ");  
    112. goto ret7;  
    113. }  
    114.   
    115. /*y*/  
    116. for (y = 0; y < frame->height; y++)  
    117. fread(&frame->data[0][y * frame->linesize[0]], 1, frame->width, fp_in);  
    118. /*u*/  
    119. for (y = 0; y < frame->height / 2; y++)  
    120. fread(&frame->data[1][y * frame->linesize[1]], 1, frame->width / 2, fp_in);  
    121. /*v*/  
    122. for (y = 0; y < frame->height / 2; y++)  
    123. fread(&frame->data[2][y * frame->linesize[2]], 1, frame->width / 2, fp_in);  
    124.   
    125. frame->pts = i++;  
    126.   
    127. encode(cdc_ctx, frame, pkt, fp_out);  
    128. }  
    129.   
    130. /*flush buffer*/  
    131. encode(cdc_ctx, NULL, pkt, fp_out);  
    132.   
    133. fclose(fp_out);  
    134. fclose(fp_in);  
    135. av_frame_free(&frame);  
    136. av_packet_free(&pkt);  
    137. avcodec_close(cdc_ctx);  
    138. avcodec_free_context(&cdc_ctx);  
    139. return;  
    140. ret7:  
    141. fclose(fp_out);  
    142. ret6:  
    143. fclose(fp_in);  
    144. ret5:  
    145. av_frame_free(&frame);  
    146. ret4:  
    147. av_packet_free(&pkt);  
    148. ret3:  
    149. avcodec_close(cdc_ctx);  
    150. ret2:  
    151. avcodec_free_context(&cdc_ctx);  
    152. ret1:  
    153. exit(1);  
    154. }  
    155.   
    156. int main(int argc, const char *argv[])  
    157. {  
    158. if (argc < 4)  
    159. {  
    160. fprintf(stderr, "Uage:<input file> <output file> <encoder_name> ");  
    161. exit(0);  
    162. }  
    163.   
    164. encode_video(argv[1], argv[2], argv[3]);  
    165.   
    166. return 0;  
    167. }  

    注:

    1. 查找编码器也可以通过编码器ID使用以下函数查找:
      codec = avcode_find_encoder(AV_CODEC_ID_H264);
    2. 在循环读取yuv文件时,不能直接把一帧的y数据全读出来拷贝给AVFrame->data[0],因为yuv文件中y值一行的大小是分辨率的width,而data[0]中一行是AVFrame->linesize[0],AVFrame->linesize[0]略大于width,所以只能一行一行读取拷贝,u、v同理
    3. avcodec_send_frame()的第二个参数可以传入NULL,代表的是刷新包,表示流的结束
    4. flush buffer冲洗缓冲区其实就是在执行一次编码过程,但是avcodec_send_frame()第二个参数传入的是NULL,然后在循环调用avcodec_receive_packet接口时,如果编码器有缓冲AVPacket数据,就会返回它们

    下载

    项目主页

    Github:https://github.com/newbie-plan/encode_video

  • 相关阅读:
    pytest: error: unrecognized arguments: --html=report.html
    运行pytest报错“PytestUnknownMarkWarning: Unknown pytest.mark.***
    SQL启动代理服务-自动备份的前提
    SQL Server数据库每日自动备份作业操作步骤(转载)
    c#播放声音文件(转载)
    使用Office组件导出Excel表格
    四舍五入保留小数问题
    List数据集按对象某个属性排序
    C# 创建Windows Service(Windows服务)程序
    WebService下实现大数据量的传输(转载)
  • 原文地址:https://www.cnblogs.com/lidabo/p/15048267.html
Copyright © 2020-2023  润新知