FFmpeg-IJKPlayer

FFmpeg VS项目 2

2018-07-07  本文已影响0人  棍子哥丸子妹

按照规划,这次是一个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 !!!!!

上一篇下一篇

猜你喜欢

热点阅读