FFMPEG学习 将YUV420P格式数据编码成H.264

  • 前言
  • 一、编码流程
  • 二、代码实现
  • 三、实现效果
  • 四、总结

前言

在学习FFMPEG过程中,我们需要获取到H264码流,但是我们采集到的一般都是YUV格式的数据,这里记录一下,用FFMPEG将采集到的YUV数据编码成H.264码流的学习过程, 并把遇到的问题和心得总结一下。


一、编码流程

二、代码实现

/*---------------------------------------------------------
*   文件名:transfer.c
*   文件说明:实现YUV数据编码成H.264文件
*   作者:hzg
*   修改记录:
*       1、修改了申请图像内存的对其数值,由原来的32位对齐修改为16位对齐,解决了编码成H.264文件之后播放绿屏乱码问题
*       2、编码第22帧的时候,才开始将包置为1,编码延迟较久。疑似编码选项av_opt_set问题
----------------------------------------------------------*/
#include <math.h>
#include <stdlib.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libavutil/frame.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>char * prename = "TestPre";
char * outname = "TestOut";
char * out_r = NULL;
int framenum = 0;int main(int argc, char **argv)
{AVFrame *frame;AVCodec *codec = NULL;AVPacket packet;AVCodecContext *codecContext;int readSize=0;int ret=0,getPacket;FILE * fileIn,*fileOut;int frameCount=0;/* register all the codecs */av_register_all();if(argc!=4){fprintf(stdout,"usage:./test_ffmpeg xxx.yuv width height\n");return -1;}//1.我们需要读一帧一帧的数据,所以需要AVFrame结构//读出的一帧数据保存在AVFrame中。frame  = av_frame_alloc();frame->width = atoi(argv[2]);frame->height = atoi(argv[3]);fprintf(stdout,"transf para width=%d,height=%d\n",frame->width,frame->height);frame->format = AV_PIX_FMT_YUV420P;//根据指定的宽,高,和像素格式申请图像内存,最后一个参数表示内存对齐的值,32位对齐4字节,导致352*288时候绿屏,尝试修改成16位对齐//修改成16位对齐之后av_image_alloc(frame->data,frame->linesize,frame->width,frame->height,frame->format,16);fileIn =fopen(argv[1],"r+");//2.读出来的数据保存在AVPacket中,因此,我们还需要AVPacket结构体//初始化packetav_init_packet(&packet);//3.读出来的数据,我们需要编码,因此需要编码器//下面的函数找到h.264类型的编码器/* find the mpeg1 video encoder */fprintf(stdout, "find encoder AV_CODEC_ID_H264\n");codec = avcodec_find_encoder(AV_CODEC_ID_H264);if (!codec) {fprintf(stderr, "Codec not found\n");exit(1);}//有了编码器,我们还需要编码器的上下文环境,用来控制编码的过程codecContext = avcodec_alloc_context3(codec);//分配AVCodecContext实例if (!codecContext){fprintf(stderr, "Could not allocate video codec context\n");return -1;}//设置编码器参数控制编码/* put sample parameters */codecContext->bit_rate = 400000;      //参数为400000的时候,编码出来的busH264文件为254KB/* resolution must be a multiple of two */codecContext->width = 352;              //输入文件bus是CIF格式,YV12 352*288codecContext->height = 288;/* frames per second */codecContext->time_base = (AVRational){1,25};/* emit one intra frame every ten frames* check frame pict_type before passing frame* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I* then gop_size is ignored and the output of encoder* will always be I frame irrespective to gop_size*/codecContext->gop_size = 10;          //每10帧发送一帧I帧/** vcodec_encode_video2函数输出的延时仅仅跟max_b_frames的设置有关,  * 想进行实时编码,将max_b_frames设置为0便没有编码延时了*/codecContext->max_b_frames = 1;            //每两个非B帧之间最多一个B帧codecContext->pix_fmt = AV_PIX_FMT_YUV420P;  /** * ultrafast,superfast, veryfast, faster, fast, medium * slow, slower, veryslow, placebo. 这是x264编码速度的选项 */  //av_opt_set(codecContext->priv_data, "preset", "slow", 0);av_opt_set(codecContext->priv_data, "preset", "ultrafast", 0);//准备好了编码器和编码器上下文环境,现在可以打开编码器了fprintf(stdout, "open encoder AV_CODEC_ID_H264\n");if (avcodec_open2(codecContext, codec, NULL) < 0)      //根据编码器上下文打开编码器{fprintf(stderr, "Could not open codec\n");return -1;}//4.准备输出文件fileOut= fopen("test.h264","w+");//下面开始编码while(1){//读一帧数据出来readSize = fread(frame->data[0],1,frame->linesize[0]*frame->height,fileIn);fprintf(stdout,"fread data[0] frame->linesize[0] %d,frame->height %d,readSize %d\n",frame->linesize[0],frame->height,readSize);if(readSize == 0){fprintf(stdout,"end of file\n");frameCount++;break;}readSize = fread(frame->data[1],1,frame->linesize[1]*frame->height/2,fileIn);//本应该是25344,实际是27648,帧数变成146,实际150fprintf(stdout,"fread data[1] frame->linesize[1] %d,readSize %d \n",frame->linesize[1],readSize);readSize = fread(frame->data[2],1,frame->linesize[2]*frame->height/2,fileIn);fprintf(stdout,"fread data[2] frame->linesize[2] %d readSize %d \n",frame->linesize[2],readSize);//初始化packetav_init_packet(&packet);/* encode the image */frame->pts = frameCount;//将AVFrame中的像素信息编码为AVPacket中的码流,成功编码一个packet,将getPacket置位1ret = avcodec_encode_video2(codecContext, &packet, frame, &getPacket); if (ret < 0) {fprintf(stderr, "Error encoding frame\n");return -1;}fprintf(stdout,"had encode %d frame,and getPacket is %d\n",framenum,getPacket);    //read并encode了22帧,才开始获得一个完整编码帧if (getPacket) {frameCount++;//获得一个完整的编码帧printf("Write frame %3d (size=%5d)\n", frameCount, packet.size);fwrite(packet.data, 1,packet.size, fileOut);av_packet_unref(&packet);}}/* flush buffer */for ( getPacket= 1; getPacket; frameCount++) {fprintf(stdout,"get flush buffer getPacket %d,frameCount %d\n",getPacket,frameCount);fflush(stdout);frame->pts = frameCount;ret = avcodec_encode_video2(codecContext, &packet, NULL, &getPacket);if (ret < 0){fprintf(stderr, "Error encoding frame\n");return -1;}if (getPacket) {printf("Write frame %3d (size=%5d)\n", frameCount, packet.size); //fwrite(packet.data, 1, packet.size, fileOut);av_packet_unref(&packet);}} fclose(fileIn);fclose(fileOut);av_frame_free(&frame);avcodec_close(codecContext);av_free(codecContext);return 0;
}

