LINUX下用FFMPEG解码264

OK, 由于项目需要, 需要在嵌入式平台上将264的视频流解码出来, 所以用到了FFMPEG。


一、平台搭建

首先,需要搭建好平台,  我用的是2.4版本的FFMPEG。 从官网下载下来的FFMPEG, 解压出来, 进到目录后直接可以运行 # ./configure     一般会提示 “yasm/nasm not found or too old. Use --disable-yasm for a crippled build.”, 系统并没有预先装yasm, 所以提示出错。 此时只需要照着提示加个选项 #  ./configure --disable-yasm。 后面一般都不会有其它问题,根据典型的安装,先编译 #make   后安装 # sudo make install


二、编写代码

前面平台如果搭建都没什么问题的话,下面可以进行编码, 其实说是说编码,也只是参考别人的来改而已, 主要是百度以及参照官网提供的例子程序来进行修改的。 在此,遇到问题多看官网上的说明, 尤其是帮助文档 点击打开链接。 并且需要找到对应的版本帮助文档才有用, 网上很多代码都是老版本的, 有不少方法都更新了,不再是从前那个接口, 所以这点对新手来说要多注意。 

 OK,闲话不多说,下面贴出代码, 因为代码量也不大,没有对其进行封装处理,将就着用一下吧。

#include "stdio.h"
#include "stdlib.h"


#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/fifo.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/avstring.h"
#include "libavutil/imgutils.h"
#include "libavutil/timestamp.h"
#include "libavutil/bprint.h"
#include "libavutil/time.h"
#include "libavutil/threadmessage.h"


#include "libavfilter/avcodec.h"
#include "libavcodec/avcodec.h"




#if HAVE_SYS_RESOURCE_H
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
#elif HAVE_GETPROCESSTIMES
#include <windows.h>
#endif
#if HAVE_GETPROCESSMEMORYINFO
#include <windows.h>
#include <psapi.h>
#endif

#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#if HAVE_TERMIOS_H
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <termios.h>
#elif HAVE_KBHIT
#include <conio.h>
#endif

#if HAVE_PTHREADS
#include <pthread.h>
#endif

#include <time.h>

#include "libavutil/avassert.h"

#define MAX_LEN  1024 * 50


此方法参考官网的例子
static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize,
                     FILE *f)
{
  //  FILE *f;
    int i;
   // f = fopen(filename,"w");
   // fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);
    for (i = 0; i < ysize; i++)
        fwrite(buf + i * wrap, 1, xsize, f);
  //  fclose(f);
}


