创建播放窗口
SDL_Surface *screen = NULL; screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0); if (!screen) { fprintf(stderr,"SDL: could not set video node - exiting "); exit(1); }
转化yuv
SDL_ SetVideoMode设置具有指定width,height和bitsperpixel的视频模式。从SDL1.2.10开始。如果width和height都为0,他将使用当前视频模式(或桌面模式,如果没有设置模式)的宽度和高度。
现在我们在屏幕上创建一个 YUV 的播放平面,以便我们可以输入视频,并设置我们的 SWSContext 将图像数据转换为YUV420 ,YUV 是包含原图像最完整的格式,没有做压缩,减损。
bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen); //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 );
显示图片
在上一节中我们使用函数将每一帧图像都保存下来了,这一节将替换那个函数,改为将每一帧图像都转码送入 sdl 播放。
if (frameFinished) { 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]; //Convert the image from its native format to RGB sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, //pFrameRGB->data, //pFrameRGB->linesize pict.data, pict.linesize ); SDL_UnlockYUVOverlay(bmp); rect.x = 0; rect.y = 0; rect.w = pCodecCtx->width; rect.h = pCodecCtx->height; SDL_DisplayYUVOverlay(bmp,&rect); SDL_Delay(40); }
本节代码在开始的时候使用了 SDL_LockYUVOverlay ,将 bmp 給锁定,因为此时要播放它,如果一个线程在播放它,另一个线程在处理它,修改它,就会出错,在完成播放后,将锁定取消。
这里的视频我按照25帧每秒,所以每隔40ms就暂停一下,如果将时间修改得太短,视频就在飞速前进,体验就不好了。
清理内存
SDL_Event event; av_free_packet(&packet); SDL_PollEvent(&event); switch(event.type) { case SDL_QUIT: { SDL_Quit(); exit(0); break; } default: { break; } }
源码:
#include<stdio.h> #include<libavcodec/avcodec.h> #include<libavformat/avformat.h> #include<libswscale/swscale.h> #include<SDL.h> #include<SDL_thread.h> #ifdef __MINGW32__ #undef main /*Prevents SDL from overriding main()*/ #endif int main(int argc, char *argv[]) { AVFormatContext *pFormatCtx = NULL; AVCodecContext *pCodecCtx = NULL; AVCodec *pCodec = NULL; AVFrame *pFrame = NULL; AVPacket packet; int i,videoStream; int frameFinished; AVDictionary *optionsDict = NULL; struct SwsContext *sws_ctx = NULL; SDL_Overlay *bmp = NULL; SDL_Surface *screen = NULL; SDL_Rect rect; SDL_Event event; if (argc < 2) { printf("Please provide a movie file "); return -1; } //register all formats and codecs av_register_all(); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { fprintf(stderr,"Could not initialize SDL - %s ",SDL_GetError()); exit(1); } //open video file if (avformat_open_input(&pFormatCtx,argv[1], NULL, NULL) != 0) { return -1; //couldn't open file } //retrieve stream information if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { return -1; //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) { return -1; //Don't find a video stream } //Get a pointer to the codec context for the video stream pCodecCtx = pFormatCtx->streams[videoStream]->codec; //Find the decoder for the video stream pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL) { fprintf(stderr,"Unsupported codec! "); return -1; //Codec not found } //open codec if (avcodec_open2(pCodecCtx, pCodec, &optionsDict) < 0) { return -1; //Could not open codec } //Allcocate an AVFrame structure pFrame = av_frame_alloc(); //pFrameRGB = av_frame_alloc(); #ifdef __DARWIN__ screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0); #else screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0); #endif if (!screen) { fprintf(stderr,"SDL: could not set video node - exiting "); exit(1); } //Allocate a place to put our YUV image on that screen bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen); //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 ); //Assign appropriate parts of buffer to image planes in pFrameRGB //Note that pFrameRGB is an AVFrame, but AVFrame is a superset // of AVFPicture //Read frames and save first five frames to disk i = 0; while (av_read_frame(pFormatCtx, &packet) >= 0) { //Is this a packet from video stream if (packet.stream_index == videoStream) { //decode video frame avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); //Did wo get a video frame if (frameFinished) { 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]; //Convert the image from its native format to RGB sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, //pFrameRGB->data, //pFrameRGB->linesize pict.data, pict.linesize ); SDL_UnlockYUVOverlay(bmp); rect.x = 0; rect.y = 0; rect.w = pCodecCtx->width; rect.h = pCodecCtx->height; SDL_DisplayYUVOverlay(bmp,&rect); SDL_Delay(40); } } //Free the packet that was allocated by av_read_frame av_free_packet(&packet); SDL_PollEvent(&event); switch(event.type) { case SDL_QUIT: { SDL_Quit(); exit(0); break; } default: { break; } } } //Free the YUV frame av_free(pFrame); //Close the codec avcodec_close(pCodecCtx); //Close the video file avformat_close_input(&pFormatCtx); return 0; }
Makefile
DIR_INC = -I/usr/local/include DIR_LIB = -L/usr/local/lib LIBS = -lavformat -lavcodec -lva-x11 -lva -lxcb-shm -lxcb-xfixes -lxcb-render -lxcb-shape -lxcb -lX11 -lasound -lz -lswresample -lswscale -lavutil -lm -pthread `sdl-config --cflags --libs` FLAGS = -Wall -ggdb project : project.c gcc project.c ${FLAGS} ${DIR_INC} ${DIR_LIB} ${LIBS} -o project .PHONY:clean clean: rm project