三、实现效果

视频不好弄,直接看截图效果

四、总结

实现的过程中遇到过问题,深入研究一下解决了一部分,还有一些后续跟进
Q1:刚编的时候发现我用的YUV数据流,编码之后播放是绿色的。如图

A1:这里查看了下,本身的源文件是150fps,但是实际编码帧数只有146fps,继续追踪发现在读取UV数据的时候,比实际源多了16个字节,再继续追踪,发现是因为在申请帧内存的时候采用的是32位对齐,在使用源为352*288的源的时候,UV数据都多了16字节,导致读取数据异常,编码也异常了。将内存申请帧的字节对齐修改为16位对齐就解决了。
Q2:看日志打印,在编码了22帧之后,才开始置位编码成功,got_packet_ptr置1。
A2:这里问题定义为编码延迟问题,通过设置preset参数可以在压缩效率和编码时间中做一个平衡,这里建议选择可以接受的最慢的值,这边尝试将 preset 由 slow 改为 ultrafast,编程了编码13帧才开始置1。这里并不是很满意,后面再继续研究。

FFMPEG 将YUV420P格式数据编码成H.264相关推荐

  1. FFmpeg将YUV420P格式数据编码成H.264

    一.编码流程 本文福利, 免费领取C++音视频学习资料包.技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,sr ...

  2. 【FFmpeg编码实战】(2)将YUV420P图片集编码成H.264视频文件(方法二)

    [FFmpeg编码实战](2)将YUV420P图片集编码成H.264视频文件(方法二) 一.编码成 H.264 视频文件,运行结果 二.编码成 MPEG4 视频文件,运行结果 三.编码成 AV_COD ...

  3. 在Linux环境下使用ffmpeg将PCM音频数据编码成aac数据

    在Linux环境下使用ffmpeg将PCM音频数据编码成aac数据 程序框图 代码演示 程序框图 将pcm数据经过aac编码器编码成aac数据,我是将从设备上采集的数据经过重采样送入aac编码器进行编 ...

  4. 利用FFmpeg编码器将JPG图片进行H.264编码原理

    利用FFmpeg编码器将JPG图片进行H.264编码原理 文章目录 利用FFmpeg编码器将JPG图片进行H.264编码原理 整体的编码流程 将JPG或BMP编码为YUV 利用FFmpeg将YUV格式 ...

  5. 使用FFmpeg的SDK库实现将H.264流封装进MP4文件时全局SPS、PPS与流中SPS、PPS冲突的问题

    一.问题 1. 使用FFmpeg的SDK库实现将H.264流封装进MP4文件的源码大致如下: char* filename = "./test.mp4" AVOutputForma ...

  6. Wireshark将UDP包解析成H.264

    音视频媒体 Wireshark将UDP包解析成H.264 Wireshark将UDP包解析成H.264 音视频媒体 前言 前言 话不多说,视频会议码流传输用的UDP协议,其网络封装为eth:ether ...

  7. 码流格式: Annex-B, AVCC(H.264)与HVCC(H.265), extradata详解

    1.前言 介绍H.264结构的文章铺天盖地,无责任翻译.无责任转载以及部分经验之谈(目前搜索最靠前的一篇实际是对stackoverflow上答案的翻译..链接后面给出了),所以缺的不是资料,是叙述准确 ...

  8. video format格式MJPEG,MPEG,H.264简介

     1.JPEG/M-JPEG JPEG是一种静止图像的压缩标准,它是一种标准的帧内压缩编码方式.当硬件处理速度足够快时,JPEG能用于实时动图像的视频压缩.在画面变动较小的情况下能提供相当不错的图像质 ...

  9. 【Android RTMP】安卓直播推流总结 ( 直播服务器搭建 | NV21 图像采集 | H.264 视频编码 | PCM 音频采集 | AAC 音频编码 | RTMP 包封装推流 )

    文章目录 一. 安卓直播推流专栏博客总结 二. 相关资源介绍 三. GitHub 源码地址 四. 整体 Android 直播推流数据到服务器并观看直播演示过程 Android 直播推流流程 : 手机采 ...