int main()
{
	//下面初始化h264解码库
	//avcodec_init();
	av_register_all();

	AVFrame *pFrame_ = NULL;

	/* find the video encoder */
	AVCodec *videoCodec = avcodec_find_decoder(CODEC_ID_H264);//得到264的解码器类
	if(!videoCodec)
	{
		printf("avcodec_find_decoder error\n");
		return -1;
	}

	AVCodecParserContext *avParserContext = av_parser_init(CODEC_ID_H264);//得到解析帧类,主要用于后面的帧头查找
	if(!avParserContext)
	{
		printf("av_parser_init  error\n");
		return -1;
	}
	AVCodecContext *codec_ = avcodec_alloc_context3(videoCodec);//解码会话层
	if(!codec_)
	{
		printf("avcodec_alloc_context3  error\n");
		return -1;
	}


	//初始化参数,下面的参数应该由具体的业务决定
	codec_->time_base.num = 1;
	codec_->frame_number = 1; //每包一个视频帧
	codec_->codec_type = AVMEDIA_TYPE_VIDEO;
	codec_->bit_rate = 0;
	codec_->time_base.den = 25;//帧率
	codec_->width = 352;//视频宽
	codec_->height = 288;//视频高

	if(avcodec_open2(codec_, videoCodec, NULL) >= 0)//打开解码器
	{
		pFrame_ = avcodec_alloc_frame();// Allocate video frame    成功打开解码器后, 此时可以分配帧内存, 当然你也可以在后面每次都分配、释放, 在此我省功夫, 只在开始分配一次
		if (!pFrame_) {
			fprintf(stderr, "Could not allocate video frame\n");
			exit(1);
		}
	}
	else
	{
		printf("avcodec_open2 error\n");
		return -1;
	}

	AVPacket packet = {0};
	int dwBufsize = 10;
	int frameFinished = dwBufsize;//这个是随便填入数字,没什么作用

	av_init_packet(&packet);
	packet.data = NULL;//这里填入一个指向完整H264数据帧的指针
	packet.size = 0;//这个填入H264数据帧的大小

	FILE *myH264 = fopen("test3.264", "rb");//解码的文件264
	if(myH264 == NULL)
	{
		perror("cant open 264 file\n");
		return -1;
	}

	FILE *yuvfile = fopen("my264.yuv", "wb");//成功解码后保存成的YUV文件, 可以用YUV工具打开浏览
	if(yuvfile == NULL)
	{
		perror("cant open YUV file\n");
		return -1;
	}

	int readFileLen = 1;
	char readBuf[MAX_LEN];
	unsigned char *parseBuf = malloc(20*MAX_LEN);//这个地方浪费了我一个下午时间, 当时我用的是栈内存,即unsigned char parseBuf[20*MAX_LEN], 结果运行程序一直报错, 此处需要用堆内存才能正常解码
	int  parseBufLen = 0;

	int frameCount = 0;
	printf("begin...\n");
	printf("readBuf address  is %x\n", readBuf);
	while(readFileLen > 0)//开始解码工作
	{
		//printf("begin...\n");
		readFileLen = fread(readBuf, 1, sizeof(readBuf), myH264);//首先从文件里读出数据
		if(readFileLen <= 0)
		{
			printf("read over\n");
			break;
		}
		else
		{
			int handleLen = 0;
			int handleFileLen = readFileLen;
			while(handleFileLen > 0)
			{
				int nLength = av_parser_parse2(avParserContext, codec_, &parseBuf, &parseBufLen, readBuf + handleLen, handleFileLen, 0, 0, 0);//查找264帧头
				
				handleFileLen -= nLength;
				handleLen += nLength;

				if(parseBufLen <= 0)//当parseBufLen大于0时,说明查找到了帧头
				{
					continue;
				}
				packet.size = parseBufLen;//将查找到的帧长度送入
				packet.data = parseBuf;//将查找到的帧内存送入

				//printf("parseBuf address is %x\n", parseBuf);
				while(packet.size > 0)
				{//下面开始真正的解码
					int decodeLen = avcodec_decode_video2(codec_, pFrame_, &frameFinished, &packet);
					if(decodeLen < 0)
						break;
					packet.size -= decodeLen;
					packet.data += decodeLen;
					if(frameFinished > 0)//成功解码
					{
						int picSize = codec_->height * codec_->width;
						//int newSize = picSize * 1.5;

						//申请内存
						//unsigned char *buf = malloc(newSize);

						int height = pFrame_->height;
						int width = pFrame_->width;

						//printf("OK, get data\n");
						//printf("Frame height is %d\n", height);
						//printf("Frame width is %d\n", width);
						frameCount ++;
						printf("Frame count is %d\n", frameCount);

						pgm_save(pFrame_->data[0], pFrame_->linesize[0],//保存Y
                 codec_->width, codec_->height, yuvfile);
						pgm_save(pFrame_->data[1], pFrame_->linesize[1],//保存U
                 codec_->width/2, codec_->height/2, yuvfile);
						pgm_save(pFrame_->data[2], pFrame_->linesize[2],//保存V
                 codec_->width/2, codec_->height/2, yuvfile);
                        
                        ///有了YUV数据, 后面可以用FFMPEG提供的转换方法,将其转成RGB数据,进行后续的显示或其它的图像处理工作


					}
					else
						printf("failed to decodec\n");
				}
			}
		}
	}
    //释放工作
	avcodec_close(codec_);
	av_free(codec_);
	av_free_packet(&packet);
	av_frame_free(&pFrame_);

	fclose(yuvfile);
	fclose(myH264);
	
}
值得一提的是,代码在开始时遇到一个奇怪的问题, 编译后运行到解码方法avcodec_decode_video2时总提示错误:[h264 @ 0x1a791e0] no frame!     后来发现是我将一个内存弄成栈而不是堆内存(在代码注释中有说明),而FFMPEG却不对其解码, 这一点让我很奇怪, 一定要堆内存么? 


三、编译

编译说起来其实是一件很容易的事,不过这也弄了快一天的时间, 主要是我第一次用这个FFMPEG的库, 没想到它的方法依赖性那么强, 就是库与库之前的依赖性比较强, 在编译过程对引用库的顺序要特别注意, 不然会报很多方法没有找到的错。  只要顺序搞对, 其它遇到一些方法没找到时主要是没引用上相应的库,那些都好办。顺序一般为 -lavformat  -lavcodec -lswresample -lavuti 


总结一下, 解码264原来以为是一件挺难搞的事, 整了两三天发现其实也挺容易的, 关键得多看帮助文档。  写到这里, 后面提供我的工程,在工程里带有用于测试用的264文件。  工程链接:点击打开链接           YUV查看工具: 点击打开链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值