av:
c++ avcs.cpp -o $@ \
-I/vtdlib/avinc \
-Wl,-rpath,/vtdlib/avlib \
-L/vtdlib/avlib \
-lavdevice -lavfilter -lswscale -lavformat -lavcodec -lswresample -lavutil
clean:
rm -rf *.o
rm -rf av
rm -rf /zhouyl/CSvideo/video.yuv
#//-I指定头文件目录
#//-L指定编译时库文件路径
#//-Wl,-rpath,xx/xx/xx指定运行时库文件路径
#//-lxxx 是代码所需求的库
#//av 和 clean 是 makefile文件目标,具体可网上查阅资料
现在可以在工程目录下创建一个cpp文件,加上makefile文件,工程目录下就有两个文件
[root@localhost avcs]# ls
avcs.cpp Makefile
[root@localhost avcs]#
接下来就是写代码了打开avcs.cpp进行编辑
#include <stdio.h>
#include <iostream>
using namespace std;
#ifdef __cplusplus
extern "C"
{
#endif
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#ifdef __cplusplus
}
#endif
struct AVContext
{
AVFormatContext* format_ctx;
AVCodecContext* vcodec_ctx;
AVCodec* vcodec;
AVPacket* packet;
AVFrame* video_frame;
int ret;
int vindex;
int des_width;//目标视频的宽高
int des_height;
FILE *yuv_file;
}avc;
void open_avfile(const char* filename)
{
avc.format_ctx = avformat_alloc_context();//01
if (avc.format_ctx == NULL)
{
printf("avformat_alloc_context err\n");
exit(1);
}
if (avformat_open_input(&avc.format_ctx, filename, NULL, NULL) != 0)
{
printf("open err\n");
exit(1);
}
}
void find_stream_info()
{
avc.vindex = -1;
if (avformat_find_stream_info(avc.format_ctx, NULL) != 0)
{
printf("find stream err\n");
exit(1);
}
for (unsigned int i = 0; i < avc.format_ctx->nb_streams; ++i)
{
if (avc.format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
avc.vindex = i;
}
}
if (avc.vindex <= -1)
{
printf("find index err\n");
exit(1);
}
}
void open_codec_context()
{
//video
avc.vcodec = avcodec_find_decoder(avc.format_ctx->streams[avc.vindex]->codecpar->codec_id);
avc.vcodec_ctx = avcodec_alloc_context3(avc.vcodec); //02
avcodec_parameters_to_context(avc.vcodec_ctx, avc.format_ctx->streams[avc.vindex]->codecpar);
if (avcodec_open2(avc.vcodec_ctx, avc.vcodec, NULL) != 0)
{
printf("open video codec err\n");
exit(1);
}
avc.des_width = avc.vcodec_ctx->width;
avc.des_height = avc.vcodec_ctx->height;
}
int process_video_data()
{
//发送一个包
avc.ret = avcodec_send_packet(avc.vcodec_ctx, avc.packet);
if (avc.ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n");
return -1;
}
//循环将包中的帧读完
while (avc.ret >= 0)
{
avc.ret = avcodec_receive_frame(avc.vcodec_ctx, avc.video_frame);
if (avc.ret == AVERROR(EAGAIN) || avc.ret == AVERROR_EOF) {
break;
}
else if (avc.ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame from the decoder\n");
avc_free();
}
if (avc.ret >= 0)
{
printf("size[0]: %d, size[1]: %d, size[2]: %d\n",
avc.video_frame->linesize[0],
avc.video_frame->linesize[1],
avc.video_frame->linesize[2]);
printf("ww: %d , h: %d\n", avc.des_width, avc.des_height);
//存 yuv420
char* buf = (char*)malloc(avc.des_height * avc.des_width * 3 / 2);
memset(buf, 0, avc.des_height * avc.des_width * 3 / 2);
int height = avc.des_height;
int width = avc.des_width;
printf("decode video ok\n");
int a = 0, i;
for (i = 0; i < height; i++)
{
memcpy(buf + a, avc.video_frame->data[0] + i * avc.video_frame->linesize[0], width);
a += width;
}
for (i = 0; i < height / 2; i++)
{
memcpy(buf + a, avc.video_frame->data[1] + i * avc.video_frame->linesize[1], width / 2);
a += width / 2;
}
for (i = 0; i < height / 2; i++)
{
memcpy(buf + a, avc.video_frame->data[2] + i * avc.video_frame->linesize[2], width / 2);
a += width / 2;
}
fwrite(buf, 1, avc.des_height * avc.des_width * 3 / 2, avc.yuv_file);
free(buf);
buf = NULL;
av_frame_unref(avc.video_frame);//重要, 每次读完一帧刷新内存为下一帧读取准备一个干净的缓存空间
}
}
return 0;
}
void process_data()
{
avc.packet = av_packet_alloc(); //04
avc.video_frame = av_frame_alloc(); //06
avc.yuv_file = fopen("/zhouyl/CSvideo/video.yuv", "ab");
if (!avc.yuv_file)return;
while (1)
{
if ((avc.ret = av_read_frame(avc.format_ctx, avc.packet)) < 0)
{
fclose(avc.yuv_file);
break;
}
if (avc.packet->stream_index == avc.vindex)
{
if (process_video_data() != 0)
{
break;
}
}
}
}
int main()
{
//注册函数
av_register_all();
//为了,代码分类易读,我将其包装成 5 个函数模块放main函数中
//模块1: 打开文件或url , 创建输入 文件格式上下文
open_avfile("/vtdlib/input/zy-1.ts");
//模块2: 查找流id来确认是视频流,音频流,字幕流
find_stream_info();
//模块3: 查找解码器,创建解码器上下文,打开解码器
open_codec_context();
//模块4: 读取包,确认视频包音频包字幕包,然后循环读帧解码
process_data();
//模块5: 内存释放
avc_free();
return 0;
}
建议从main函数开始看并一个一个模块分析