最新文章

  1. Ext2.0框架的Grid使用介绍(转)
  2. 网站压力测试工具webbench简介、安装、使用
  3. TensorFlow Wide And Deep 模型详解与应用 TensorFlow Wide-And-Deep 阅读344 作者简介:汪剑,现在在出门问问负责推荐与个性化。曾在微软雅虎工作,
  4. 如何设计实时数据平台(设计篇)
  5. Nginx(四):负载均衡Load balancing
  6. 【Tyvj3500】【BZOJ1031】字符加密,后缀数组
  7. ORACLE登录错误的解决方法
  8. Git安装后安装目录改变
  9. crx文件里面的html文件,javascript – Chrome扩展程序:在crx文件中打开html,标签上没有图标...
  10. RHEL6入门系列之三十一,管理计划任务
  11. 转载 《TypeScript 类型定义 DefinitelyTyped》
  12. android 人脸识别边框_Android camera实时预览 实时处理,人脸识别示例
  13. 基于JSP实现学校社团管理系统
  14. 2022年5月网络教育大学英语B统考题库复习题及考试时间
  15. CSS实现文字动画炫酷效果
  16. 46岁新晋院士:我上研究生时,卸载了所有游戏
  17. 修改树莓派的CoD(即蓝牙识别类型)
  18. 黑群晖安装Transmission和emby搭建家庭影音娱乐中心服务器(上)
  19. 小陈java学习笔记IO
  20. 川崎机器人here指令_川崎机器人常用编程大法解析——川崎机器人

热门文章

  1. AcWing-822. 走方格(排列组合)
  2. png格式转eps格式[易操作]
  3. 2023 影视视频微信小程序源码 带支付和采集
  4. 模糊神经网络matlab定义,模糊神经网络matlab仿真
  5. 三星Galaxy S5:更强更准的手势操作
  6. 1、nacos功能简介
  7. 游戏设计艺术 第2版 读书笔记(完)
  8. 高温焊接对晶振的影响
  9. oracle11内存建议,环境:oracle 11.2.0.1 +aix6内存问题
  10. Vc2013实战(2) 彻底厘清C运行库的涵义