/******************
程序功能:
内存方式读取视频流,对视频流自定义分辨率进行播放
******************/
#include "sdl2\SDL.h"
extern "C"
{
#include "libavformat\avformat.h"
#include "libswscale\swscale.h"
};
FILE *fp = NULL;
int read_buffer(void *opaque,uint8_t *buf,int buf_size)
{
if(!feof(fp))
{
int true_size = fread(buf,1,buf_size,fp);
return true_size;
}
else
return -1;
}
/*
缩放一帧视频画面的分辨率(适用于YUV420P类型的视频帧):
pCodecCtx -- 保存了相应流的详细编码信息的结构体(如视频的宽、高、编码类型等)
pFrame -- 用于保存数据帧的数据结构(分辨率改变前)
pNewFrame -- 用于保存数据帧的数据结构(分辨率改变后)
NewWidth -- 新分辨率的宽度
NewHeight -- 新分辨率的高度
*/
void ResolutionChange(AVCodecContext *pCodecCtx,AVFrame *pFrame,AVFrame *pNewFrame,int pNewWidth,int pNewHeight)
{
pNewFrame->linesize[0] = pNewWidth;
pNewFrame->linesize[1] = pNewWidth/2;
pNewFrame->linesize[2] = pNewWidth/2;
//用 sws_getContext函数 得到 sws_scale函数 运行的上下文,之后用 sws_scale函数 将图形缩放
struct SwsContext *pSwsCtx = NULL;
pSwsCtx = sws_getContext(pCodecCtx->width,pCodecCtx->height,PIX_FMT_YUV420P,pNewWidth,pNewHeight,PIX_FMT_YUV420P,SWS_SINC,NULL,NULL,NULL);
if(pSwsCtx == NULL)
return ;
sws_scale(pSwsCtx,pFrame->data,pFrame->linesize,0,pCodecCtx->height,pNewFrame->data,pNewFrame->linesize);
sws_freeContext(pSwsCtx);
}
int main(int argc,char** argv)
{
av_register_all();
AVFormatContext *pFormatCtx = NULL;
pFormatCtx = avformat_alloc_context();
char filepath[]="test.flv";
fp = fopen(filepath,"rb");
if(fp == NULL)
exit(1);
unsigned char *aviobuffer = (unsigned char *)av_malloc(32768);//32K
AVIOContext *avio = avio_alloc_context(aviobuffer,32768,0,NULL,read_buffer,NULL,NULL);
pFormatCtx->pb = avio;
if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL) != 0)
exit(1);
if(avformat_find_stream_info(pFormatCtx,NULL) < 0)
exit(1);
int videoIndex = -1;
for(int i=0;i<pFormatCtx->nb_streams;i++)
{
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoIndex = i;
break;
}
}
if(videoIndex == -1)
exit(1);
AVCodecContext *pCodecCtx;
pCodecCtx = pFormatCtx->streams[videoIndex]->codec;
AVCodec *pCodec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec == NULL)
exit(1);
if(avcodec_open2(pCodecCtx,pCodec,NULL) < 0)
exit(1);
AVFrame *pFrame,*pNewFrame;
pFrame = av_frame_alloc();
pNewFrame = av_frame_alloc();
if((pFrame == NULL) || (pNewFrame == NULL))
exit(1);
//自定义分辨率
int NewWidth = 640,NewHeight = 480;
int PictureSize = avpicture_get_size(PIX_FMT_YUV420P,NewWidth,NewHeight);
uint8_t* buffer = (uint8_t *)av_malloc(PictureSize*sizeof(uint8_t));
avpicture_fill((AVPicture *)pNewFrame,buffer,PIX_FMT_YUV420P,NewWidth,NewHeight);
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER))
exit(1);
SDL_Window *window = nullptr;
window = SDL_CreateWindow("MyPlayer",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,NewWidth,NewHeight,SDL_WINDOW_OPENGL);
if(!window)
exit(1);
SDL_Renderer *renderer = nullptr;
renderer = SDL_CreateRenderer(window,-1,0);
if(renderer == nullptr)
exit(1);
SDL_Texture *texture = nullptr;
texture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_YV12,SDL_TEXTUREACCESS_STREAMING,NewWidth,NewHeight);
SDL_Rect rect;
SDL_Event event;
AVPacket *packet=(AVPacket *)malloc(sizeof(AVPacket));
av_new_packet(packet,PictureSize);
int frameFinished;
while(av_read_frame(pFormatCtx,packet) >= 0)
{
if(packet->stream_index == videoIndex)
{
if(avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,packet) >= 0)
{
if(frameFinished)
{
ResolutionChange(pCodecCtx,pFrame,pNewFrame,NewWidth,NewHeight);
rect.x = 0;
rect.y = 0;
rect.w = NewWidth;
rect.h = NewHeight;
SDL_UpdateYUVTexture(texture,&rect,pNewFrame->data[0],pNewFrame->linesize[0],pNewFrame->data[1],pNewFrame->linesize[1],pNewFrame->data[2],pNewFrame->linesize[2]);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer,texture,&rect,&rect);
SDL_RenderPresent(renderer);
SDL_Delay(20);
}
}
else
exit(1);
}
av_free_packet(packet);
SDL_PollEvent(&event);
switch(event.type)
{
case SDL_QUIT:
SDL_Quit();
exit(1);
break;
default:
break;
}
}
SDL_DestroyTexture(texture);
SDL_Quit();
av_free(buffer);
av_frame_free(&pNewFrame);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}