ffmpeg的库可下载:搜索ffmpeg sdk 3.2即可得到。
一、解码显示过程
我使用的环境是vs2008
头文件:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#ifdef __cplusplus
}
#endif
1、注册所用编码器或者解码器
av_register_all();
2、打开视频文件
AVFormatContext *pFormatCtx;//AVFormatContext 即format I/O context,比较重要,里面记录了流文件相关信息,基本贯穿整个处理流程
// Open video file
if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
return -1; // Couldn't open file
dump_format(pFormatCtx, 0,argv[1], 0);//用于调试,可以输出一些相关信息的
3、获取流的一些信息,比如说解码时需要的height及width
if(av_find_stream_info(pFormatCtx)<0)
return -1; // Couldn't find stream information
4、找到视频流
AVCodecContext *pCodecCtx;//AVCodecContext即Codec的相关信息
// Find the first video stream
int videoStream=-1;
for(int i=0; i<pFormatCtx->nb_streams; i++)
{
if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
videoStream=i;
break;
}
}
if(videoStream==-1)
return -1; // Didn't find a video stream
// Get a pointer to the codec context for the video stream
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
5、为对应的视频流找到编解码器
// Find the decoder for the video stream
AVCodec *pCodec;//编解码器信息
pCodec = NULL;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL) {
fprintf(stderr, "Unsupported codec!\n");
return -1; // Codec not found
}
6、为对应的流打开所需要的编码器
// Open codec
if(avcodec_open(pCodecCtx, pCodec)<0)
return -1; // Could not open code
7、解码
-----分配一个AVFrame的结构,用于记录原始图像信息
AVFrame *pFrame;
pFrame = NULL;
pFrame = avcodec_alloc_frame();
if(pFrame==NULL)
return -1;
-----根据图像大小创建一个缓冲区
uint8_t *buffer;
int numBytes;
// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width,
pCodecCtx->height);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
avpicture_fill((AVPicture *)pFrame, buffer, PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);//填充AVPicture对应域
-------解码
int frameFinished;
AVPacket packet;
i=0;
while(av_read_frame(pFormatCtx, &packet)>=0) {
// Is this a packet from the video stream?
if(packet.stream_index==videoStream) {
// Decode video frame
avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
packet.data, packet.size);
// Did we get a video frame?
if(frameFinished) {
显示视频。。。。。。
}
}
// Free the packet that was allocated by av_read_frame
}
-------释放资源
av_free_packet(&packet);
av_free(buffer);
av_free(pFrame);
// Close the codec
avcodec_close(pCodecCtx);
// Close the video file
av_close_input_file(pFormatCtx);
-------显示视频
1)加入SDL动态库
1)下载SDL开发包SDL-devel-1.2.14-VC6.zip,并解压主要生成include、lib文件,下载地址:http://www.libsdl.org/download-1.2.php;
2)在vc的安装目录Microsoft Visual Studio\VC98\Include下创建一个新文件夹SDL,将1)中解压后的include文件夹中所有文件拷贝到所创建SDL文件夹中
3)将1)中解压后的lib文件夹中.lib文件以及.dll文件拷贝到所创建的工程目录下
4)在工程的选项配置为多线程已解决库冲突问题
5)编译后会发现数据定义有冲突,我解决的办法是把SDL中对应的项注释掉
2)初始化SDL库
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
{//SDL init
fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
return -1;
}
3)设置视频模式
SDL_Surface *screen;
screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
if(!screen) //create a screen
{
fprintf(stderr, "SDL: could not set video mode - exiting\n");
return -2;
}
4)创建贴图
SDL_Overlay *bmp;
bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,
SDL_YV12_OVERLAY, screen);
SDL_LockYUVOverlay(bmp);
AVPicture pict;
pict.data[0] = bmp->pixels[0];
pict.data[1] = bmp->pixels[2];
pict.data[2] = bmp->pixels[1];
pict.linesize[0] = bmp->pitches[0];
pict.linesize[1] = bmp->pitches[2];
pict.linesize[2] = bmp->pitches[1];
SwsContext *ctx;
ctx = sws_getContext(
pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
PIX_FMT_YUV420P,
SWS_BICUBIC, NULL, NULL, NULL);
if(ctx == NULL)
{
fprintf(stderr, "Cannot get resampling context\n");
return -3;
}
//sws_scale( ctx,pFrame->data,pFrame->linesize,0,pCodecCtx->height,pFrame->data,pFrame->linesize);//原文中这句话没有注释,我在测试时发现需要注释掉才能成功
sws_scale( ctx,pFrame->data,pFrame->linesize,0,pCodecCtx->height,pict.data,pict.linesize);
SDL_UnlockYUVOverlay(bmp);
//draw
5)显示图像
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = pCodecCtx->width;
rect.h = pCodecCtx->height;
SDL_DisplayYUVOverlay(bmp, &rect);
二、注意
1、此实例只解码视频
2、显示图像的时候没有帧率方面的处理,所以播放速度过快,cpu占用率过高,有待完善
- #include <windows.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #pragma once
- #ifdef __cplusplus
- extern "C" {
- #endif
- #include <libavcodec/avcodec.h>
- #include <libavformat/avformat.h>
- #include <libswscale/swscale.h>
- #ifdef __cplusplus
- }
- #endif
- //定义BMP文件头
- #ifndef _WINGDI_
- #define _WINGDI_
- typedef struct tagBITMAPFILEHEADER {
- WORD bfType;
- DWORD bfSize;
- WORD bfReserved1;
- WORD bfReserved2;
- DWORD bfOffBits;
- } BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
- typedef struct tagBITMAPINFOHEADER{
- DWORD biSize;
- LONG biWidth;
- LONG biHeight;
- WORD biPlanes;
- WORD biBitCount;
- DWORD biCompression;
- DWORD biSizeImage;
- LONG biXPelsPerMeter;
- LONG biYPelsPerMeter;
- DWORD biClrUsed;
- DWORD biClrImportant;
- } BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
- #endif
- //保存BMP文件的函数
- void SaveAsBMP (AVFrame *pFrameRGB, int width, int height, int index, int bpp)
- {
- char buf[5] = {0};
- BITMAPFILEHEADER bmpheader;
- BITMAPINFOHEADER bmpinfo;
- FILE *fp;
- char *filename = new char[255];
- //文件存放路径,根据自己的修改
- sprintf_s(filename,255,"%s%d.bmp","D:/My Documents/Visual Studio 2008/Projects/WriteVideo/Debug/test",index);
- if ( (fp=fopen(filename,"wb+")) == NULL )
- {
- printf ("open file failed!\n");
- return;
- }
- bmpheader.bfType = 0x4d42;
- bmpheader.bfReserved1 = 0;
- bmpheader.bfReserved2 = 0;
- bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
- bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;
- bmpinfo.biSize = sizeof(BITMAPINFOHEADER);
- bmpinfo.biWidth = width;
- bmpinfo.biHeight = height;
- bmpinfo.biPlanes = 1;
- bmpinfo.biBitCount = bpp;
- bmpinfo.biCompression = BI_RGB;
- bmpinfo.biSizeImage = (width*bpp+31)/32*4*height;
- bmpinfo.biXPelsPerMeter = 100;
- bmpinfo.biYPelsPerMeter = 100;
- bmpinfo.biClrUsed = 0;
- bmpinfo.biClrImportant = 0;
- fwrite (&bmpheader, sizeof(bmpheader), 1, fp);
- fwrite (&bmpinfo, sizeof(bmpinfo), 1, fp);
- fwrite (pFrameRGB->data[0], width*height*bpp/8, 1, fp);
- fclose(fp);
- }
- //主函数
- int main (void)
- {
- unsigned int i = 0, videoStream = -1;
- AVCodecContext *pCodecCtx;
- AVFormatContext *pFormatCtx;
- AVCodec *pCodec;
- AVFrame *pFrame, *pFrameRGB;
- struct SwsContext *pSwsCtx;
- const char *filename = "D:/My Documents/Visual Studio 2008/Projects/WriteVideo/Debug/DELTA.MPG";
- AVPacket packet;
- int frameFinished;
- int PictureSize;
- uint8_t *buf;
- //注册编解码器
- av_register_all();
- //打开视频文件
- if ( av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL) != 0 )
- {
- printf ("av open input file failed!\n");
- exit (1);
- }
- //获取流信息
- if ( av_find_stream_info(pFormatCtx) < 0 )
- {
- printf ("av find stream info failed!\n");
- exit (1);
- }
- //获取视频流
- for ( i=0; i<pFormatCtx->nb_streams; i++ )
- if ( pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
- {
- videoStream = i;
- break;
- }
- if (videoStream == -1)
- {
- printf ("find video stream failed!\n");
- exit (1);
- }
- pCodecCtx = pFormatCtx->streams[videoStream]->codec;
- pCodec = avcodec_find_decoder (pCodecCtx->codec_id);
- if (pCodec == NULL)
- {
- printf ("avcode find decoder failed!\n");
- exit (1);
- }
- //打开解码器
- if ( avcodec_open(pCodecCtx, pCodec)<0 )
- {
- printf ("avcode open failed!\n");
- exit (1);
- }
- //为每帧图像分配内存
- pFrame = avcodec_alloc_frame();
- pFrameRGB = avcodec_alloc_frame();
- if ( (pFrame==NULL)||(pFrameRGB==NULL) )
- {
- printf("avcodec alloc frame failed!\n");
- exit (1);
- }
- PictureSize = avpicture_get_size (PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
- buf = (uint8_t*)av_malloc(PictureSize);
- if ( buf == NULL )
- {
- printf( "av malloc failed!\n");
- exit(1);
- }
- avpicture_fill ( (AVPicture *)pFrameRGB, buf, PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
- //设置图像转换上下文
- pSwsCtx = sws_getContext (pCodecCtx->width,
- pCodecCtx->height,
- pCodecCtx->pix_fmt,
- pCodecCtx->width,
- pCodecCtx->height,
- PIX_FMT_BGR24,
- SWS_BICUBIC,
- NULL, NULL, NULL);
- i = 0;
- while(av_read_frame(pFormatCtx, &packet) >= 0)
- {
- if(packet.stream_index==videoStream)
- {
- avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
- packet.data, packet.size);
- if(frameFinished)
- {
- //反转图像 ,否则生成的图像是上下调到的
- pFrame->data[0] += pFrame->linesize[0] * (pCodecCtx->height - 1);
- pFrame->linesize[0] *= -1;
- pFrame->data[1] += pFrame->linesize[1] * (pCodecCtx->height / 2 - 1);
- pFrame->linesize[1] *= -1;
- pFrame->data[2] += pFrame->linesize[2] * (pCodecCtx->height / 2 - 1);
- pFrame->linesize[2] *= -1;
- //转换图像格式,将解压出来的YUV420P的图像转换为BRG24的图像
- sws_scale (pSwsCtx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
- SaveAsBMP (pFrameRGB, pCodecCtx->width, pCodecCtx->height, i++, 24);
- }
- }
- av_free_packet(&packet);
- }
- sws_freeContext (pSwsCtx);
- av_free (pFrame);
- av_free (pFrameRGB);
- avcodec_close (pCodecCtx);
- av_close_input_file (pFormatCtx);
- return 0;
- }