写一个播放器,mplayer和vlc都是很好的参考,相对来说vlc的代码更容易看一点。

最简单的方法是用ffmpeg解码,SDL作为显示前端,这样几十行代码就可以构建一个简易的播放器出来。

SDL是跨平台的多媒体开发包www.libsdl.org去官网下载自己对应开发工具的开发包,并用自己喜欢的开发工具设置好头文件和lib文件。

同样配置好ffmpeg的头文件和lib文件,主要是

-lavutil -lavformat -lavcodec -lavdevice -lSDL.dll -lSDLmain

然后编译下面这几行代码。

//myPlayer.c
#include <avcodec.h>
#include <avformat.h>

#include <SDL.h>
#include <SDL_thread.h>

#ifdef __MINGW32__
#undef main /* Prevents SDL from overriding main() */
#endif

#include <stdio.h>

int main(int argc, char *argv[]) {
AVFormatContext *pFormatCtx;
int i, videoStream;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame;
AVPacket packet;
int frameFinished;
float aspect_ratio;

SDL_Overlay *bmp;
SDL_Surface *screen;
SDL_Rect rect;
SDL_Event event;

if(argc < 2) {
fprintf(stderr, “Usage:myPlayer xx.avi 目前不支持rmvb之类”);
exit(1);
}
//注册解码器
av_register_all();
//初始化SDL
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
exit(1);
}

//打开文件
if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
return -1;

//获取流信息
if(av_find_stream_info(pFormatCtx)<0)
return -1;

// Dump文件信息
dump_format(pFormatCtx, 0, argv[1], 0);

//找到第一个视频流
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
videoStream=i;
break;
}
if(videoStream==-1)
return -1; //没有视频流

//获取视频流的AVCodecContext
pCodecCtx=pFormatCtx->streams[videoStream]->codec;

//获取视频流的解码器
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL) {
return -1;
}

// 打开解码器
if(avcodec_open(pCodecCtx, pCodec)<0)
return -1;

// 分配 video frame
pFrame=avcodec_alloc_frame();

//创建一个 screen。SDL用screen显示视频。

screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 32, 0);

if(!screen) {
exit(1);
}

//创建一个显示到screen的图像
bmp = SDL_CreateYUVOverlay(pCodecCtx->width,
pCodecCtx->height,
SDL_YV12_OVERLAY,
screen);

i=0;
while(av_read_frame(pFormatCtx, &packet)>=0) {
if(packet.stream_index==videoStream) {
// 如果这个packet是视频流的包,则解码之
avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
packet.data, packet.size);

//得到视频帧?
if(frameFinished) {
SDL_LockYUVOverlay(bmp); //锁定sdl的播放表面

AVPicture pict;//avpicture是avframe的子集
pict.data[0] = bmp->pixels[0];//让准备显示到屏幕的bmp与pict共用YUV数据
pict.data[1] = bmp->pixels[2];
pict.data[2] = bmp->pixels[1];

pict.linesize[0] = bmp->pitches[0];
pict.linesize[1] = bmp->pitches[2];
pict.linesize[2] = bmp->pitches[1];

// 把图像转到sdl使用的yuv格式。即yuv420p。
img_convert(&pict, PIX_FMT_YUV420P,
(AVPicture *)pFrame, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height);

SDL_UnlockYUVOverlay(bmp); //释放overlay准备显示。

rect.x = 0;
rect.y = 0;
rect.w = pCodecCtx->width;
rect.h = pCodecCtx->height;
SDL_DisplayYUVOverlay(bmp, &rect);//显示

}
}

// 善后
av_free_packet(&packet);
SDL_PollEvent(&event);
switch(event.type) {
case SDL_QUIT:
SDL_Quit();
exit(0);
break;
default:
break;
}

}

av_free(pFrame);
avcodec_close(pCodecCtx);
av_close_input_file(pFormatCtx);

return 0;
}

我用Mingw版的SDL开发包和./configure -enable-memalign-hack参数编译的ffmpeg静态库在SlickEdit+MingW下编译成功。VC的话可能要修改一些语句。因为没有处理帧同步和音频流,最后生成的是一个快速播放视频并且没有声音的播放器,考虑到没有超过一百行的代码量,的确很惊人。

更多参考http://www.dranger.com/ffmpeg/

对我来说视频播放器是生活中比较重要的东西。

现在比较流行的播放器了解的有一下几种,

  1. MPC系列,国内的暴风风暴之类的山寨机都是这个系列的,它们一般的解码都是通过ffdshow来做的,而ffdshow用到了ffmpeg。
  2. Mplayer, VLC这两个比较相似,都有成熟的开源社区支持并因此很有技术内涵,也都是从Linux移植到Windows的,缺点是官方版的GUI界面太简陋,影响看片的心情,好在有众多不同的编译版可以下载。
  3. 来自韩国的Kmplayer和GOM。Kmp功能比较多,但大部分用不到,属于华丽型的,用过一段时间,直到发现很多影片它的播放效果明显不如Mplayer,才把它换了,GOM貌似也是一个类型的,而且因为用了ffmpeg而没有开源被mplayer社区列进了黑名单,比较囧。

视频播放器最重要的功能是支持尽可能多的文件格式,尽可能地还原出视频文件的真实画质,这一点通过ffmpeg再加上real的解码器基本就可以做的很好了。

其次就是灵活的字幕功能,看美剧日漫是学外语的最佳途径,播放器应该考虑到这方面的需要,能提供灵活的显示方式,如快速切换显示语种或双语同时显示(KMP有这个功能),能提供上下文而不只是当前在说的一句话,能根据字幕内容定位播放,Mplayer有根据字幕快进后退一句话的功能,但不是很好用,最好能像foobar的乐辞插件一样,用一个窗口显示对话剧本和时间轴,拖动时间轴到某句台词上来定位播放,多加一个窗口肯定会影响观看效果,但这个功能的目标群是学外语的人,并且可以用快捷键在需要的时候再呼出来,应该很有用。

另外固定间隔的快进也不是很人性化,人们总是觉得某个场景没有意思,才会快进跳过的,而不是觉得特定的十几秒没有意思,而场景的切换一般会伴随着大面积的背景变换,如从室内走到室外,可以根据分析视频的总体背景确定场景的切换,在用户要求快进的时候自动切换到下一幕,不过这种检测可能对一幕一幕拍的很有条理的肥皂剧比较适合,要是碰到从头到尾灰蒙蒙一片的另类电影效果肯定不理想。这时候可以考虑另外一个比较容易确定的信息,音频。通过检测演员说话的间隔,也可以大概的猜测剧情的进展(除非是那种一直有一个画外音喋喋不休的片子)。把这些手段结合起来,再设定一个跳跃的最大范围,保持检测不准确时快进功能的可用性,或者用一个内部的检测准确度阀值来控制智能快进功能的使用,当算法自己都觉得不太准确的时候就用普通基于固定秒数的快进方式。显然这些方法都需要预先读取和分析视频文件信息, 限制非常大甚至不太现实,最好的解决方法是弄一个新的视频格式标准,在压制的时候直接把相关信息写人文件,扯远了,总之还是觉得用看着字幕剧本拖时间轴的方法比较实用而且靠谱。