您的位置:首页 > 其它

ffmpeg学习(三)——ffmpeg+SDL2 实现简单播放器

2014-09-24 11:45 253 查看
本篇实现基于ffmpeg动态库用测试程序播放本地文件和RTSP视频流。

参考文章:/article/1379137.html

/article/1864101.html

创建工程,参考上一篇文章:/article/5949996.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即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: