ffmpeg学习(三)——ffmpeg+SDL2 实现简单播放器
本篇实现基于ffmpeg动态库用测试程序播放本地文件和RTSP视频流。
参考文章:http://blog.csdn.net/leixiaohua1020/article/details/8652605,
http://blog.csdn.net/guanghua2_0beta/article/details/37578299
创建工程,参考上一篇文章:http://www.cnblogs.com/wenjingu/p/3990071.html,注意:下载SDL2库的开发版,lib文件放到lib文件夹下,dll放到debug文件夹下。
代码如下,在参考文章的基础上做了少量改动,主要是将ffmpeg老版本的部分函数在替换为2.4版本中的新函数。
#include "stdafx.h" #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavdevice/avdevice.h> #include <libavformat/avformat.h> #include <libavfilter/avfilter.h> #include <libavutil/avutil.h> #include <libswscale/swscale.h> #include <SDL2/SDL.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <math.h> #ifdef __cplusplus } #endif int ffplayer() { AVFormatContext *pFormatCtx; int i, videoindex; AVCodecContext *pCodecCtx; AVCodec *pCodec; //char filepath[]="F:\\Work\\ffmpegdemo\\Debug\\Wildlife.wmv"; char rtspUrl[] = "rtsp://admin:12345@192.168.10.76:554"; av_register_all();//注册组件 avformat_network_init();//支持网络流 pFormatCtx = avformat_alloc_context();//初始化AVFormatContext if(avformat_open_input(&pFormatCtx,/*filepath*/rtspUrl,NULL,NULL)!=0){//打开文件或网络流 printf("无法打开文件\n"); return -1; } if(avformat_find_stream_info(pFormatCtx, NULL)<0)//查找流信息 { printf("Couldn't find stream information.\n"); return -1; } videoindex=-1; for(i=0; i<pFormatCtx->nb_streams; i++) //获取视频流ID if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) { videoindex=i; break; } if(videoindex==-1) { printf("Didn't find a video stream.\n"); return -1; } pCodecCtx=pFormatCtx->streams[videoindex]->codec; pCodec=avcodec_find_decoder(pCodecCtx->codec_id);//查找解码器 if(pCodec==NULL) { printf("Codec not found.\n"); return -1; } if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)//打开解码器 { printf("Could not open codec.\n"); return -1; } AVFrame *pFrame,*pFrameYUV; pFrame=avcodec_alloc_frame();//存储解码后AVFrame pFrameYUV=avcodec_alloc_frame();//存储转换后AVFrame uint8_t *out_buffer; out_buffer=new uint8_t[avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)];//分配AVFrame所需内存 avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);//填充AVFrame //------------SDL初始化-------- if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1; } SDL_Window *screen = SDL_CreateWindow("RTSP Client Demo", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, pCodecCtx->width, pCodecCtx->height, SDL_WINDOW_RESIZABLE/* SDL_WINDOW_HIDDEN*/| SDL_WINDOW_OPENGL); if(!screen) { printf("SDL: could not set video mode - exiting\n"); return -1; } SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0); SDL_Texture* sdlTexture = SDL_CreateTexture( sdlRenderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height); SDL_Rect rect; //----------------------------- int ret, got_picture; static struct SwsContext *img_convert_ctx; int y_size = pCodecCtx->width * pCodecCtx->height; SDL_Event event; AVPacket *packet=(AVPacket *)malloc(sizeof(AVPacket));//存储解码前数据包AVPacket av_new_packet(packet, y_size); //输出一下信息----------------------------- printf("文件信息-----------------------------------------\n"); //av_dump_format(pFormatCtx,0,filepath,0); printf("-------------------------------------------------\n"); //------------------------------ while(av_read_frame(pFormatCtx, packet)>=0)//循环获取压缩数据包AVPacket { if(packet->stream_index==videoindex) { ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);//解码。输入为AVPacket,输出为AVFrame if(ret < 0) { printf("解码错误\n"); return -1; } if(got_picture) { //像素格式转换。pFrame转换为pFrameYUV。 img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); sws_freeContext(img_convert_ctx); //------------SDL显示-------- rect.x = 0; rect.y = 0; rect.w = pCodecCtx->width; rect.h = pCodecCtx->height; SDL_UpdateTexture( sdlTexture, &rect, pFrameYUV->data[0], pFrameYUV->linesize[0] ); SDL_RenderClear( sdlRenderer ); SDL_RenderCopy( sdlRenderer, sdlTexture, &rect, &rect ); SDL_RenderPresent( sdlRenderer ); //延时20ms SDL_Delay(20); //------------SDL----------- } } av_free_packet(packet); SDL_PollEvent(&event); switch( event.type ) { case SDL_QUIT: SDL_Quit(); exit(0); break; default: break; } } SDL_DestroyTexture(sdlTexture); delete[] out_buffer; av_free(pFrameYUV); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); return 0; } int _tmain(int argc, _TCHAR* argv[]) { ffplayer(); return 0; }
测试效果:
播放rtsp网络流
也可以播放本地文件,将rtspUrl替换为filepath即可。