自己配置工程配置了好久还是出现各种问题,最后用雷神配置好的工程上跑代码。
基于tutorial的第一个简单demo在雷神配置好的ffmpeg+SDL工程上顺利通过,因为tutorial上的demo使用的API有许多已经被删除或修改了,所以需要对照新的API稍作一些修改,一下是tutorial的第一个小程序的流程图和代码
流程图:
代码:
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL2/SDL.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <SDL2/SDL.h>
#include <libavutil/imgutils.h>
#ifdef __cplusplus
};
#endif
#endif
//Output YUV420P data as a file
#define OUTPUT_YUV420P 0
void SaveFrame(AVFrame*,int,int,int);
int main(int argc, char *argv[])
{
av_register_all();//注册文件格式和库
char* Filename = "bigbuckbunny_480x272.h265";
AVFormatContext *pFormatCtx;//文件头部格式信息
avformat_network_init();
pFormatCtx = avformat_alloc_context();
if(avformat_open_input(&pFormatCtx, Filename, NULL, NULL)!=0)
return -1;
//根据头部寻找数据流信息,填到pFormatCtx->streams
if(av_find_stream_info(pFormatCtx)<0)
return -1;
int i;
int videoStream = -1;
for(i = 0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
videoStream = i;
break;
}
if(videoStream == -1)
return -1;
//指向流context的指针,里面包含流的解码信息
AVCodecContext *pCodecCtx;
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
//找到并相应解码器
AVCodec *pCodec;
pCodec= avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL){
fprintf(stderr, "Unsupported codec!\n");
return -1;
}
//打开解码器
if(avcodec_open2(pCodecCtx,pCodec, NULL)<0)
return -1;
//需要申请保存帧的buffer
AVFrame *pFrame, *pFrameRGB;
pFrame = avcodec_alloc_frame();
pFrameRGB = avcodec_alloc_frame();
if(pFrameRGB == NULL)
return -1;
//手动申请一个临时保存的buffer,这个要知道大小
uint8_t *buffer;
int numBytes;
numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
//这个av_malloc保证字节对齐
buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
//AVframe继承自(可能不是class实现,类似)AVPicture
//将PFrameRGB与buffer联系到一起
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
//下面开始从流中读取数据
int frameEnd;
AVPacket packet;
struct SwsContext *img_convert_ctx;//格式转换使用
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_video2(pCodecCtx,pFrame,&frameEnd,&packet);
}
//读取一帧
if(frameEnd){
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
if(img_convert_ctx == NULL){
fprintf(stderr, "Cannot initialize the conversion context!\n");
exit(1);
}
sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height,pFrameRGB->data, pFrameRGB->linesize);
if(++i<=5)
//对数据进行操作
SaveFrame(pFrameRGB,pCodecCtx->width, pCodecCtx->height, i);
}
}
av_free(buffer);
av_free(pFrameRGB);
av_free(pFrame);
avcodec_close(pCodecCtx);
av_close_input_file(pFormatCtx);
av_free_packet(&packet);
return 0;
}
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
FILE *pFile;
char szFilename[32];
int y;
//openfile
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile = fopen(szFilename, "wb");
if(pFile == NULL)
return;
//write header
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
//write pixel data
for(y=0; y<height; y++)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
//close file
fclose(pFile);
}