本帖最后由 weyl 于 2013-7-5 02:24 编辑
播放视频并不是什么难事,在游戏窗口中播放视频就有点麻烦了。实际上一个游戏并不需要很复杂的视频功能,只需能够播放或者跳过就可以了。
虽然sdl自己带了一个smpeg,不过说实话有点废。那么我们需要引入一些外部的库来实现更复杂的功能。
这几天试着给猪3增加了一个开头动画(山寨来的,不是原创,哪位熟悉动画制作的可以友情支援)。至于在游戏的窗口播放,现在找到了两个解决方案:
1. bass_dshow(xVideo)
这个也属于以前介绍过的bass音频库的插件之一,依赖bass和bassmix,本质是使用directx show,因此只能用于windows。
关键的问题在于,这个库的使用简单得有点过分。一般来说,我们想画个图,总要弄个内部结构先画上,然后把我们内部的图弄到游戏窗口上,但是xVideo完全不考虑这个,它直接抢占程序的窗口来放视频,而不管这个窗口的来历,这个窗口可以是一个Form,SDL窗口,甚至是带有OpenGL属性的窗口(这个太狠了)。
而简单地说,用xVideo的库来放一个视频,只需4到5个语句,常用的如下:- xVideo_Init(0, 0);
- xVideo_StreamCreateFile 用来创建视频流
- xVideo_ChannelSetAttribute 设置音量,声道平衡等
- xVideo_ChannelGetInfo 可以获取视频的属性,如尺寸等
- xVideo_ChannelResizeWindow 设置视频的尺寸和位置
- xVideo_StreamFree
- xVideo_Free
复制代码 这个库公开的版本1.22是非商业应用免费,但是不支持回调。新的版本可以下载到未注册的,注册过的似乎已经不提供免费授权了。
在Windows上做游戏的话,推荐使用,因为简单有效。
2. ffmpeg
这个库只能用于解码,无法用于播放。一般编写播放器使用这个,做游戏用就过于复杂了。因为游戏用的视频一般不长,所以我这里用了一个很简单的方式,即先解码完整音频,再解码视频。注意,这只是一个偷懒的方案,并不是一个好的处理手段。
特别地,ffmpeg与bass结合播放音频,目前网络上的资料非常少见,至少我是没查到。以下代码是我自己摸索出来的:
初始化部分,音频视频都需要:- av_register_all();
- if av_open_input_file(pFormatCtx, PChar(filename), nil, 0, nil) = 0 then
- begin
- av_find_stream_info(pFormatCtx);
- ..............................
复制代码 解码完整音频部分,原理是创建Push模式的音频流再向其中压入数据:- audioStream := -1;
- for i := 0 to pFormatCtx.nb_streams - 1 do
- begin
- if pFormatCtx.streams[i].codec.codec_type = AVMEDIA_TYPE_AUDIO then
- begin
- audioStream := i;
- break;
- end;
- end;
- pCodecCtxA := pFormatCtx.streams[audioStream].codec;
- pCodecA := avcodec_find_decoder(pCodecCtxA.codec_id);
- avcodec_open(pCodecCtxA, pCodecA);
- bufferA := av_mallocz(AVCODEC_MAX_AUDIO_FRAME_SIZE);
- //创建push模式的bass音频流
- A := BASS_StreamCreate(pCodecCtxA.sample_rate, pCodecCtxA.channels, 0, STREAMPROC_PUSH, nil);
- //将找到的音频流解压并压入
- while True do
- begin
- if av_read_frame(pFormatCtx, packet) < 0 then
- break;
- if (packet.stream_index = audioStream) then
- begin
- sizeA := AVCODEC_MAX_AUDIO_FRAME_SIZE;
- avcodec_decode_audio2(pCodecCtxA, pSmallint(bufferA), sizeA, packet.Data, packet.size);
- BASS_StreamPutData(A, bufferA, sizeA);
- end;
- av_free_packet(@packet);
- end;
- av_free(bufferA);
- BASS_ChannelSetAttribute(A, BASS_ATTRIB_VOL, VOLUME / 100.0);
复制代码 上面的代码获得的音频流A就可以用bass播放了。
至于解码视频部分,网上的资料很多,配合SDL输出的非常多,配合opengl的比较少,其实关键的地方是转换格式用的那几个参数必须正确。
比如用SDL的YUV输出的话,最关键的两句是:- img_convert_ctx :=
- sws_getContext(pCodecCtx.Width, pCodecCtx.Height, pCodecCtx.pix_fmt, pCodecCtx.Width,
- pCodecCtx.Height, PIX_FMT_YUV420P, SWS_BICUBIC, nil, nil, nil);
- sws_scale(img_convert_ctx, @pFrame.Data[0], @pFrame.linesize[0], 0, pCodecCtx.Height,
- @pict.Data[0], @pict.linesize[0]);
复制代码 这个网上都说得很清楚,只是前面的指针可能会把人弄糊涂。
opengl用下面的语句:- img_convert_ctx :=
- sws_getContext(pCodecCtx.Width, pCodecCtx.Height, pCodecCtx.pix_fmt, pCodecCtx.Width,
- pCodecCtx.Height, PIX_FMT_RGB24, SWS_BICUBIC, nil, nil, nil);
- sws_scale(img_convert_ctx, @pFrame.Data[0], @pFrame.linesize[0], 0, pCodecCtx.Height,
- @pFrameRGB.Data[0], @pFrameRGB.linesize[0]);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, pCodecCtx.Width, pCodecCtx.Height,
- 0, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB.Data[0]);
复制代码 关键的地方是PIX_FMT_RGB24,GL_RGB这几个参数不能选错。
|