最近有个项目需要研究视频压缩域,主要对于运动矢量。获取运动矢量的各种算法比较复杂,发现ffmpeg可以直接获取,非常简便。
初学ffmpeg,雷霄骅大神的博客提供了非常大的帮助,http://blog.csdn.net/leixiaohua1020/article/details/15811977/。
下载ffmpeg源码,在doc/example下有extract_mvs.c文件 ,一个提取运动矢量简单例程。在vs2010编译,遇到一些麻烦,参考http://blog.csdn.net/leixiaohua1020/article/details/12747899,http://blog.csdn.net/leixiaohua1020/article/details/12029697。
关于运动矢量的显示,或者说原始帧数据的显示,雷神用的是SDL,这里使用的是opencv.
在源码基础上改的,图像bgr数据保存在pFrameRGB->data[0],这是关键。转为IplImage然后画出运动矢量,显示播放。
//#ifdef __cplusplus
//
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
using namespace std;
extern "C"
{
#include
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
};
//#endif
//#pragma comment(lib,"avutil.lib")
//#pragma comment(lib,"avcodec.lib")
//#pragma comment(lib,"avformat.lib")
#include
#include
using namespace std;
static AVFormatContext *fmt_ctx = NULL;
static AVCodecContext *video_dec_ctx = NULL;
static AVStream *video_stream = NULL;
static const char *src_filename = NULL;
static int video_stream_idx = -1;
static AVFrame *frame = NULL;
static AVFrame *pFrameRGB=NULL;
static AVPicture *pFrame=NULL;
static AVPacket pkt;
static int video_frame_count = 0;
static SwsContext *img_convert_ctx=NULL;
//FILE *output;
FILE *fp;
static IplImage *imgShow=NULL;
static IplImage *imgShow2=NULL;
static unsigned char* frameData=0;
typedef struct{
int mv_sx;
int mv_sy;
int mv_x;
int mv_y;
}MV_DATA;
const int K=3;
void uchar2IplImageBGR(unsigned char *inArrayCur, int img_w, int img_h,IplImage* pImg);
int img_w,img_h;
const int mbNum=131100;//1920*1080/16+1920/4*2+1080/4*2;
int num,nozero;
CvPoint p1,p2;
MV_DATA global_mv;
int j,k;
unsigned char *out_roi;
unsigned char *double_show1;
unsigned char *double_show2;
IplImage* roi_show=NULL;
IplImage* double_show =NULL;
unsigned char *gray_roi;
IplImage* ipl_gray_roi =NULL;
static int decode_packet(int *got_frame, int cached)
{
MV_DATA* mv_data=(MV_DATA*)malloc(mbNum*sizeof(MV_DATA));//读取运动矢量保存到mv_data中
num=0,nozero=0;
ave_x=0,ave_y=0;//平均值
out_roi=(unsigned char *)malloc(img_w*img_h);
roi_show=cvCreateImage(cvSize(img_w,img_h),IPL_DEPTH_8U,1);
double_show = cvCreateImage(cvSize(img_w*2,img_h),IPL_DEPTH_8U,3);
double_show1=(unsigned char *)malloc(img_w*img_h*3);
double_show2=(unsigned char *)malloc(img_w*img_h*3);
gray_roi=(unsigned char *)malloc(img_w*img_h);
ipl_gray_roi = cvCreateImage(cvSize(img_w,img_h),IPL_DEPTH_8U,1);
vector mv_datas;
for(int s=0;s0;
}
int decoded = pkt.size;
*got_frame = 0;
if (pkt.stream_index == video_stream_idx) {
int ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);
if (ret < 0) {
//fprintf(stderr, "Error decoding video frame (%s)\n", av_err2str(ret));
return ret;
}
if (*got_frame) {
int i;
AVFrameSideData *sd;
video_frame_count++;
sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MOTION_VECTORS);
if (sd) {
img_convert_ctx = sws_getContext(video_dec_ctx->width, video_dec_ctx->height, video_dec_ctx->pix_fmt, video_dec_ctx->width, video_dec_ctx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(img_convert_ctx, (const uint8_t* const*)frame->data, frame->linesize, 0, video_dec_ctx->height, pFrameRGB->data, pFrameRGB->linesize);
//fwrite(pFrameRGB->data[0],(video_dec_ctx->width)*(video_dec_ctx->height)*3,1,output);
//memcpy(frameData,pFrameRGB->data[0],video_dec_ctx->width*video_dec_ctx->height*3);
uchar2IplImageBGR(pFrameRGB->data[0],video_dec_ctx->width,video_dec_ctx->height,imgShow);
cvCopy(imgShow,imgShow2);
const AVMotionVector *mvs = (const AVMotionVector *)sd->data;
for (i = 0; i < sd->size / sizeof(*mvs); i++) {
const AVMotionVector *mv = &mvs[i];
//printf("%d,%2d,%2d,%2d,%4d,%4d,%4d,%4d,0x%"PRIx64"\n",
//video_frame_count, mv->source,
//mv->w, mv->h, mv->src_x, mv->src_y,
//mv->dst_x, mv->dst_y);
p1.x=mv->src_x;
p1.y=mv->src_y;
p2.x=mv->dst_x;
p2.y=mv->dst_y;
cvLine(imgShow,p2,p2,cvScalar( 0,0,255 ),1,8,0);
}
cvShowImage("1",imgShow);
if(cvWaitKey(50)>=32)
cvWaitKey(0);
sws_freeContext(img_convert_ctx);
}
}
}
free(out_roi);
free(double_show1);
free(double_show2);
free(gray_roi);
cvReleaseImage(&roi_show);
cvReleaseImage(&double_show);
cvReleaseImage(&ipl_gray_roi);
free(mv_data);
mv_datas.clear();
return decoded;
}
static int open_codec_context(int *stream_idx,
AVFormatContext *fmt_ctx, enum AVMediaType type)
{
int ret;
AVStream *st;
AVCodecContext *dec_ctx = NULL;
AVCodec *dec = NULL;
AVDictionary *opts = NULL;
ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
if (ret < 0) {
fprintf(stderr, "Could not find %s stream in input file '%s'\n",
av_get_media_type_string(type), src_filename);
return ret;
} else {
*stream_idx = ret;
st = fmt_ctx->streams[*stream_idx];
/* find decoder for the stream */
dec_ctx = st->codec;
dec = avcodec_find_decoder(dec_ctx->codec_id);
if (!dec) {
fprintf(stderr, "Failed to find %s codec\n",
av_get_media_type_string(type));
return AVERROR(EINVAL);
}
/* Init the video decoder */
av_dict_set(&opts, "flags2", "+export_mvs", 0);
if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) {
fprintf(stderr, "Failed to open %s codec\n",
av_get_media_type_string(type));
return ret;
}
}
return 0;
}
int main(int argc, char** argv)
{
int ret = 0, got_frame;
//string name;
//getline(cin,name,'\n');
src_filename = "1.mp4";
cvNamedWindow("1",0);
cvNamedWindow("2",0);
char motionname[] = "MV_Data.txt";
fp = fopen(motionname, "w");
av_register_all();
if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {
fprintf(stderr, "Could not open source file %s\n", src_filename);
exit(1);
}
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
fprintf(stderr, "Could not find stream information\n");
exit(1);
}
if (open_codec_context(&video_stream_idx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
video_stream = fmt_ctx->streams[video_stream_idx];
video_dec_ctx = video_stream->codec;
}
av_dump_format(fmt_ctx, 0, src_filename, 0);
if (!video_stream) {
fprintf(stderr, "Could not find video stream in the input, aborting\n");
ret = 1;
goto end;
}
frame = av_frame_alloc();
pFrameRGB=av_frame_alloc();
uint8_t *out_buffer;
out_buffer=new uint8_t[avpicture_get_size(AV_PIX_FMT_BGR24, video_dec_ctx->width, video_dec_ctx->height)];
avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_BGR24, video_dec_ctx->width, video_dec_ctx->height);
imgShow = cvCreateImage(cvSize(video_dec_ctx->width,video_dec_ctx->height),IPL_DEPTH_8U,3);
imgShow2 = cvCreateImage(cvSize(video_dec_ctx->width,video_dec_ctx->height),IPL_DEPTH_8U,3);
frameData=(unsigned char *)malloc(video_dec_ctx->width*video_dec_ctx->height*3);
img_w=video_dec_ctx->width;
img_h=video_dec_ctx->height;
if (!frame) {
fprintf(stderr, "Could not allocate frame\n");
ret = AVERROR(ENOMEM);
goto end;
}
/* initialize packet, set data to NULL, let the demuxer fill it */
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
//output=fopen("out.rgb","wb+");
/* read frames from the file */
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
AVPacket orig_pkt = pkt;
do {
ret = decode_packet(&got_frame, 0);
if (ret < 0)
break;
pkt.data += ret;
pkt.size -= ret;
} while (pkt.size > 0);
av_packet_unref(&orig_pkt);
}
/* flush cached frames */
pkt.data = NULL;
pkt.size = 0;
do {
decode_packet(&got_frame, 1);
} while (got_frame);
end:
//fclose(output);
fclose(fp);
avcodec_close(video_dec_ctx);
avformat_close_input(&fmt_ctx);
av_frame_free(&frame);
av_frame_free(&pFrameRGB);
cvReleaseImage(&imgShow);
free(frameData);
getchar();
return ret < 0;
}
void uchar2IplImageBGR(unsigned char *inArrayCur, int img_w, int img_h,IplImage* pImg)
{
int i,j;
for (i = 0; i < img_h; i++)
{
for (j = 0; j < img_w*3; j++)
{
*(pImg->imageData + i*pImg->widthStep+j)=inArrayCur[(i)*img_w*3+j] ;
}
}
}
后话:这是几个月前的东西,很多细节和遇到的问题都记不太清了。工作中很多知识没有总结,一段时间过后忘记了,跟没做过一样,没有任何进步,有时候想起来非常惋惜,这也是写博客的意义所在。不要找借口说工作太忙,其实就是懒!!