参考资料:
1.雷博博客
继续ffmpeg学习之路。。。
前面写了将音频解码为PCM并通过SDL2进行播放的代码,接下来则是需要对视频进行解码为YUV并通过SDL2进行播放,然后再音视频同步播放,有余力的话,通过QT制作一个简单的音视频播放器,计划好多,一步一步来。。。
视频的解码和音频的解码流程一致,不同的地方也就是解码接口不一样,还有转换部分不一样。
1)解码流程
整个解码流程采用伪代码大致如下:
初始化复用器和解复用器—>获取输入文件的一些信息—->查找解码器并打开—->读出视频数据并解码—>将解码后的数据写入文件中—>结束,做去初始化工作
2)转换
音频是采用SwrContext结构体对音频数据进行重采样,而视频则是因为解码后的YUV数据中会存储一些无效像素,因此每行像素的长度linesize并不等于实际的宽度width,所以需要用sws_scale()进行转换,去除无用数据,使 width和linesize取值相等。
转换使用的是FFMPEG中的libswsscale库,libswscale是一个主要用于处理图片像素数据的类库。可以完成图片像素格式的转换,图片的拉伸等工作。
在这份代码中,主要使用了三个函数:
sws_getContext/sws_scale/sws_freeContext
sws_getContext
函数原型:
struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param);
初始化SwsContext结构体,里面的参数分为src 和 dst两部分,根据我们的需要进行填充
sws_scale
函数原型:
int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],
const int srcStride[], int srcSliceY, int srcSliceH,
uint8_t *const dst[], const int dstStride[]);
处理图像数据,转换像素。
sws_freeContext:
函数原型:
void sws_freeContext(struct SwsContext *swsContext);
释放SwsContext结构体。
3)linux下编译命令
gcc test.cpp -o test -I /usr/local/include -L /usr/local/lib -lavformat -lavcodec -lavutil -lswscale
4)代码
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
};
#define debug_msg(fmt, args ...) printf("--->[%s,%d] " fmt "\n\n", __FUNCTION__, __LINE__, ##args)
int main(int argc, char* argv[])
{
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVPacket *packet;
AVFrame *pFrame,*pFrameYUV;
struct SwsContext *img_convert_ctx;
uint8_t *out_buffer;
int ret, got_picture;
int i = 0;
int videoindex = -1;
char filepath[]="Titanic.ts";
FILE *fd_yuv = fopen("output.yuv", "wb+");
av_register_all();
pFormatCtx = avformat_alloc_context();
if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL) != 0)
{
debug_msg("Couldn't open input stream.\n");
return -1;
}
if(avformat_find_stream_info(pFormatCtx,NULL) < 0)
{
debug_msg("Couldn't find stream information.\n");
return -1;
}
for(i = 0; i < pFormatCtx->nb_streams; i++)
{
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoindex=i;
break;
}
}
if(videoindex == -1)
{
debug_msg("Didn't find a video stream.\n");
return -1;
}
pCodecCtx = pFormatCtx->streams[videoindex]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec == NULL)
{
debug_msg("Codec not found.\n");
return -1;
}
if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
{
debug_msg("Could not open codec.\n");
return -1;
}
packet=(AVPacket *)av_malloc(sizeof(AVPacket));
pFrame = av_frame_alloc();
pFrameYUV = av_frame_alloc();
out_buffer=(uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
debug_msg("----------->width is %d, height is %d, size is %d,\n",
pCodecCtx->width, pCodecCtx->height,
avpicture_get_size( pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height));
//av_dump_format(pFormatCtx,0,filepath,0);
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
while(av_read_frame(pFormatCtx, packet)>=0)
{
if(packet->stream_index==videoindex)
{
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
if(ret < 0)
{
debug_msg("Decode Error.\n");
return -1;
}
if(got_picture)
{
sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize);
fwrite(pFrameYUV->data[0],1, (pCodecCtx->width*pCodecCtx->height),fd_yuv);
fwrite(pFrameYUV->data[1],1, (pCodecCtx->width*pCodecCtx->height)/4,fd_yuv);
fwrite(pFrameYUV->data[2],1, (pCodecCtx->width*pCodecCtx->height)/4,fd_yuv);
//fwrite(out_buffer,1, (pCodecCtx->width*pCodecCtx->height)*3/2,fd_yuv);
}
}
av_free_packet(packet);
}
fclose(fd_yuv);
sws_freeContext(img_convert_ctx);
av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}