一.Linux下MIPS平台交叉编译FFMpeg库:
1.下载ffmpeg库的源代码
(http://sourceforge.net/project/showfiles.php?group_id=205275&package_id=248632):
[root@localhost ffmpeg]# ls
ffmpeg-laster.tar.gz
2.解压:
[root@localhost ffmpeg]# tar zxvf ffmpeg-laster.tar.gz
3.开始配置,并编译:
[root@localhost ffmpeg]# mkdir ffmpeg-release
[root@localhost ffmpeg]# cd ffmpeg
[root@localhost ffmpeg]# ./configure --enable-cross-compile --target-os=linux --cross-prefix=mipsel-linux- --cc=mipsel-linux-gcc --enable-shared --arch=mips --prefix=/opt/brcm/ffmpeg/ffmpeg-release
[root@localhost ffmpeg]# make
[root@localhost ffmpeg]# make install
[root@localhost ffmpeg]# cd ../ffmpeg-release
[root@localhost ffmpeg-release]# tar -zcvf ffjpeg_lib.tar.gz ./lib
板子上运行:
# cp ffjpeg_lib.tar.gz /usr/local/lib/
# cd /usr/local/lib/
# tar -zxvf ffjpeg_lib.tar.gz -C ../
# rm ffjpeg_lib.tar.gz
# cp ffmpeg-release/bin/* /bin/
# ffmpeg
FFmpeg version SVN-r21694, Copyright (c) 2000-2010 Fabrice Bellard, et al.
built on Nov 17 2012 02:25:17 with gcc 4.5.3
configuration: --enable-cross-compile --target-os=linux --cross-prefix=mipsel-linux- --cc=mipsel-linux-gcc --enable-shared --arch=mips --prefix=/opt/brcm/ffmpeg/ffmpeg-release
libavutil 50. 9. 0 / 50. 9. 0
libavcodec 52.52. 0 / 52.52. 0
libavformat 52.51. 0 / 52.51. 0
libavdevice 52. 2. 0 / 52. 2. 0
libswscale 0.10. 0 / 0.10. 0
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...
Use -h to get full help or, even better, run 'man ffmpeg'
#
到现在为止, 我们就成功的将 ffmpeg 移植到我们的开发板上了。
二.Linux下使用ffmpeg库:
1.命令行使用:
ffmpeg -i test.mp4 -y -f image2 -ss 30 -s 95*95 -vframes 1 a.jpg
找一个MP4文件:test.mp4,然后使用上面的命令就可以取得30秒时视频的图像,保存为
95*95像素的JPG文件。
ffmpeg -i 000094.mp4 -vframes 30 -pix_fmt rgb24 -y -f gif a.gif
把前30帧转化为GIF文件。
2.在程序中调用函数截获视频图片:
首先加入库支持,在我们的可执行程序的Makefile文件中,加入:
CFLAGS += -I/opt/brcm/ffmpeg/ffmpeg-release/include/
LFLAGS += -L/opt/brcm/ffmpeg/ffmpeg-release/lib/ -lavutil -lavformat -lavcodec -lswscale
(记住,后面最后四个参数缺一不可,否则编译不通过)
然后关于在编程中使用FFMPEG库的方法,附带一个成熟的Demo。
(test_snap.c和Makefile)
三.遇到的问题:
1.找不到关键帧:first frame is no keyframe: 这样的截图为透明色,不满足截图要求。
解决方案: 跟踪截图函数的返回值,如果no keyframe,将开始时间推迟1秒,再截图,直到成功为止。
test_snap.c 如下:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#undef sprintf
#undef uint8_t
#undef uint16_t
#undef uint32_t
#define uint8_t unsigned char
#define uint16_t unsigned short
#define uint32_t unsigned long
#pragma pack(2)
typedef struct BMPHeader
{
uint16_t identifier;
uint32_t file_size;
uint32_t reserved;
uint32_t data_offset;
} BMPHeader;
typedef struct BMPMapInfo
{
uint32_t header_size;
uint32_t width;
uint32_t height;
uint16_t n_planes;
uint16_t bits_per_pixel;
uint32_t compression;
uint32_t data_size;
uint32_t hresolution;
uint32_t vresolution;
uint32_t n_colors_used;
uint32_t n_important_colors;
}BMPMapInfo;
int CreateBmpImg(AVFrame *pFrame, int width, int height, int iFrame)
{
BMPHeader bmpheader;
BMPMapInfo bmpinfo;
FILE *fp;
int y;
char filename[32];
// Open file
memset(filename, 0x0, sizeof(filename));
sprintf(filename, "%d.bmp", iFrame+1);
JPRINTF(("Create BMP File : [%s] \n",filename));
fp = fopen(filename, "wb");
if(!fp)return -1;
bmpheader.identifier = ('M'<<8)|'B';
bmpheader.reserved = 0;
bmpheader.data_offset = sizeof(BMPHeader) + sizeof(BMPMapInfo);
bmpheader.file_size = bmpheader.data_offset + width*height*24/8;
bmpinfo.header_size = sizeof(BMPMapInfo);
bmpinfo.width = width;
bmpinfo.height = height;
bmpinfo.n_planes = 1;
bmpinfo.bits_per_pixel = 24;
bmpinfo.compression = 0;
bmpinfo.data_size = height*((width*3 + 3) & ~3);
bmpinfo.hresolution = 0;
bmpinfo.vresolution = 0;
bmpinfo.n_colors_used = 0;
bmpinfo.n_important_colors = 0;
fwrite(&bmpheader,sizeof(BMPHeader),1,fp);
fwrite(&bmpinfo,sizeof(BMPMapInfo),1,fp);
for(y=height-1; y>=0; y--)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, fp);
fclose(fp);
return 0;
}
//解码指定videostream,并保存frame数据到pFrame上
//返回: 0--成功,非0--失败
int DecodeVideoFrame(AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx,
int videoStream, int64_t endtime, AVFrame *pFrame, int *keyframe_err)
{
static AVPacket packet;
static uint8_t *rawData;
static int bytesRemaining = 0;
int bytesDecoded;
int frameFinished;
static int firstTimeFlag = 1;
int snap_cnt=0;
*keyframe_err=0;
if (firstTimeFlag)
{
firstTimeFlag = 0;
packet.data = NULL;//第一次解frame,初始化packet.data为null
}
while (1)
{
do
{
if (packet.data == NULL) av_free_packet(&packet); //释放旧的packet
if (av_read_frame(pFormatCtx, &packet) < 0)
{
//从frame读取数据保存到packet上,<0表明到了stream end
printf("-->av_read_frame end\n");
goto exit_decode;
}
} while (packet.stream_index != videoStream); //判断当前frame是否为指定的video stream
//判断当前帧是否到达了endtime,是则返回false,停止取下一帧
if (packet.pts >= endtime) return -1;
bytesRemaining = packet.size;
rawData = packet.data;
while (bytesRemaining > 0)
{
++snap_cnt;
bytesDecoded = avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, rawData, bytesRemaining);
if (bytesDecoded < 0) return -1;
bytesRemaining -= bytesDecoded;
rawData += bytesDecoded;
// if (frameFinished) return 0;
if (frameFinished)
{
if(snap_cnt<=1)
*keyframe_err=1;
return 0;
}
}
}
exit_decode:
bytesDecoded = avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, rawData, bytesRemaining);
if(packet.data != NULL) av_free_packet(&packet);
if (frameFinished != 0) return 0;
return -1;
}
void usage(const char *function)
{
printf("Usage: %s [File Name] [Start Time] [End Time]\n", function);
printf("Ex: ./railgun panda.mpg 003005 003010\n");
printf("Time Format: HrsMinsSecs. Ex 003005 means 00 hours 30 minutes 05 senconds\n");
printf("\n");
}
void ParseTime(IN ulong snap_start_time, int64_t *pStartSec,
IN ulong snap_end_time, int64_t *pEndSec)
{
int64_t starttime = 0, endtime = 0;
if (pStartSec)
{
starttime = snap_start_time;
*pStartSec = (3600*starttime/10000) + \
(60*(starttime%10000)/100) + \
(starttime%100);
}
if (pEndSec)
{
endtime = snap_end_time;
*pEndSec = (3600*endtime/10000) + \
(60*(endtime%10000)/100) + \
(endtime%100);
}
}
int __navi_snap_mtv_file(IN char *snap_file_name, IN ulong snap_start_time,
IN ulong snap_end_time,IN int snap_w, IN int snap_h)
{
//RINTF(("Open snap_file_name == [%s] \n",snap_file_name));
const char *filename;
AVFormatContext *ic = NULL;
AVCodecContext *dec = NULL;
AVCodec *codec = NULL;
AVFrame *frame = NULL;
AVFrame *frameRGB = NULL;
uint8_t *buffer = NULL;
int numBytes;
int i, videoStream;
int64_t startTime = 0;
int64_t endTime = 0;
int keyframe_err=0;
static struct SwsContext *img_convert_ctx = NULL;
// Register all formats and codecs
av_register_all();
filename = snap_file_name;
// parse begin time and end time
ParseTime(snap_start_time, &startTime, NULL, NULL);
ParseTime(snap_start_time, &startTime, snap_end_time, &endTime);
startTime *= AV_TIME_BASE;
endTime *= AV_TIME_BASE;
// Open video file
if(av_open_input_file(&ic, filename, NULL, 0, NULL)!=0)
{
printf("Cannt open input file\n");
goto exit_err;
}
// Retrieve stream information
if(av_find_stream_info(ic)<0)
{
printf("Cannt find stream info\n");
goto exit_err;
}
// Dump information about file onto standard error
dump_format(ic, 0, filename, 0);
// Find the first video stream
videoStream=-1;
for(i=0; i<ic->nb_streams; i++)
if(ic->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
{
videoStream=i;
break;
}
if(videoStream==-1)
{
printf("No video stream\n");
goto exit_err;
}
// Get a pointer to the codec context for the video stream
dec=ic->streams[videoStream]->codec;
// Find the decoder for the video stream
codec=avcodec_find_decoder(dec->codec_id);
if(codec==NULL)
{
printf("Found no codec\n");
goto exit_err;
}
// Open codec
if(avcodec_open(dec, codec)<0)
{
printf("Cannt open avcodec\n");
goto exit_err;
}
// Allocate video frame
frame=avcodec_alloc_frame();
// Allocate an AVFrame structure
frameRGB=avcodec_alloc_frame();
if(frameRGB==NULL)
{
av_free(frame);
printf("Cannt alloc frame buffer for RGB\n");
goto exit_err;
}
// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_RGB24, dec->width, dec->height);
buffer=(uint8_t *)av_malloc(numBytes);
if (!buffer)
{
av_free(frame);
av_free(frameRGB);
printf("Cannt alloc picture buffer\n");
goto exit_err;
}
// Assign appropriate parts of buffer to image planes in pFrameRGB
avpicture_fill((AVPicture *)frameRGB, buffer, PIX_FMT_RGB24, dec->width, dec->height);
img_convert_ctx = sws_getContext(dec->width, dec->height, dec->pix_fmt, snap_w, snap_h, PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);
if (img_convert_ctx == NULL) {
printf("Cannot initialize the conversion context\n");
goto exit_err;
}
// Seek frame
startTime = av_rescale_q(startTime, AV_TIME_BASE_Q, ic->streams[videoStream]->time_base);
endTime = av_rescale_q(endTime, AV_TIME_BASE_Q, ic->streams[videoStream]->time_base);
avformat_seek_file(ic, videoStream, INT64_MIN, startTime, startTime, 0);
// Read frames and save first five frames to dist
i=0;
while(!DecodeVideoFrame(ic, dec, videoStream, endTime, frame, &keyframe_err))
{
// Save the frame to disk
i++;
sws_scale(img_convert_ctx, (AVPicture*)frame->data, (AVPicture*)frame->linesize,
0, dec->height, (AVPicture*)frameRGB->data, (AVPicture*)frameRGB->linesize);
if(0 == CreateBmpImg(frameRGB, snap_w, snap_h, i))
{
break; // get one bmp file, we jump out; if you want get more bmp file, clear this line.
}
}
exit_err:
// Free the RGB image
if (buffer)
av_free(buffer);
if (frameRGB)
av_free(frameRGB);
// Free the YUV frame
if (frame)
av_free(frame);
// Close the codec
if (dec)
avcodec_close(dec);
// Close the video file
if (ic)
av_close_input_file(ic);
if (img_convert_ctx)
sws_freeContext(img_convert_ctx);
if(keyframe_err==1) // if first frame is no keyframe.
return -1;
else
return 0;
}
解决关键帧的代码:
if(-1 == __navi_snap_mtv_file(pXPlayTempFileName,000050,000130,96,96))
{ // if first frame is no keyframe.
int ret=-1;
for(i=0; i<30; ++i)
{
if(0==ret) break;
ret = __navi_snap_mtv_file(pXPlayTempFileName,000050+i,000130,96,96);
}
}
Makefile 如下:
CC = mipsel-linux-gcc
TARGET = test_snap
CFLAGS += -I/opt/ffmpeg/ffmpeg-release/include/
LFLAGS += -L/opt/ffmpeg/ffmpeg-release/lib/ -lavutil -lavformat -lavcodec -lswscale
$(TARGET):
$(CC) -o $(TARGET) test_snap.c $(LFLAGS) $(CFLAGS)
附带几个我自己看过的网站:
http://ray.imiddle.net/2008/10/ffmpeg-install-and-usage/
http://blog.csdn.net/menuconfig/article/details/2600890
http://bbs.chinaunix.net/thread-1932536-1-1.html
OK,今天就这多, O(∩_∩)O哈哈~