* 学习自雷神的博客和视频,图片来自他的课件,雷神博客:*
http://blog.csdn.net/leixiaohua1020/
日期:2016.10.1
作者:isshe
github:github.com/isshe
邮箱:[email protected]
前面四个是最常用的
* avcodec_decode_open2()这个函数是解码函数, 最主要的一个函数。
* 图中解码流程是:获取一个pakcet, 然后调用解码函数,把AVPacket结构中的data转换为AVFrame结构的data。
* AVPacket结构存储一帧压缩的编码数据。
* AVFrame结构存储一帧解码后的像素数据(对音频则是采样数据)
* AVFrame结构的元素data是双重指针,YUV数据来说包含data[0],data[1],data[2]分别存Y、U、V数据,注意每帧中U、V数据是Y数据的四分之一大小(对420P来说)。『Y:亮度数据, U,V:色差数据,由于人的眼睛对亮度更敏感,故而YUV数据中存更多的Y而减少UV的数据。当只有Y数据的时候,显示为黑白』
* 解码出来的数据可能函数无效像素。需要用sws_scale()函数处理(大概也可自编写函数处理)
* 如图:
*
* ffmpeg的函数简介:待更新(不定期更新)
* AVFormatContext是一个统筹全局的结构, 包含一些视频文件名,视频时长,视频码率等封装格式信息。
* AVInputFormat包含一些具体的视频格式信息,每种视频格式对应一个这个结构。
* 一般来说视频文件有两个流:视频流和音频流。有几个流就有几个AVStream数据结构, 一般视频流的index==0(也有其他情况), AVStream在AVFormatContext中是一个双重指针。
* AVCodecContext包含像素等编解码信息(对于视频)。
* AVCodec每种视/音频对应一个该结构体(例如h264).
* ffmpeg相关数据结构:待更新(不定期更新)
#include
//#include
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include
#include
#include //裁剪
}
//输入和输出的文件名,解码前的数据放H264文件中,解码后放YUV文件中
#define FILENAME "cuc_ieschool.flv"
#define FILENAME_YUV "cuc_ieschool_512x288.yuv"
#define FILENAME_H264 "cuc_ieschool_512x288.H264"
#define FILENAME_INFO "cuc_ieschool.info"
void print_AVFormatContext_info(AVFormatContext *pFormatCtx, FILE *file_stream);
int main(int argc, char *argv[])
{
AVFormatContext *pFormatCtx; //统筹结构,保存封装格式相关信息
AVCodecContext *pCodecCtx; //保存视/音频编解码相关信息
AVCodec *pCodec; //每种编解码器对应一个结构体
AVFrame *pFrame; //解码后的结构
AVFrame *pFrameYUV;
AVPacket *pPacket; //解码前
struct SwsContext *img_convert_ctx; //头文件只有一行,但是实际上这个结构体十分复杂
int i = 0;
int video_index = 0; //为了检查哪个流是视频流,保存流数组下标
int y_size = 0; //
int ret = 0; //
int got_picture = 0; //
char filename[256] = FILENAME; //
int frame_cnt; //帧数计算
uint8_t *out_buffer; //???
FILE *fp = NULL;
FILE *YUVfp = NULL;
if (argc == 2)
{
memcpy(filename, argv[1], strlen(argv[1])+1);
}
else if (argc > 2)
{
printf("Usage: ./*.out video_filename");
}
av_register_all(); //注册所有组件
avformat_network_init(); //初始化网络,貌似暂时没用到,可以试试删除
pFormatCtx = avformat_alloc_context(); //分配内存
//打开文件, 注意第一个参数是指针的指针
if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0)
{
printf("Couldn't open input stream.\n");
return (-1);
}
//获取流信息
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
{
printf("Couldn't find stream info\n");
return (-1);
}
//找出视频流, nb_streams表示视频中有几种流
video_index = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
video_index = i;
break;
}
}
if (-1 == video_index)
{
printf("Couldn't find a video stream\n");
return (-1);
}
//复制编解码的信息到编解码的结构AVCodecContext结构中,
//一方面为了操作结构中数据方便(不需要每次都从AVFormatContext结构开始一个一个指向)
//另一方面方便函数的调用
pCodecCtx = pFormatCtx->streams[video_index]->codec; //可以查看源码avformat.h中定义的结构
//找到一个和视频流对应的解码器
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (NULL == pCodec)
{
printf("Couldn't found a decoder\n");
return (-1);
}
//打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
printf("Couldn't open decoder\n");
return (-1);
}
//输出一些想要输出的信息
fp = stdout;
fp = fopen(FILENAME_INFO, "w+");
if (fp == NULL)
{
fprintf(stderr, "fopen error\n");
return (-1);
}
//输出结构中的信息以更了解这些结构。这里代码只示例一个。
print_AVFormatContext_info(pFormatCtx, fp);
//输出视频文件信息
printf("------------------------file infomation-----------------------------\n");
av_dump_format(pFormatCtx, 0, filename, 0);
printf("--------------------------------------------------------------------\n");
pFrame = av_frame_alloc();
pFrameYUV = av_frame_alloc();
out_buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
avpicture_fill((AVPicture *)pFrameYUV, out_buffer,
AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
//???
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
pPacket = (AVPacket *)av_malloc(sizeof(AVPacket));
frame_cnt = 0; //帧数计算吗?
//打开文件
fp = fopen(FILENAME_H264, "w+");
if (fp == NULL)
{
fprintf(stderr, "h264 fopen error\n");
return (-1);
}
YUVfp = fopen(FILENAME_YUV, "w+");
if (NULL == YUVfp)
{
fprintf(stderr, "YUV fopen error\n");
return (-1);
}
while(av_read_frame(pFormatCtx, pPacket) >= 0)
{
if (pPacket->stream_index == video_index)
{
//这里可以输出H264马流信息,这里是解码前
fwrite(pPacket->data, pPacket->size, 1, fp);
//解码
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, pPacket);
if (ret < 0)
{
printf("Decode fail\n");
return (-1);
}
if (got_picture == 0)
{
printf("get picture error\n");
}
else
{
//调整解码出来的图像,解码出来的可能含有填充的信息。
sws_scale(img_convert_ctx, (const uint8_t * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize);
// printf("Decoded frame index: %d\n", frame_cnt);
// 已经解码,可以输出YUV的信息
// 这个data是一个数组,需要注意!一般3个:代表Y、U、V
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
fwrite(pFrameYUV->data[0], 1, pCodecCtx->width * pCodecCtx->height, YUVfp);
fwrite(pFrameYUV->data[1], 1, pCodecCtx->width * pCodecCtx->height / 4, YUVfp);
fwrite(pFrameYUV->data[2], 1, pCodecCtx->width * pCodecCtx->height / 4, YUVfp);
frame_cnt++;
}
}
av_free_packet(pPacket);
}
printf("Decoded frame count: %d\n", frame_cnt);
fclose(fp);
fclose(YUVfp);
sws_freeContext(img_convert_ctx);
av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
void print_AVFormatContext_info(AVFormatContext *pFormatCtx, FILE *file_stream)
{
fprintf(file_stream, "-------------------------AVFormatContext--------------------------\n");
fprintf(file_stream, "ctx_flags = %d\n"
"nb_streams = %u\n"
"filename = %s\n"
"start_time = %d\n"
"duration = %d\n"
"bit_rate = %d\n"
"packet_size = %u\n"
"max_delay = %d\n"
"flags = %d\n"
"probesize = %d\n"
"key = %d\n"
"keylen = %d\n"
"nb_programs = %d\n",
pFormatCtx->ctx_flags, pFormatCtx->nb_streams,
pFormatCtx->filename, pFormatCtx->start_time,
pFormatCtx->duration, pFormatCtx->bit_rate,
pFormatCtx->packet_size, pFormatCtx->max_delay,
pFormatCtx->flags, pFormatCtx->probesize,
pFormatCtx->key, pFormatCtx->keylen, pFormatCtx->nb_programs);
fprintf(file_stream, "------------------------------------------------------------------\n");
}
g++ ffmpeg_decoder.c -o ffmpeg_decoder.out -O2 -Wall -g \
-lavformat -lavcodec -lavformat -lavutil -lswresample -lswscale \
-lx264 -lx265 -lvpx -lmp3lame -lopus -lfdk-aac -lX11 -lva -lvdpau -lva-drm \
-lva-x11 -lvorbisenc -lvorbis -ltheoraenc -ltheoradec -ldl -lm -lpthread -lz
相关结构的信息存到.info文件,裸流数据存到h264文件,解码后数据存yuv文件。
http://download.csdn.net/detail/i_scream_/9644386