在安卓设备上平滑播放连续的mp4视频序列

4
我希望能在我的安卓设备上流畅地播放(渲染到表面)两个或更多连续的MP4视频序列(每个都存储在设备上的不同文件中,也许在启动时不存在),以使观众感受到只观看了一个连续的视频,而没有停顿、闪烁等现象。首先,仅针对我的Nexus 7平板电脑实现这一点就足够了。 为了显示单个视频,我一直在使用MediaCodec API,类似于http://dpsm.wordpress.com/2012/07/28/android-mediacodec-decoded/,它运行良好。只有在第一个序列结束后(调用decoder.stopdecoder.release),创建并配置第二个解码器,才能看到混合效果。为了实现两个不同视频序列之间的平滑淡入淡出,我考虑在第一个视频播放期间通过init功能预先初始化第二个视频,方法是通过decoder.configure(format, surface, null, 0)进行配置。此外,第一帧视频也通过decoder.queueInputBuffer排队。 但是这样做会导致以下错误:
01-13 16:20:37.182: E/BufferQueue(183): [SurfaceView] connect: already connected (cur=3, req=3)
01-13 16:20:37.182: E/MediaCodec(9148): native_window_api_connect returned an error: Invalid argument (-22)
01-13 16:20:37.182: E/Decoder Init(9148): Exception decoder.configure: java.lang.IllegalStateException

在我看来,一个表面只能同时由一个解码器使用。那么,有没有其他可能性呢?也许可以使用OpenGL?

祝好, Alex。

2个回答

1
你所描述的使用多个 MediaCodec 实例的方法是可行的,但每次只能有一个“生产者”连接到一个 Surface。在进行第二次之前,您需要拆除第一个,并且我不确定您可以获得多接近的时间。
相反,您可以解码为SurfaceTexture,然后在 SurfaceView 上绘制(使用 OpenGL,正如您想的那样)。
您可以在 ExtractMpegFramesTest 示例中看到将 MP4 文件呈现为 SurfaceTexture 的示例。从那里,您只需将纹理呈现到您的表面(SurfaceView? TextureView?),使用类似于 CameraToMpegTest 中的 STextureRender 类。

这里有一些额外的示例,位于Grafika,不过那里的视频播放器更接近你已经拥有的(解码器输出到TextureView)。

顺便说一下,你需要确定在第N个视频的最后一帧和第N+1个视频的第一帧之间要放置多少延迟时间。如果录制是以固定帧速率进行的,那么很容易做到,但某些来源(例如screenrecord)则并非如此记录。

更新: 如果您可以保证电影片段具有相同的特征(大小,编码类型 -- 本质上是MediaFormat中的所有内容),则有一种更简单的方法。当您遇到流的结束时,您可以flush()解码器,然后开始输入下一个文件。我在Grafika视频播放器中使用它来循环播放视频(请参见MoviePlayer#doExtract())。


感谢您的示例(ExtractMpegFramesTest),将解码到SurfaceTexture工作得非常好!现在我面临的问题是将纹理渲染到表面。据我所知,CameraToMpegTest也在存储视频而不显示,对吗?我可以使用SurfaceView和TextureView,但根据http://developer.android.com/training/graphics/opengl/environment.html,选择TextureView可能更容易一些。 附:是的,保证电影剪辑具有相同的特征和固定帧速率。 - A.Wolf
bigflake示例不会显示任何内容。ExtractMpegFramesTest创建一个SurfaceTexture,并将MediaCodec输出指向它。要在TextureView上显示输出,您需要更改代码,以便它使用来自TextureView#getSurfaceTexture()的SurfaceTexture。然后,TextureView类将负责锁定缓冲区并将其呈现到屏幕上。(如果您使用SurfaceView,则必须自己进行呈现。) - fadden
我根据你的建议做了一些更改;我创建了一个TextureView并通过mTextureView.setSurfaceTextureListener(this)设置了监听器。我将SurfaceTexture转发到CodecOutputSurface并使用它代替创建新的SurfaceTexture。但是,当我尝试通过mSurfaceTexture.attachToGLContext(mTextureRender.getTextureId())将其附加到GL上下文时,我遇到了以下错误:“E/GLConsumer: [unnamed-11969-0] attachToContext: GLConsumer is already attached to a context”。是否有必要附加它?从这里该怎么办? - A.Wolf
1
不要去操作从SurfaceView获取的SurfaceTexture的EGL上下文。那个属于UI线程。可以参考Grafika里的TextureViewGLActivity(https://github.com/google/grafika)。在`Renderer#run()`函数中,创建一个新的EGL上下文,并使用`TextureView`的`SurfaceTexture`作为表面。技巧是,你不是在`TextureView`的`EGLSurface`上绘制,而是在它的`Surface`上绘制……这些是完全不同的东西。你在`Surface`上绘制,它会映射到一个纹理上,然后`TextureView`将其渲染到`EGLSurface`上。 - fadden

0
疯狂的想法。尝试将两个视频合并成一个。它们都在您的设备上,所以不应该花费太长时间。而且您可以自己实现淡入淡出效果。

这通常是一种有前途的方法,但情况可能是当第一个视频开始播放时,并非所有视频序列都已存储在我的设备上,我必须同时下载它们。因此,“连接”对我来说不是一个选项。 - A.Wolf
@A.Wolf,流媒体怎么样? - Ilya Gazman
当然,它“感觉”类似于流式传输,但我们没有连续的帧流。每个视频序列都是由帧组成的,比如大约2秒钟的视频。 - A.Wolf
1
@A.Wolf,所以不要使用MP4。只需播放图像。这样更容易。 - Ilya Gazman
我也在考虑从MP4输入文件中逐帧提取并单独渲染它们。有没有一种方法可以直接将YUV帧显示到表面上?我相信转换它们会花费太多时间。 - A.Wolf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接