ffmpeg解码yuv. libxxx.so.58及以上库版本3.X版本

文章目录

        • 环境

环境

编译环境
在这里插入图片描述
makefile文件

 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函数开始看并一个一个模块分析

你可能感兴趣的:(ffmpeg)