gpt4 book ai didi

android - 使用 MediaCodec 和 GLSurfaceView 搜索视频

转载 作者:行者123 更新时间:2023-12-02 17:22:31 39 4
gpt4 key购买 nike

我正在尝试使用 MediaCodec 解码器实现对视频文件的搜索,该解码器输出到 GLSurfaceView。该解决方案基于 Bigflake 示例和 fadden评论。它适用于 SurfaceView,但我在使用 GLSurafaceView 时遇到一些问题:渲染帧始终为黑色

查看实现:

class GLVideoView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : GLSurfaceView(context, attrs),
SurfaceTexture.OnFrameAvailableListener {

private var outputSurface: OutputSurface? = null
private var videoPlayer: VideoPlayer? = null

private var videoFilePath: String? = null
private var videoDuration: Int = 0
private var videoWidth = 0
private var videoHeight = 0

private val renderer: Renderer

init {
setEGLContextClientVersion(2)
renderer = VideoRender()
setRenderer(renderer)
renderMode = RENDERMODE_WHEN_DIRTY
}

// region Public API

fun setVideoSource(videoFilePath: String) {
this.videoFilePath = videoFilePath

val metadataRetriever = MediaMetadataRetriever().apply { setDataSource(videoFilePath) }
videoDuration = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toInt()
videoWidth = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH).toInt()
videoHeight = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT).toInt()
try {
val rotation = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION).toInt()
if (rotation == 90 || rotation == 270) {
val temp = videoWidth
videoWidth = videoHeight
videoHeight = temp
}
} catch (e: Exception) {
// ignore
}
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
...
}

override fun onFrameAvailable(st: SurfaceTexture) {
L.debug { "onFrameAvailable()" }
outputSurface?.updateTextureImage()
requestRender()
}

// endregion

// region Private API

private fun initVideoPlayer() {
val filePath = videoFilePath ?: throw IllegalStateException("No video source!")

outputSurface = OutputSurface(this)
val surface = outputSurface?.surface ?: throw IllegalStateException("No surface created!")

videoPlayer = VideoPlayer(filePath, outputSurface!!).apply { initialize(surface) }
}

// endregion

companion object {
private val L = Logger()
}

private inner class VideoRender : Renderer {
override fun onDrawFrame(gl: GL10?) {
L.debug { "onDrawFrame()" }
outputSurface?.drawImage()
}

override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
}

override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
if (videoPlayer == null) {
initVideoPlayer()
}
}
}
}

OutputSurface 来自Bigflake ,以及TextureRenderer link

这是基本的解码器实现:

internal class GLSyncVideoDecoder(
private val mediaExtractor: VideoExtractor,
private val outputSurface: OutputSurface
) : VideoFrameDecoder {

private lateinit var mediaCodec: MediaCodec

private lateinit var taskHandler: Handler
private val uiHandler: Handler = Handler(Looper.getMainLooper())

@Volatile
private var isRunning = false

@Throws(IOException::class)
override fun initCodec(
outSurface: Surface,
inputFormat: MediaFormat,
handlerThread: HandlerThread
): Boolean {
taskHandler = Handler(handlerThread.looper)

val mime = inputFormat.getString(MediaFormat.KEY_MIME) ?: return false

mediaCodec = MediaCodec.createDecoderByType(mime).apply {
configure(inputFormat, outSurface, null, 0)
start()
}

return true
}

override fun decodeFrameAt(timeUs: Long) {
if (isRunning) {
L.debug { "!@# Skip 'seekTo()' at time: $timeUs" }
return
}

isRunning = true
taskHandler.post {
mediaCodec.flush()

seekTo(timeUs, mediaCodec)

isRunning = false
}
}

private fun seekTo(timeUs: Long, decoder: MediaCodec) {
var outputDone = false
var inputDone = false

mediaExtractor.seekTo(timeUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC)

val bufferInfo = MediaCodec.BufferInfo()

outerloop@ while (true) {
var ptUs = 0L
// Feed more data to the decoder.
if (!inputDone) {
val inputBufIndex = decoder.dequeueInputBuffer(1000)
if (inputBufIndex >= 0) {
val inputBuf = decoder.getInputBuffer(inputBufIndex)
val chunkSize = mediaExtractor.readSampleData(inputBuf!!, 0)

if (chunkSize < 0) {
// End of stream -- send empty frame with EOS flag set.
decoder.queueInputBuffer(
inputBufIndex,
0,
0,
0L,
MediaCodec.BUFFER_FLAG_END_OF_STREAM
)
inputDone = true
} else {
val presentationTimeUs = mediaExtractor.sampleTime
val flags = mediaExtractor.sampleFlags
ptUs = presentationTimeUs
decoder.queueInputBuffer(
inputBufIndex,
0,
chunkSize,
presentationTimeUs,
flags
)
mediaExtractor.advance()
}
}
}

if (!outputDone) {
val decoderStatus = decoder.dequeueOutputBuffer(bufferInfo, 1000)
when {
decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER -> { }
decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED -> { }
decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> { }
decoderStatus < 0 -> throw RuntimeException("unexpected result from decoder.dequeueOutputBuffer: $decoderStatus")
else -> { // decoderStatus >= 0
if ((bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
outputDone = true
break@outerloop
}

val presentationTimeUs = bufferInfo.presentationTimeUs
val validFrame = presentationTimeUs >= timeUs
val doRender = (bufferInfo.size != 0) && validFrame

decoder.releaseOutputBuffer(decoderStatus, doRender)
if (doRender) {
break@outerloop
}
}
}
}
}
}

...
}

如何让TextureRenderer正确绘制到GLSurfaceView?我做错了什么? OpenGL 绘图是否不正确或 GLSurfaceView 未链接到 MediaCodec 输出表面?

最佳答案

终于找到问题的答案了。我遵循 VideoSurfaceView.java 中的代码。(将 OutputSurface 放入 Renderer 线程,并从 Renderer 的 onDrawFrame() 更新 SurfaceTexture texImage)

希望它可以帮助将来的人。感谢您的关注:)

关于android - 使用 MediaCodec 和 GLSurfaceView 搜索视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58706872/

39 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com