FFmpeg VS项目 2
按照规划,这次是一个ffmpeg编码的例子,下面介绍下Demo流程
1.把一个yuv文件编码成h264文件
2.编码的含义:yuv文件仅仅是存储像素数据的文件,所谓编码需要我们自己给编码器填充参数,按照我的设置的参数编码成码流文件
3.编码流程如下:其中有些Api,有替换,:这里直接把雷神的截图拉过来了,建议大家可以多看看雷神的博客。
image介绍下各个函数的含义:
av_register_all():注册FFmpeg所有编解码器。
avformat_alloc_output_context2():初始化输出码流的AVFormatContext。
avio_open():打开输出文件。
av_new_stream():创建输出码流的AVStream。
avcodec_find_encoder():查找编码器。
avcodec_open2():打开编码器。
avformat_write_header():写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。
avcodec_encode_video2():编码一帧视频。即将AVFrame(存储YUV像素数据)编码为AVPacket(存储H.264等格式的码流数据)。
上面的这个函数也有更新:请参考我上篇日记 http://www.jianshu.com/p/8c49c4fe95a7
av_write_frame():将编码后的视频码流写入文件。
flush_encoder():输入的像素数据读取完成后调用此函数。用于输出编码器中剩余的AVPacket。(这里我demo里面的这个函数没写好,没起作用,导致最后几帧数据没写入文件,明天更新代码)
av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。
到目前为止:应搞清楚编码的含义和流程,剩下的是代码参数调参了,也就那几个参数,本人也踩了好多坑!! demo地址 在
下面是代码范例:
// ConsoleApplication4.cpp: 定义控制台应用程序的入口点。
#include "stdafx.h"
extern "C"
{
#include "libavcodec\avcodec.h"
#include "libavformat\avformat.h"
#include "libavutil\avutil.h"
#include "libswresample\swresample.h"
#include "libswscale\swscale.h"
#include "libavutil\imgutils.h"
}
void flush_encoder(AVFormatContext * fmt_ctx, AVCodecContext * c_ctx, unsigned int stream_index)
{
int framecnt = 0;
int ret;
AVPacket pkt;
while (1)
{
pkt.data = NULL;
pkt.size = 0;
av_init_packet(&pkt);
ret = avcodec_send_frame(c_ctx, NULL);
ret = avcodec_receive_packet(c_ctx, &pkt);
if (ret != 0)
{
break;
}
framecnt++;
ret = av_write_frame(fmt_ctx, &pkt);
printf("Succees to encode frame: %5d\tsize:%5d\n", framecnt, pkt.size);
if (ret < 0)
break;
}
}
int main()
{
//printf("ffmpeg encode demo %s: ",avcodec_configuration());
AVFormatContext * pFormatCtx;
AVOutputFormat *fmt;// 和AVInputFormat 对应 在编码的时候 使用
AVStream * video_st;// 编码对应的 码流结构体
AVCodecContext * pCodecCtx;//编码器对应的 信息结构体
const AVCodec * pCodec;
AVPacket pkt;//用来存储解码后的每一帧数据 最后可以对这个数据进行操作 干哈都行
uint8_t * picture_buf;//图片的真实大小 这里默认从 480*360 转换 (可以查看测试的视频宽高)
AVFrame *pFrame;//需要从yuv文件中拿到数据 最后构造一个AVFrame 编码前的一帧数据
int picture_size;//宽高
int y_size;
int framecnt = 0;
FILE * inputfp;//引入测试的yuv格式的文件
fopen_s(&inputfp, "testyuv.yuv", "rb");
int in_w = 480, in_h = 360;//这里定义下测试的宽高
int framenum = 4456;//有可以播放yuv格式的播放器 可以看下 总的帧数
const char * out_file = "testh264.mp4";
//1 注册编码器 等 相关
av_register_all();
//Method1.
pFormatCtx = avformat_alloc_context();
//Guess a Format
fmt = av_guess_format(NULL, out_file, NULL);
pFormatCtx->oformat = fmt;
//Method 2.
//avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);
//fmt = pFormatCtx->oformat;
//2 打开输出文件
if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE)<0)
{
printf("Failed to open output file! \n");
return -1;
}
//3 构造一个输出码流 AVStream
video_st = avformat_new_stream(pFormatCtx, 0);
if (video_st == NULL)
{
printf("AVStream create failed.\n");
return -1;
}
//4 构建参数
//Param that must set
pCodecCtx = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(pCodecCtx, video_st->codecpar);
pCodecCtx->codec_id = AV_CODEC_ID_H264;
pCodecCtx->codec = avcodec_find_encoder(pCodecCtx->codec_id);
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
pCodecCtx->width = in_w;
pCodecCtx->height = in_h;
pCodecCtx->bit_rate = 400000;
pCodecCtx->gop_size = 250;//
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
//H264
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 51;
//Optional param
pCodecCtx->max_b_frames = 3;
AVCodecParameters * p = video_st->codecpar;
p->codec_id = pCodecCtx->codec_id;
p->codec_type = pCodecCtx->codec_type;
p->width = pCodecCtx->width;
p->height = pCodecCtx->height;
p->format = pCodecCtx->pix_fmt;
p->bit_rate = pCodecCtx->bit_rate;
video_st->time_base = pCodecCtx->time_base;
AVDictionary *param = 0;
//H.264
if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {
av_dict_set(¶m, "preset", "slow", 0);
av_dict_set(¶m, "tune", "zerolatency", 0);
}
//5 打印我们构建的信息
av_dump_format(pFormatCtx, 0, out_file, 1);
//6 根据code_id 查找编码器
pCodec = pCodecCtx->codec;
if (pCodec == NULL)
{
printf("Didn't find EnCoder.\n");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0)
{
printf("Failed to open encoder ! \n");
return -1;
}
//7 创建一个AVframe来存储yuv数据
pFrame = av_frame_alloc();
picture_size = pCodecCtx->width * pCodecCtx->height * 3 / 2;
//picture_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);
picture_buf = (uint8_t *)av_malloc(picture_size);
av_image_fill_arrays(pFrame->data, pFrame->linesize, picture_buf,
AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);
//8 先写个头
avformat_write_header(pFormatCtx, NULL);
//9 创建一个AVPacket 来存储h264数据
av_new_packet(&pkt, picture_size);
//10 开始循环编码 yuv数据
y_size = pCodecCtx->width*pCodecCtx->height;
int index = 0;
for (int i = 0; i < framenum; i++)
{
//读取yuv data
if (fread(picture_buf, 1, y_size * 3 / 2, inputfp) <= 0)
{
printf("Failed to read raw yuv data ! \n");
return -1;
}
else if (feof(inputfp))
{
break;
}
//PTS
pFrame->pts = i * (video_st->time_base.den) / ((video_st->time_base.num) * 25);
pFrame->width = pCodecCtx->width;
pFrame->height = pCodecCtx->height;
pFrame->format = pCodecCtx->pix_fmt;
//Encdoe
int ret = avcodec_send_frame(pCodecCtx, pFrame);
if (ret != 0)
{
printf("avcodec_send_frame error! \n");
return -1;
}
ret = avcodec_receive_packet(pCodecCtx, &pkt);
if (ret == 0)
{
printf("Succees to encode frame: %5d\tsize:%5d\n", framecnt, pkt.size);
framecnt++;
pkt.stream_index = video_st->index;
ret = av_write_frame(pFormatCtx, &pkt);
av_packet_unref(&pkt);
}
}
//11 Flush Encoder for 剩余的packet
flush_encoder(pFormatCtx,pCodecCtx,video_st->index);
if (ret<0)
{
printf("Flush encoder failed \n");
return -1;
}*/
//12 写尾
av_write_trailer(pFormatCtx);
if (video_st) {
avcodec_close(pCodecCtx);
av_free(pFrame);
av_free(picture_buf);
}
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
fclose(inputfp);
return 0;
}
**代码我加了注释流程很清晰,末尾!!!!这里所有的编码器参数 都是我自己测试的,能编码成功!!! 注意:仅仅 是一个编码流程的demo ,一些参数要自己去调试,H264格式的编码器 设置,每一帧数据的AVFrame拼接,只需要改变Demo 里面对应的参数。。。 **
这里给出文件地址 : https://github.com/liuguangsen/ffmpegSoftDecoderDemo/blob/master/Encode_ffmpeg.cpp
目前项目只需要 软编和软解,Demo下周再写,这个星期接下来七天我会仔细理下自己的笔记。把一些ffmpeg知识点整理下!!
欢迎FFmpeg大神 戳我liugstick@163.com !!!!!