FFPlay是一个使用了 ffmpeg 和 sdl 库的、一个简单的可移植的媒体播放器。
来源:互联网 发布:淘宝优惠券不能用 编辑:程序博客网 时间:2024/06/05 10:42
视频播放器播放一个互联网上的视频文件,需要经过以下几个步骤:解协议,解封装,解码视音频,视音频同步。如果播放本地文件则不需要解协议,为以下几个步骤:解封装,解码视音频,视音频同步。他们的过程如图所示。
解协议的 作用,就是将流媒体协议的数据,解析为标准的相应的封装格式数据。视音频在网络上传播的时候,常常采用各种流媒体协议,例如HTTP,RTMP,或是 MMS等等。这些协议在传输视音频数据的同时,也会传输一些信令数据。这些信令数据包括对播放的控制(播放,暂停,停止),或者对网络状态的描述等。解协 议的过程中会去除掉信令数据而只保留视音频数据。例如,采用RTMP协议传输的数据,经过解协议操作后,输出FLV格式的数据。
解封装的 作用,就是将输入的封装格式的数据,分离成为音频流压缩编码数据和视频流压缩编码数据。封装格式种类很多,例如 MP4,MKV,RMVB,TS,FLV,AVI等等,它的作用就是将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。例如,FLV格式的数 据,经过解封装操作后,输出H.264编码的视频码流和AAC编码的音频码流。
解码的作用,就是将视频 /音频压缩编码数据,解码成为非压缩的视频/音频原始数据。音频的压缩编码标准包含AAC,MP3,AC-3等等,视频的压缩编码标准则包含 H.264,MPEG2,VC-1等等。解码是整个系统中最重要也是最复杂的一个环节。通过解码,压缩编码的视频数据输出成为非压缩的颜色数据,例如 YUV420P,RGB等等;压缩编码的音频数据输出成为非压缩的音频抽样数据,例如PCM数据。
视音频同步的作用,就是根据解封装模块处理过程中获取到的参数信息,同步解码出来的视频和音频数据,并将视频音频数据送至系统的显卡和声卡播放出来。
(以上内容摘自雷霄骅博士的博客http://blog.csdn.net/leixiaohua1020/article/details/18893769)
此处暂时只处理本地视频文件的视频播放,所以涉及的过程如下:
因此,如下Code为实现一个最简视频播放器
// gcc myFFMpeg.c -o myFFMpeg.o -lSDL2main -lSDL2 -lavformat -lavcodec -lavutil -lm -lz -lswscale 编译命令#include <stdio.h>#define __STDC_CONSTANT_MACROS//Linux...#ifdef __cplusplusextern "C"{#endif#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libswscale/swscale.h>#include <SDL2/SDL.h>#include <libavutil/imgutils.h>#ifdef __cplusplus};#endifint 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[]="test.mp4"; //SDL--------------------------- int screen_w=0,screen_h=0; SDL_Window *screen; SDL_Renderer* sdlRenderer; SDL_Texture* sdlTexture; SDL_Rect sdlRect; FILE *fp_yuv; 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)); //Output Info----------------------------- av_dump_format(pFormatCtx,0,filepath,0); 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); if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1; } screen_w = pCodecCtx->width; screen_h = pCodecCtx->height; //SDL 2.0 Support for multiple windows screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, SDL_WINDOW_OPENGL); if(!screen) { printf("SDL: could not create window - exiting:%s\n",SDL_GetError()); return -1; } sdlRenderer = SDL_CreateRenderer(screen, -1, 0); sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,pCodecCtx->width,pCodecCtx->height); sdlRect.x=0; sdlRect.y=0; sdlRect.w=screen_w; sdlRect.h=screen_h; //SDL End---------------------- 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); //SDL--------------------------- SDL_UpdateYUVTexture(sdlTexture, &sdlRect, pFrameYUV->data[0], pFrameYUV->linesize[0], pFrameYUV->data[1], pFrameYUV->linesize[1], pFrameYUV->data[2], pFrameYUV->linesize[2]); SDL_RenderClear( sdlRenderer ); SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect); SDL_RenderPresent( sdlRenderer ); //SDL End----------------------- //Delay 40ms SDL_Delay(40); } } av_free_packet(packet); } //flush decoder //FIX: Flush Frames remained in Codec 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); //SDL--------------------------- SDL_UpdateTexture( sdlTexture, &sdlRect, pFrameYUV->data[0], pFrameYUV->linesize[0] ); SDL_RenderClear( sdlRenderer ); SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect); SDL_RenderPresent( sdlRenderer ); //SDL End----------------------- //Delay 40ms SDL_Delay(40); } sws_freeContext(img_convert_ctx); SDL_Quit(); av_frame_free(&pFrameYUV); av_frame_free(&pFrame); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); return 0;}
FFPlay主要结构如下:
整体结构分为三个线程read_thread,video_thread和main的主线程:
read_thread:
作用:打开待播文件,读取视频数据,解封装,
avformat_open_input:打开待读视频文件
av_find_best_stream:找到视频流
stream_component_open:打开视频流
av_read_frame:获得一帧视频的压缩数据
video_thread:
作用:从packet_queue中获取packet,然后进行解码,将解码完的数据入队frame_queue
avcodec_decode_video2:解码
main:
作用:事件监听线程,对UI的操作做出反应
收到定周期的refresh_loop_wait_event事件,将解码后的画面描画到surface上,形成视频图像。
三个线程通过2个队列进行数据的交互:
packet_queue的操作
typedef struct PacketQueue { MyAVPacketList *first_pkt, *last_pkt; int nb_packets; int size; int abort_request; int serial; SDL_mutex *mutex; SDL_cond *cond;} PacketQueue;
入队操作的如下图所示:
http://www.cnblogs.com/bingqili/p/5919025.html
- FFPlay是一个使用了 ffmpeg 和 sdl 库的、一个简单的可移植的媒体播放器。
- ffmpeg+sdl教程----------编写一个简单的播放器1
- ffmpeg+sdl教程----------编写一个简单的播放器1
- ffmpeg+sdl教程----------编写一个简单的播放器1
- ffmpeg+sdl教程----------编写一个简单的播放器1
- 使用ffmpeg+SDL的简单播放器
- 一个跨平台的媒体播放器
- 一个简单的使用SDL播放YUV文件的示例
- 一个简单的使用SDL播放YUV文件的示例
- 一个基于Gstreamer的Linux下的简单媒体播放器
- Baresip 是一个可移植的模块化的 SIP 用户代理,支持音频和视频。
- ffmpeg+sdl教程----编写一个简单的播放器6(其他的时钟同步方式)
- ffmpeg+sdl教程----编写一个简单的播放器6(其他的时钟同步方式)
- ffmpeg+sdl教程----编写一个简单的播放器6(其他的时钟同步方式)
- 使用GStreamer的插件playbin开发一个简单的媒体播放器 视频与音乐
- Ffmpeg学习进阶二----ffplay的使用和分析
- ffmpeg、ffplay、ffprobe、rtmpdump的安装和使用
- [ffmpeg]FFPLAY的原理
- mysql 导出前缀相同表小脚本
- LeetCode-521 Longest Uncommon Subsequence I 最长不公共子串
- Shiro框架Ini配置(四)
- shell——3(网站数据备份注意事项)
- 新增数据表或字段
- FFPlay是一个使用了 ffmpeg 和 sdl 库的、一个简单的可移植的媒体播放器。
- hdu 1281 棋盘游戏
- 创建序列
- ajax 基础例子
- 【Hadoop--03】HDFS写文件
- React组件中对a标签的href的带变量拼接
- win10 裝在NET Framework 3.5(包括.NET 2.0和3.0)
- 连接数据库Mysql
- css margin的相关属性,问题及应用