流程:
解复用->解码->yuv->交给纹理->交给渲染器->播放。
其中前三步使用ffmpeg实现,后三步用sdl实现。
#include <stdio.h>
#include <SDL.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
// compatibility with newer API
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
#define av_frame_alloc avcodec_alloc_frame
#define av_frame_free avcodec_free_frame
#endif
int main(int argc, char *argv[]) {
int ret = -1;
AVFormatContext *pFormatCtx = NULL; //for opening multi-media file
int i, videoStream;
AVCodecContext *pCodecCtxOrig = NULL; //codec context
AVCodecContext *pCodecCtx = NULL;
struct SwsContext *sws_ctx = NULL;
AVCodec *pCodec = NULL; // the codecer
AVFrame *pFrame = NULL;
AVPacket packet;
int frameFinished;
float aspect_ratio;
AVPicture *pict = NULL;
SDL_Rect rect;
Uint32 pixformat;
//for render
SDL_Window *win = NULL;
SDL_Renderer *renderer = NULL;
SDL_Texture *texture = NULL;
//set defualt size of window
int w_width = 640;
int w_height = 480;
if(argc < 2) {
//fprintf(stderr, "Usage: command <file>\n");
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Usage: command <file>");
return ret;
}
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
//fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not initialize SDL - %s\n", SDL_GetError());
return ret;
}
//Register all formats and codecs
av_register_all();
// Open video file
if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0){
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open video file!");
goto __FAIL; // Couldn't open file
}
// Retrieve stream information
if(avformat_find_stream_info(pFormatCtx, NULL)<0){
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to find stream infomation!");
goto __FAIL; // Couldn't find stream information
}
// Dump information about file onto standard error
av_dump_format(pFormatCtx, 0, argv[1], 0);
// Find the first video stream
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){
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Din't find a video stream!");
goto __FAIL;// Didn't find a video stream
}
// Get a pointer to the codec context for the video stream
pCodecCtxOrig=pFormatCtx->streams[videoStream]->codec;
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtxOrig->codec_id);
if(pCodec==NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unsupported codec!\n");
goto __FAIL; // Codec not found
}
// Copy context
pCodecCtx = avcodec_alloc_context3(pCodec);
if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't copy codec context");
goto __FAIL;// Error copying codec context
}
// Open codec
if(avcodec_open2(pCodecCtx, pCodec, NULL)<0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open decoder!\n");
goto __FAIL; // Could not open codec
}
// Allocate video frame
pFrame=av_frame_alloc();
w_width = pCodecCtx->width;
w_height = pCodecCtx->height;
win = SDL_CreateWindow( "Media Player",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
w_width, w_height,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if(!win){
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create window by SDL");
goto __FAIL;
}
renderer = SDL_CreateRenderer(win, -1, 0);
if(!renderer){
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Renderer by SDL");
goto __FAIL;
}
pixformat = SDL_PIXELFORMAT_IYUV;
texture = SDL_CreateTexture(renderer,
pixformat,
SDL_TEXTUREACCESS_STREAMING,
w_width,
w_height);
// initialize SWS context for software scaling
sws_ctx = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
AV_PIX_FMT_YUV420P,
SWS_BILINEAR,
NULL,
NULL,
NULL
);
pict = (AVPicture*)malloc(sizeof(AVPicture));
avpicture_alloc(pict,
AV_PIX_FMT_YUV420P,
pCodecCtx->width,
pCodecCtx->height);
// Read frames and save first five frames to disk
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, &frameFinished, &packet);
// Did we get a video frame?
if(frameFinished) {
// Convert the image into YUV format that SDL uses
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height,
pict->data, pict->linesize);
SDL_UpdateYUVTexture(texture, NULL,
pict->data[0], pict->linesize[0],
pict->data[1], pict->linesize[1],
pict->data[2], pict->linesize[2]);
// Set Size of Window
rect.x = 0;
rect.y = 0;
rect.w = pCodecCtx->width;
rect.h = pCodecCtx->height;
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, &rect);
SDL_RenderPresent(renderer);
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
/*
SDL_Event event;
SDL_PollEvent(&event);
switch(event.type) {
case SDL_QUIT:
goto __QUIT;
break;
default:
break;
}
*/
}
__QUIT:
ret = 0;
__FAIL:
// Free the YUV frame
if(pFrame){
av_frame_free(&pFrame);
}
// Close the codec
if(pCodecCtx){
avcodec_close(pCodecCtx);
}
if(pCodecCtxOrig){
avcodec_close(pCodecCtxOrig);
}
// Close the video file
if(pFormatCtx){
avformat_close_input(&pFormatCtx);
}
if(pict){
avpicture_free(pict);
free(pict);
}
if(win){
SDL_DestroyWindow(win);
}
if(renderer){
SDL_DestroyRenderer(renderer);
}
if(texture){
SDL_DestroyTexture(texture);
}
SDL_Quit();
return ret;
}