=====================================================
最简单的基于FFmpeg的视频播放器系列文章列表:
100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)
最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)
最简单的基于FFmpeg的解码器-纯净版(不包含libavformat)
最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器
最简单的基于FFMPEG的Helloworld程序
=====================================================
本文补充记录《最简单的基于FFMPEG+SDL的视频播放器》中的两个例子:FFmpeg视频解码器和SDL像素数据播放器。这两个部分是从视频播放器中拆分出来的两个例子。FFmpeg视频解码器实现了视频数据到YUV数据的解码,而SDL像素数据播放器实现了YUV数据的显示。简而言之,原先的FFmpeg+SDL视频播放器实现了:
视频数据->YUV->显示器
FFmpeg视频解码器实现了:
视频数据->YUV
SDL像素数据播放器实现了:
YUV->显示器
FFmpeg视频解码器
源代码
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#include <stdio.h>
-
-
#define __STDC_CONSTANT_MACROS
-
-
#ifdef _WIN32
-
-
extern "C"
-
{
-
#include "libavcodec/avcodec.h"
-
#include "libavformat/avformat.h"
-
#include "libswscale/swscale.h"
-
#include "libavutil/imgutils.h"
-
};
-
#else
-
-
#ifdef __cplusplus
-
extern "C"
-
{
-
#endif
-
#include <libavcodec/avcodec.h>
-
#include <libavformat/avformat.h>
-
#include <libswscale/swscale.h>
-
#include <libavutil/imgutils.h>
-
#ifdef __cplusplus
-
};
-
#endif
-
#endif
-
-
-
int main(int argc, char* argv[])
-
{
-
AVFormatContext *pFormatCtx;
-
int i, videoindex;
-
AVCodecContext *pCodecCtx;
-
AVCodec *pCodec;
-
AVFrame *pFrame,*pFrameYUV;
-
unsigned char *out_buffer;
-
AVPacket *packet;
-
int y_size;
-
int ret, got_picture;
-
struct SwsContext *img_convert_ctx;
-
-
char filepath[]="Titanic.mkv";
-
-
FILE *fp_yuv=fopen("output.yuv","wb+");
-
-
av_register_all();
-
avformat_network_init();
-
pFormatCtx = avformat_alloc_context();
-
-
if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){
-
printf("Couldn't open input stream.\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++)
-
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;
-
}
-
-
pFrame=av_frame_alloc();
-
pFrameYUV=av_frame_alloc();
-
out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height,1));
-
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,
-
AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);
-
-
-
-
packet=(AVPacket *)av_malloc(sizeof(AVPacket));
-
-
printf("--------------- File Information ----------------\n");
-
av_dump_format(pFormatCtx,0,filepath,0);
-
printf("-------------------------------------------------\n");
-
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
-
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
-
-
while(av_read_frame(pFormatCtx, packet)>=0){
-
if(packet->stream_index==videoindex){
-
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
-
if(ret < 0){
-
printf("Decode Error.\n");
-
return -1;
-
}
-
if(got_picture){
-
sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
-
pFrameYUV->data, pFrameYUV->linesize);
-
-
y_size=pCodecCtx->width*pCodecCtx->height;
-
fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);
-
fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);
-
fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);
-
printf("Succeed to decode 1 frame!\n");
-
-
}
-
}
-
av_free_packet(packet);
-
}
-
-
-
while (1) {
-
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
-
if (ret < 0)
-
break;
-
if (!got_picture)
-
break;
-
sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
-
pFrameYUV->data, pFrameYUV->linesize);
-
-
int y_size=pCodecCtx->width*pCodecCtx->height;
-
fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);
-
fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);
-
fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);
-
-
printf("Flush Decoder: Succeed to decode 1 frame!\n");
-
}
-
-
sws_freeContext(img_convert_ctx);
-
-
fclose(fp_yuv);
-
-
av_frame_free(&pFrameYUV);
-
av_frame_free(&pFrame);
-
avcodec_close(pCodecCtx);
-
avformat_close_input(&pFormatCtx);
-
-
return 0;
-
}
运行结果
程序运行后,会解码下面的视频文件。
解码后的YUV420P数据被保存成了一个文件。使用YUV播放器设置宽高之后可以查看YUV内容。
SDL像素数据播放器
源代码
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#include <stdio.h>
-
-
extern "C"
-
{
-
#include "sdl/SDL.h"
-
};
-
-
const int bpp=12;
-
-
int screen_w=500,screen_h=500;
-
const int pixel_w=320,pixel_h=180;
-
-
unsigned char buffer[pixel_w*pixel_h*bpp/8];
-
-
-
-
#define REFRESH_EVENT (SDL_USEREVENT + 1)
-
-
#define BREAK_EVENT (SDL_USEREVENT + 2)
-
-
int thread_exit=0;
-
-
int refresh_video(void *opaque){
-
thread_exit=0;
-
while (!thread_exit) {
-
SDL_Event event;
-
event.type = REFRESH_EVENT;
-
SDL_PushEvent(&event);
-
SDL_Delay(40);
-
}
-
thread_exit=0;
-
-
SDL_Event event;
-
event.type = BREAK_EVENT;
-
SDL_PushEvent(&event);
-
-
return 0;
-
}
-
-
int main(int argc, char* argv[])
-
{
-
if(SDL_Init(SDL_INIT_VIDEO)) {
-
printf( "Could not initialize SDL - %s\n", SDL_GetError());
-
return -1;
-
}
-
-
SDL_Window *screen;
-
-
screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
-
screen_w, screen_h,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
-
if(!screen) {
-
printf("SDL: could not create window - exiting:%s\n",SDL_GetError());
-
return -1;
-
}
-
SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
-
-
Uint32 pixformat=0;
-
-
-
-
pixformat= SDL_PIXELFORMAT_IYUV;
-
-
SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer,pixformat, SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h);
-
-
FILE *fp=NULL;
-
fp=fopen("test_yuv420p_320x180.yuv","rb+");
-
-
if(fp==NULL){
-
printf("cannot open this file\n");
-
return -1;
-
}
-
-
SDL_Rect sdlRect;
-
-
SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video,NULL,NULL);
-
SDL_Event event;
-
while(1){
-
-
SDL_WaitEvent(&event);
-
if(event.type==REFRESH_EVENT){
-
if (fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp) != pixel_w*pixel_h*bpp/8){
-
-
fseek(fp, 0, SEEK_SET);
-
fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp);
-
}
-
-
SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w);
-
-
-
sdlRect.x = 0;
-
sdlRect.y = 0;
-
sdlRect.w = screen_w;
-
sdlRect.h = screen_h;
-
-
SDL_RenderClear( sdlRenderer );
-
SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect);
-
SDL_RenderPresent( sdlRenderer );
-
-
}else if(event.type==SDL_WINDOWEVENT){
-
-
SDL_GetWindowSize(screen,&screen_w,&screen_h);
-
}else if(event.type==SDL_QUIT){
-
thread_exit=1;
-
}else if(event.type==BREAK_EVENT){
-
break;
-
}
-
}
-
SDL_Quit();
-
return 0;
-
}
运行结果
程序运行后,会读取程序文件夹下的一个YUV420P文件,内容如下所示。
接下来会将YUV内容绘制在弹出的窗口中。
下载
Simplest FFmpeg Player
项目主页
SourceForge:https://sourceforge.net/projects/simplestffmpegplayer/
Github:https://github.com/leixiaohua1020/simplest_ffmpeg_player
开源中国:http://git.oschina.net/leixiaohua1020/simplest_ffmpeg_player
CSDN下载地址:http://download.csdn.net/detail/leixiaohua1020/8924321
本程序实现了视频文件的解码和显示(支持HEVC,H.264,MPEG2等)。
是最简单的FFmpeg视频解码方面的教程。
通过学习本例子可以了解FFmpeg的解码流程。
项目包含6个工程:
simplest_ffmpeg_player:标准版,FFmpeg学习的开始。
simplest_ffmpeg_player_su:SU(SDL Update)版,加入了简单的SDL的Event。
simplest_ffmpeg_decoder:一个包含了封装格式处理功能的解码器。使用了libavcodec和libavformat。
simplest_ffmpeg_decoder_pure:一个纯净的解码器。只使用libavcodec(没有使用libavformat)。
simplest_video_play_sdl2:使用SDL2播放YUV的例子。
simplest_ffmpeg_helloworld:输出FFmpeg类库的信息。