您的位置:首页 > 其它

基于ffmpeg-1.1的视频解码并输出到LCD显示

2014-06-28 23:58 369 查看
http://www.2cto.com/kf/201301/184980.html

ffmpeg_decode.c:

#include <libavformat/avformat.h>

#include <libavcodec/avcodec.h>

#include <libavdevice/avdevice.h>

#include <libavutil/avutil.h>

#include <libswscale/swscale.h>

#include "myhead.h"

#include "lcd.h"

/* ffmpeg中的AVFormat库可以帮助进行这一“分拆音视频流”的过程;而AVCodec则帮助解码视频。 */

int main (int argc, char **argv)

{

if (argc < 2) {

my_debug("Usage:%s file_name\n", argv[0]);

exit(1);

}

char *fb_dev = "/dev/fb0";

struct lcd_info_t *lcdinfo = lcd_init(fb_dev);

if (NULL == lcdinfo)

err_exit("lcd initialize failure");

av_register_all();

// 调用它用以注册所有支持的文件格式以及编解码器

/* AVFormatContext保存需要读入的文件的格式信息,比如流的个数以及流数据等*/

AVFormatContext *pFormatCtx = NULL;

// 必须为NULL或者由avformat_alloc_context分配得到

if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)

err_exit("avformat_open_input");

/*

** avformat_open_input函数只是读文件头,并不会填充流信息,因此我们需要接下来调用

** avformat_find_stream_info获取文件中的流信息,此函数会读取packet,并确定文件中所有的流信息,

** pFormatCtx->streams指向文件中的流,但此函数并不会改变文件指针,读取的packet会给后面的

** 解码进行处理.最后调用一个帮助函数av_dump_format,输出文件的信息,也就是我们在使用ffmpeg时

** 能看到的文件详细信息.第二个参数指定输出哪条流的信息,-1表示给ffmpeg自己选择.最后一个参数

** 用于指定dump的是不是输出文件,我们dump的是输入文件,因此一定要是0.

*/

if(avformat_find_stream_info(pFormatCtx, NULL ) < 0 )

err_exit("avformat_find_stream_info");

av_dump_format(pFormatCtx, -1, argv[1], 0);

/* 现在 pFormatCtx->streams 中已经有所有流了,因此现在我们遍历它找到第一条视频流 */

int videoStream = -1, i;

for(i = 0; i < pFormatCtx->nb_streams; i++)

if( pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {

videoStream = i;

break;

}

if(videoStream == -1)

err_exit("not find video stream");

/* 接下来我们通过这条 video stream 的编解码信息打开相应的解码器 */

AVCodecContext *pCodecCtx;

// 保存了相应流的详细编码信息,比如视频的宽、高,编码类型等

AVCodec *pCodec;

// 真正的编解码器,其中有编解码需要调用的函数

pCodecCtx = pFormatCtx->streams[videoStream]->codec;

pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

if(pCodec == NULL)

err_exit("not find video decode");

if( avcodec_open2(pCodecCtx, pCodec, NULL) < 0 )

err_exit("not open video decode");

/* 接下来我们准备给即将解码的图片分配内存空间 */

/* AVFrame:用于保存数据帧的数据结构,这里的两个帧分别是保存颜色转换前后的两帧图像 */

/* pFrame用于存储解码后的数据,pFrameRGB用于存储转换后的数据 */

AVFrame *pFrame = avcodec_alloc_frame();

if(pFrame == NULL)

err_exit("avcodec_alloc_frame");

AVFrame *pFrameRGB = avcodec_alloc_frame();

if(pFrameRGB == NULL)

err_exit("avcodec_alloc_frame");

my_debug("width:%d

height:%d\n", pCodecCtx->width, pCodecCtx->height);

/*

** 调用 avpicture_get_size 根据 pCodecCtx 中原始图像的宽高计算 RGB24 格式

** 的图像需要占用的空间大小,这是为了之后给 pFrameRGB 分配空间:

*/

int numBytes = avpicture_get_size(AV_PIX_FMT_RGB565LE, pCodecCtx->width, pCodecCtx->height);

/*

** 首先是用 av_malloc 分配上面计算大小的内存空间,然后调用

** avpicture_fill 将 pFrameRGB 跟 buffer 指向的内存关联起来

*/

uint8_t *buffer = av_malloc(numBytes);

avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB565LE, pCodecCtx->width, pCodecCtx->height);

/* 一切准备好就可以开始从文件中读取视频帧并解码得到图像了

** av_read_frame 从文件中读取一个packet,对于视频来说一个packet里面包含一帧图像数据,音频可能包含多

** 个帧(当音频帧长度固定时),读到这一帧后,如果是视频帧,则使用 avcodec_decode_video2 对packet中的帧

** 进行解码,有时候解码器并不能从一个packet中解码得到一帧图像数据(比如在需要其他参考帧的情况下),因

** 此会设置 frameFinished,如果已经得到下一帧图像则设置 frameFinished 非零,否则为零.所以这里我们判

** 断 frameFinished 是否为零来确定 pFrame 中是否已经得到解码的图像.注意在每次处理完后需要调用

** av_free_packet 释放读取的packet.解码得到图像后,很有可能不是我们想要的 RGB24 格式,因此需要使用

** swscale 来做转换,调用 sws_getCachedContext 得到转换上下文,使用 sws_scale 将图形从解码后的格式转

** 换为 RGB24,最后将前50帧写人 ppm 文件.最后释放图像以及关闭文件

*/

i = 0;

int frameFinished;

AVPacket packet;

// 解析文件时会将音/视频帧读入到packet中

lcd_printf(lcdinfo, 0, 0, RED, 0, 0, "制作:赵建辉 QQ:809205580");

while(av_read_frame(pFormatCtx, &packet) >= 0 ) {

if(packet.stream_index == videoStream) {

avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

if(frameFinished) {

struct SwsContext *img_convert_ctx = NULL;

img_convert_ctx = sws_getCachedContext(img_convert_ctx, pCodecCtx->width,

pCodecCtx->height, pCodecCtx->pix_fmt,

pCodecCtx->width, pCodecCtx->height,

AV_PIX_FMT_RGB565LE, SWS_BICUBIC,

NULL, NULL, NULL);

if(!img_convert_ctx)

err_exit("Cannot initialize sws conversion context\n");

sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0,

pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

show16bpp(lcdinfo, 0, 16, pCodecCtx->width, pCodecCtx->height, (u16 *)pFrameRGB->data[0]);

}

}

av_free_packet(&packet);

}

av_free(buffer);

av_free(pFrameRGB);

av_free(pFrame);

avcodec_close(pCodecCtx);

avformat_close_input(&pFormatCtx);

if (lcd_release(lcdinfo) < 0)

err_exit("lcd_release");

return 0;

}

lcd.h:

#ifndef LCD_H_

#define LCD_H_

#include <fcntl.h>

#include <sys/mman.h>

#include <sys/ioctl.h>

#include <linux/fb.h>

#include <stdarg.h>

#include "myhead.h"

#define RED (0x1f << 11)

#define GREEN (0x3f << 5)

#define BLUE (0x1f)

extern const unsigned char __CHS[];

extern const unsigned char __ASCII[];

struct lcd_info_t {

int lcdfd;

char *fbp;

int w;

int h;

int xoffset;

int yoffset;

int bpp;

int screensize;

int line_length;

};

/*

** lcd_device: such as "/dev/fb0"

** return: NULL error

*/

struct lcd_info_t *lcd_init(const char *lcd_device);

/*

** return:0 succeed

** :-1 error

*/

int lcd_release(struct lcd_info_t *lcdinfo);

void lcd_putpixel(struct lcd_info_t *lcdinfo, int x, int y, u16 color);

/*

** color:what backgroung color to show

*/

void clear_screen(struct lcd_info_t *lcdinfo, u16 color);

/*

** (xp,yp):display position

** w,h:what image size to display

** video:what image to display

*/

void show16bpp(struct lcd_info_t *lcdinfo, int xp, int yp, int w, int h, u16 *video);

/*

** print 8x16 ascii

** (xp,yp):display position

** ch:what ascii to show

** color:what text color to show

** bgc:what background color to show

** bg :1 show background color

** :0 don't show background color

*/

void lcd_put_ascii(struct lcd_info_t *lcdinfo, int xp, int yp, const unsigned char ch, u16 color, u16 bgc, u8 bg);

