Tiny4412移植ffmpeg实现视频解码

描述

Tiny4412移植ffmpeg实现视频解码

  FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。

1.硬件平台

硬件平台:Tiny4412(Cortex-A9)
交叉编译器:arm-linux-gcc(Ver4.5.1)
开发板系统:linux3.5
ffmpeg版本:ffmpeg-4.2.5

2.ffmpeg源码编译

  安装ffmpeg库之前需要先安装x264库。

 2.1 x264编译安装

  x264下载地址:https://www.videolan.org/developers/x264.html

视频解码

 安装x264

#解压
tar xvf /mnt/hgfs/ubuntu/software_pack/x264-master.tar.bz2
#配置信息,生成Makefile
./configure --prefix=$PWD/_install --enable-shared --disable-asm --host=arm-linux
#修改config.mak 
gedit config.mak 
视频解码

  编译 make

视频解码

若编译出现报错:undefined reference to `clock_gettime’,则添加动态链接: -lrt
  重新编译 make

/*编译*/
make && make install
/*生成文件*/
[wbyq@wbyq x264-master]$ tree _install/
_install/
├── bin
│   └── x264
├── include
│   ├── x264_config.h
│   └── x264.h
└── lib
    ├── libx264.so -> libx264.so.164
    ├── libx264.so.164
    └── pkgconfig
        └── x264.pc

4 directories, 6 files

 2.2 ffmpeg编译安装

  下载地址:http://www.ffmpeg.org/download.html

视频解码

  编译安装ffmpeg

/*解压*/
tar xvf /mnt/hgfs/ubuntu/software_pack/ffmpeg-4.2.5.tar.bz2 
/*配置信息,生成makefile文件*/
./configure --enable-shared --enable-static --prefix=$PWD/_install --cross-prefix=arm-linux- --arch=arm --target-os=linux --enable-gpl --extra-cflags=-I/home/wbyq/tiny4412_pack/x264-master/_install/include --extra-ldflags=-L/home/wbyq/tiny4412_pack/x264-master/_install/lib --enable-ffmpeg --enable-libx264
/*编译源码*/
make && make install

3.视频解码

#include 
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#define FILE_NAME "1.mp4"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
extern const unsigned char ascii_32_16[][32*16/8];
static  struct fb_var_screeninfo vinfo;/*lcd可变形参结构体*/
static struct fb_fix_screeninfo finfo;/*lcd固定参数结构体*/
static unsigned char *lcd_p;/*lcd缓冲区首地址*/

u32 lcd_widht;/*LCD屏宽度*/
u32 lcd_hight;/*LCD屏高度*/

static unsigned char *lcd_p2;/*图像数据*/
u32 map_w;
u32 map_h;
/*画点函数*/
void LCD_DrawPoint(int x,int y,int c)
{
	unsigned int *p=(unsigned int *)(y*finfo.line_length+x*vinfo.bits_per_pixel/8+lcd_p);
	*p=c;
}
/*画点函数*/
void LCD_DrawPoint2(int x,int y,int c)
{
	unsigned int *p=(unsigned int *)(y*map_w*3+x*3+lcd_p2);
	*p=c;
}
/*显示数据*/
void LCD_DisplayData(int x,int y,int w,int h,const unsigned char *data)
{
	int i,j;
	int x0=x;
	unsigned char temp;
	for(i=0;i0x80)
    {
		str+=2;
    }
    else
    {
      LCD_DisplayData(x,y,size/2,size,ascii_32_16[*str-' ']);
      str++;
      x+=size/2;
    }
  }
}
/*显示图片函数(居中显示)*/
void LCD_Image(int map_w,int map_h,u8 *map_rgb)
{
    u32 w=map_w;//图片宽度
    u32 h=map_h;//图片高度
    //printf("w=%d,h=%dn",w,h);
   // u16 x=(lcd_widht-w)/2;
  //  u16 y=(lcd_hight-h)/2;
	u16 x=0;
	u16 y=0;
    int i,j;
    int cnt=0;
    u32 rgb=0xff0000;
    u8 *image_rgb=map_rgb;
    u32 *p=(unsigned int *)(y*finfo.line_length+x*vinfo.bits_per_pixel/8+lcd_p);//获取到坐标地址
    for(i=0;istreams[videostream];
	AVCodec *vcodec=avcodec_find_decoder(stream->codecpar->codec_id);
	if(!vcodec)
	{
		printf("未找到解码器n");
		return -1;
	}
	/*申请视频AVCodecContext空间。需要传递一个编码器,也可以不传,但不会包含编码器。*/
	res=avcodec_open2(stream->codec,vcodec,NULL);
	if(res)
	{
		printf("打开解码器失败n");
		return -1;
	}
	
	/*寻找音频解码器*/
	AVStream *audstream = ps->streams[audiostream];
	AVCodec *audcodec=avcodec_find_decoder(audstream->codecpar->codec_id);
	if(!audcodec)
	{
		printf("audcodec failedn");
		return -1;
	}
	/*申请音频AVCodecContext空间。需要传递一个编码器,也可以不传,但不会包含编码器。*/
	res=avcodec_open2(audstream->codec,audcodec,NULL);
	if(res)
	{
		printf("open audio failedn");
		return -1;
	}
	printf("sucessn");
	int sample_rate=audstream->codec->sample_rate;/*采样率*/
	int channel=audstream->codec->channels;/*通道数*/
	
	printf("sample_rate:%dn",sample_rate);
	printf("channel:%dn",channel);
	int sample_fmt;
	switch(audstream->codec->sample_fmt)
	{
		case AV_SAMPLE_FMT_U8:/*8位*/
			sample_fmt=AV_SAMPLE_FMT_U8;
			break;
		case AV_SAMPLE_FMT_FLTP:/*浮点型*/
			sample_fmt=AV_SAMPLE_FMT_FLTP;
			break;
	}
	int go_audio;
	AVPacket *packet=av_malloc(sizeof(AVPacket));/*分配包*/
	AVFrame *frame=av_frame_alloc();/*分配视频帧*/
	AVFrame *audioframe=av_frame_alloc();/*分配音频帧*/
	int audiosize=2*1024*2;
	char *out_buffer = (char*)av_malloc(audiosize);
	/*对解码数据进行重采样*/
	SwrContext *swrCtx = swr_alloc();
	enum AVSampleFormat in_sample_fmt=audstream->codec->sample_fmt;/*输入采样格式*/
	enum AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16;/*输出采样格式:16bit PCM*/
	int in_sample_rate=audstream->codec->sample_rate;/*输入采样率*/
	int out_sample_rate=44100;/*输出采样率*/
	
	uint64_t in_ch_layout=audstream->codecpar->channel_layout;//输入的声道布局
	uint64_t out_ch_layout=audstream->codecpar->channel_layout;/*立体声*/
	
	swr_alloc_set_opts(swrCtx,out_ch_layout,out_sample_fmt,out_sample_rate,/*输入音频格式*/
								in_ch_layout,in_sample_fmt,in_sample_rate,/*输出音频格式*/
								0,NULL);
	
	swr_init(swrCtx);
	//视频解码
	AVFrame *frameRGB=av_frame_alloc();/*申请yuv空间*/
	/*分配空间,进行图像转换*/
	int width=ps->streams[videostream]->codecpar->width;
	int height=ps->streams[videostream]->codecpar->height;
	int fmt=ps->streams[videostream]->codecpar->format;/*流格式*/
	map_w=800;
	map_h=480;
	int size=avpicture_get_size(AV_PIX_FMT_BGR24, map_w,map_h);
	unsigned char *buff=NULL;
	printf("w=%d,h=%d,size=%dn",width,height,size);
	buff=av_malloc(size);
	/*计算一帧空间大小*/
	avpicture_fill((AVPicture *)frameRGB,buff,AV_PIX_FMT_BGR24,map_w,map_h);
	/*转换上下文*/
	struct SwsContext *swsctx=sws_getContext(width,height, fmt,map_w,map_h, AV_PIX_FMT_BGR24,SWS_BICUBIC,NULL,NULL,NULL);

	/*读帧*/
	int go=0;
	int Framecount=0;
	printf("read fream buffn");
	int audio_count=0;
	time_t sec=0,sec2=0;
	char buff_time[200]={0};
	struct tm result;
	while((av_read_frame(ps,packet)>=0))
	{
		sec=time(NULL);
		if(sec!=sec2)
		{
			sec2=sec;
			localtime_r(&sec2,&result);//将秒单位时间转换为时间结构体
			strftime(buff_time,sizeof(buff_time),"%H:%M:%S",&result);//时间格式化打印	
		}

		if(packet->stream_index == AVMEDIA_TYPE_VIDEO)/*判断是否为视频*/
		{
			res=avcodec_decode_video2(ps->streams[videostream]->codec,frame,&go,packet);
			if(res<0)
			{
				printf("avcodec_decode_video2 failedn");
				return -1;
			}
			if(go)
			{
				sws_scale(swsctx,(const uint8_t **)frame->data,frame->linesize,0,map_h,(const uint8_t **)frameRGB->data,frameRGB->linesize);
				lcd_p2=buff;
				NT35310_DisplayStr(lcd_widht/2-strlen(buff_time)/2*16,50,32,buff_time);
				LCD_Image(map_w,map_h,buff);
				Framecount++;
				//printf("frame index:%dn",Framecount);
			}
		}
		else if(packet->stream_index==AVMEDIA_TYPE_AUDIO)/*音频流*/
		{
			res=avcodec_decode_audio4(audstream->codec,audioframe,&go_audio,packet);
			if(res<0)
			{
				printf("decode_audio4 failedn");
				return -1;
			}
			if(go_audio)//音频数据处理
			{
			}
		}
	}
	av_free_packet(packet);
	sws_freeContext(swsctx);
	av_frame_free(&frame);
	av_frame_free(&frameRGB);
	avformat_free_context(ps);
	return 0;
}

*3;j+=3)>*w>

  makefile文件

CFLAGS=-I/home/wbyq/tiny4412_pack/ffmpeg-4.2.5/_install/include -L/home/wbyq/tiny4412_pack/ffmpeg-4.2.5/_install/lib 
CFLAGS+=-I/home/wbyq/tiny4412_pack/x264-master/_install/include -L/home/wbyq/tiny4412_pack/x264-master/_install/lib
CFLAGS+= -lpthread -lm -ldl -lavcodec -lavfilter -lavutil -lswresample -lavdevice -lavformat -lpostproc -lswscale -lpthread -lstdc++ -lm -lasound -lx264
OBJ=main.c ascii.c
app:
	arm-linux-gcc -o app $(OBJ) $(CFLAGS)   

4.相关函数介绍

4.1 打开输入流并读取头数据avformat_open_input

int avformat_open_input(AVFormatContext **ps, const char *filename, ff_const59 AVInputFormat *fmt, AVDictionary **options);
函数功能:打开一个输入流并读取头部信息,但编解码器不会打开
   1.分配一个AVFormatContext的实例。
   2.调用init_input函数初始化输入流的信息。这里会初始化AVInputFormat。
   3.根据上一步初始化好的AVInputFormat的类型,调用它的read_header方法,读取文件头。
形 参: AVFormatContext **ps 媒体文件或媒体流的构成和基本信息
   filename 输入文件名
   AVInputFormat *fmt 输入文件格式,一般填NULL即可。
     如果fmt参数非空,也就是人为的指定了一个AVInputFormat的实例,那么这个函数就不会再检测输入文件的格式了;如果fmt为空,那么这个函数就会自动探测输入文件的格式等信息。
     AVDictionary **options 附加的一些选项,一般填NULL即可;
返回值: 成功返回0

4.2 查找流信息avformat_find_stream_info

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
函数功能:查找流信息
形参: AVFormatContext *ic 输入的流信息
   AVDictionary **options 附加的一些选项,一般填NULL即可;
返回值: 成功返回0

4.3 查找输入流中的音视频信息av_find_best_stream

int av_find_best_stream(AVFormatContext *ic,
           enum AVMediaType type,
           int wanted_stream_nb,
            int related_stream,
            AVCodec **decoder_ret,
           int flags);
形参: AVFormatContext *ic 输入的流信息
    type 查找类型AVMEDIA_TYPE_VIDEO视频、AVMEDIA_TYPE_AUDIO音频
     wanted_stream_nb 用户请求的流编号,-1用于自动选择,
    related_stream 尝试查找与此相关的流(例如,在同一程序中),如果没有,则为-1
    decoder_ret 如果非空,则返回所选流的解码器,可以填NULL,
     flags 标志,当前未定义任何
返回值: 成功情况下的非负流编号

4.4 初始化音视频编解码器avcodec_open2

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
函数功能:该函数用于 初始化 一个视音频编解码器的AVCodecContext。
形参:avctx 编解码器上下文
   codec 要打开的编解码器
   options 一个包含AVcodeContext和编解码器专用选项的存储工具。
返回值: 成功返回0,失败返回负数

4.5 分配一个重采样swr_alloc_set_opts

struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
           int64_t out_ch_layout,
           enum AVSampleFormat out_sample_fmt,
           int out_sample_rate,
           int64_t in_ch_layout,
           enum AVSampleFormat in_sample_fmt,
           int in_sample_rate,
           int log_offset,
           void *log_ctx);
函数功能:分配一个重采样,设置/重置公共参数
形参: s 现有的Swr上下文(如果可用),或NULL(如果不可用)
    out_ch_layout 输出声道格式
    out_sample_fmt 输出采样频率
    in_ch_layout 输入的声道布局
    in_sample_fmt 输入采样格式
    log_offse、log_ctx 日志信息,填0和NULL即可
返回值: 错误时为NULL,否则为已分配上下文

 

 
 
审核编辑:汤梓红
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分