/*

** node:must in ANSI character encoding

** print 16x16 Chinese word

** (xp,yp):display position

** al:area number and location number

** color:what Chinese word color to show

** bgc:what background color to show

** bg :1 show background color

** :0 don't show background color

*/

void lcd_put_hz(struct lcd_info_t *lcdinfo, int xp, int yp, u16 al, u16 color, u16 bgc, u8 bg);

void lcd_printf(struct lcd_info_t *lcdinfo, int xp, int yp, u16 color, u16 bgc, u8 bg, char *fmt, ...);

#endif

lcd.c:

#include "lcd.h"

struct lcd_info_t *lcd_init(const char *lcd_device)

{

struct fb_var_screeninfo vinfo;

struct fb_fix_screeninfo finfo;

static struct lcd_info_t *lcdinfo = NULL;

lcdinfo = malloc(sizeof(struct lcd_info_t));

if (NULL == lcdinfo) {

my_debug("malloc");

return NULL;

}

bzero(lcdinfo, sizeof(struct lcd_info_t));

/* Open the file for reading and writing */

lcdinfo->lcdfd = open(lcd_device, O_RDWR);

if (lcdinfo->lcdfd < 0) {

my_debug("open");

return NULL;

}

my_debug("The framebuffer device was opened successfully.\n");

/* Get fixed screen information */

if (ioctl(lcdinfo->lcdfd, FBIOGET_FSCREENINFO, &finfo)) {

my_debug("ioctl");

return NULL;

}

/* Get variable screen information */

if (ioctl(lcdinfo->lcdfd, FBIOGET_VSCREENINFO, &vinfo)) {

my_debug("ioctl");

return NULL;

}

lcdinfo->w = vinfo.xres;

lcdinfo->h = vinfo.yres;

lcdinfo->xoffset = vinfo.xoffset;

lcdinfo->yoffset = vinfo.yoffset;

lcdinfo->bpp = vinfo.bits_per_pixel;

lcdinfo->line_length = finfo.line_length;

my_debug("%dx%d, %dbpp\n", lcdinfo->w, lcdinfo->h, lcdinfo->bpp);

/* Figure out the size of the screen in bytes */

lcdinfo->screensize = lcdinfo->w * lcdinfo->h * lcdinfo->bpp / 8;

/* Map the device to memory */

lcdinfo->fbp = (char *)mmap(0, lcdinfo->screensize, PROT_READ | PROT_WRITE, MAP_SHARED, lcdinfo->lcdfd, 0);

if (MAP_FAILED == lcdinfo->fbp) {

my_debug("mmap");

return NULL;

}

my_debug("The framebuffer device was mapped to memory successfully.\n");

clear_screen(lcdinfo, 0);

return lcdinfo;

}

int lcd_release(struct lcd_info_t *lcdinfo)

{

if (munmap(lcdinfo->fbp, lcdinfo->screensize)< 0) {

my_debug("munmap");

return -1;

}

my_debug("The framebuffer device was munmapped to memory successfully.\n");

close(lcdinfo->lcdfd);

my_debug("The framebuffer device was closed successfully.\n");

free(lcdinfo);

return 0;

}

void lcd_putpixel(struct lcd_info_t *lcdinfo, int x, int y, u16 color)

{

int location = 0;

if ( (x >= lcdinfo->w) || (y >= lcdinfo->h))

return;

location = (x + lcdinfo->xoffset) * (lcdinfo->bpp / 8) + (y + lcdinfo->yoffset) * lcdinfo->line_length;

*((u16 *)(lcdinfo->fbp + location)) = color;

}

void clear_screen(struct lcd_info_t *lcdinfo, u16 color)

{

int location;

for (int y = 0; y < lcdinfo->h; y++) {

for (int x = 0; x < lcdinfo->w; x++) {

location = (x + lcdinfo->w) * (lcdinfo->bpp / 8) + (y + lcdinfo->h) * lcdinfo->line_length;

lcd_putpixel(lcdinfo, x, y, color);

}

}

}

void show16bpp(struct lcd_info_t *lcdinfo, int xp, int yp, int w, int h, u16 *video)

{

if (w > lcdinfo->w - xp)

w = lcdinfo->w - xp;

if (h > lcdinfo->h - yp)

h = lcdinfo->h - yp;

for (int y = yp; y < h; y++) {

for (int x = xp; x < w; x++) {

lcd_putpixel(lcdinfo, x, y, *video++);

}

}

}

void lcd_put_ascii(struct lcd_info_t *lcdinfo, int xp, int yp, const unsigned char ch, u16 color, u16 bgc, u8 bg)

{

unsigned char *pZK, mask, buffer;

int x, y;

pZK = (unsigned char *)&__ASCII[ch * 16];

for (y = 0; y < 16; y++) {

mask = 0x80;

buffer = pZK[y];

for (x = 0; x < 8; x++) {

if(buffer & mask)

lcd_putpixel(lcdinfo, xp + x, yp + y, color);

else if(bg)

lcd_putpixel(lcdinfo, xp + x, yp + y, bgc);

mask = mask >> 1;

}

}

}

void lcd_put_hz(struct lcd_info_t *lcdinfo, int xp, int yp, u16 al, u16 color, u16 bgc, u8 bg)

{

int x, y;

unsigned char *pZK,mask,buf;

pZK = (unsigned char *)&__CHS[(((al >> 8) - 1 )*94 + (al & 0x00FF)- 1)*32];

for (y = 0 ; y < 16 ; y++) {

/* left */

mask = 0x80;

buf = pZK[y * 2];

for (x = 0; x < 8; x++) {

if (buf & mask)

lcd_putpixel(lcdinfo, xp + x, yp + y, color);

else if (bg)

lcd_putpixel(lcdinfo, xp + x, yp + y, bgc);

mask = mask >> 1;

}

/* right */

mask = 0x80;

buf = pZK[y * 2 + 1];

for (x = 0; x < 8; x++) {

if (buf & mask)

lcd_putpixel(lcdinfo, xp + x + 8, yp + y, color);

else if (bg)

lcd_putpixel(lcdinfo, xp + x + 8, yp + y, bgc);

mask = mask >> 1;

}

}

}

void lcd_printf(struct lcd_info_t *lcdinfo, int xp, int yp, u16 color, u16 bgc, u8 bg, char *fmt, ...)

{

char __LCD_Printf_Buf[256];

va_list ap;

unsigned char *pStr = (unsigned char *)__LCD_Printf_Buf;

unsigned int i = 0;

va_start(ap,fmt);

vsprintf(__LCD_Printf_Buf,fmt,ap);

va_end(ap);

while (*pStr != 0) {

switch (*pStr) {

case '\n':

break;

default:

if ( *pStr > 0xA0 && *(pStr+1) > 0xA0 ) { // Chinese

lcd_put_hz(lcdinfo, xp , yp, (*pStr - 0xA0)*0x0100 + *(pStr+1) - 0xA0, color, bgc, bg);

pStr++;

i++;

xp += 16;

}

else { /* ASCII */

lcd_put_ascii(lcdinfo, xp , yp, *pStr, color, bgc, bg);

xp += 8;

}

break;

}

pStr++;

i++;

if(i > 256) break;

}

}

myhead.h:

#ifndef MYHEAD_H_

#define MYHEAD_H_

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <errno.h>

#define MIN(x, y) ((x) < (y) ? (x) : (y))

#define MAX(x, y) ((x) > (y) ? (x) : (y))

#define err_exit(errmsg) \

do { \

fprintf(stderr, "in %s at %s : %d : %s : %s\n", __FUNCTION__, __FILE__, __LINE__ - 1, errmsg, strerror(errno)); \

exit(EXIT_FAILURE);

\

} while(0)

#ifdef DEBUG

#define my_debug(fmt, arg...)

fprintf(stderr, fmt, ##arg)

#else

#define my_debug(fmt,...)

#endif

typedef unsigned char u8;

typedef unsigned short u16;

typedef unsigned int u32;

#endif

Makefile:

DEBUG = y

ifeq ($(DEBUG), y)

CFLAGS += -DDEBUG

endifwww.2cto.com

CROSS = arm-linux-

CFLAGS += -std=gnu99 -Wall -lavutil -lavformat -lavcodec -lswscale

OBJ = ffmpeg_decode

all:

$(CROSS)gcc $(CFLAGS) *.c -o $(OBJ)

install:

cp $(OBJ) /home/work/rootfs

clean:

rm -f *.o $(OBJ)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: