|
@@ -17,6 +17,7 @@ package com.tencent.qgame.animplayer
|
|
|
|
|
|
|
|
import android.graphics.SurfaceTexture
|
|
import android.graphics.SurfaceTexture
|
|
|
import android.media.MediaCodec
|
|
import android.media.MediaCodec
|
|
|
|
|
+import android.media.MediaCodecInfo
|
|
|
import android.media.MediaExtractor
|
|
import android.media.MediaExtractor
|
|
|
import android.media.MediaFormat
|
|
import android.media.MediaFormat
|
|
|
import android.os.Build
|
|
import android.os.Build
|
|
@@ -36,6 +37,17 @@ class HardDecoder(player: AnimPlayer) : Decoder(player), SurfaceTexture.OnFrameA
|
|
|
private val bufferInfo by lazy { MediaCodec.BufferInfo() }
|
|
private val bufferInfo by lazy { MediaCodec.BufferInfo() }
|
|
|
private var needDestroy = false
|
|
private var needDestroy = false
|
|
|
|
|
|
|
|
|
|
+ // 动画的原始尺寸
|
|
|
|
|
+ private var videoWidth = 0
|
|
|
|
|
+ private var videoHeight = 0
|
|
|
|
|
+
|
|
|
|
|
+ // 动画对齐后的尺寸
|
|
|
|
|
+ private var alignWidth = 0
|
|
|
|
|
+ private var alignHeight = 0
|
|
|
|
|
+
|
|
|
|
|
+ // 动画是否被对齐标志位
|
|
|
|
|
+ private var needAlign = false
|
|
|
|
|
+
|
|
|
override fun start(fileContainer: IFileContainer) {
|
|
override fun start(fileContainer: IFileContainer) {
|
|
|
isStopReq = false
|
|
isStopReq = false
|
|
|
needDestroy = false
|
|
needDestroy = false
|
|
@@ -49,11 +61,15 @@ class HardDecoder(player: AnimPlayer) : Decoder(player), SurfaceTexture.OnFrameA
|
|
|
override fun onFrameAvailable(surfaceTexture: SurfaceTexture?) {
|
|
override fun onFrameAvailable(surfaceTexture: SurfaceTexture?) {
|
|
|
if (isStopReq) return
|
|
if (isStopReq) return
|
|
|
ALog.d(TAG, "onFrameAvailable")
|
|
ALog.d(TAG, "onFrameAvailable")
|
|
|
|
|
+ renderData()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fun renderData() {
|
|
|
renderThread.handler?.post {
|
|
renderThread.handler?.post {
|
|
|
try {
|
|
try {
|
|
|
glTexture?.apply {
|
|
glTexture?.apply {
|
|
|
updateTexImage()
|
|
updateTexImage()
|
|
|
- render?.renderFrame(player.configManager.config)
|
|
|
|
|
|
|
+ render?.renderFrame()
|
|
|
player.pluginManager.onRendering()
|
|
player.pluginManager.onRendering()
|
|
|
render?.swapBuffers()
|
|
render?.swapBuffers()
|
|
|
}
|
|
}
|
|
@@ -64,15 +80,6 @@ class HardDecoder(player: AnimPlayer) : Decoder(player), SurfaceTexture.OnFrameA
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private fun startPlay(fileContainer: IFileContainer) {
|
|
private fun startPlay(fileContainer: IFileContainer) {
|
|
|
- try {
|
|
|
|
|
- if (!prepareRender()) {
|
|
|
|
|
- throw RuntimeException("render create fail")
|
|
|
|
|
- }
|
|
|
|
|
- } catch (t: Throwable) {
|
|
|
|
|
- onFailed(Constant.REPORT_ERROR_TYPE_CREATE_RENDER, "${Constant.ERROR_MSG_CREATE_RENDER} e=$t")
|
|
|
|
|
- release(null, null)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
var extractor: MediaExtractor? = null
|
|
var extractor: MediaExtractor? = null
|
|
|
var decoder: MediaCodec? = null
|
|
var decoder: MediaCodec? = null
|
|
@@ -103,9 +110,23 @@ class HardDecoder(player: AnimPlayer) : Decoder(player), SurfaceTexture.OnFrameA
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- val videoWidth = format.getInteger(MediaFormat.KEY_WIDTH)
|
|
|
|
|
- val videoHeight = format.getInteger(MediaFormat.KEY_HEIGHT)
|
|
|
|
|
|
|
+ videoWidth = format.getInteger(MediaFormat.KEY_WIDTH)
|
|
|
|
|
+ videoHeight = format.getInteger(MediaFormat.KEY_HEIGHT)
|
|
|
ALog.i(TAG, "Video size is $videoWidth x $videoHeight")
|
|
ALog.i(TAG, "Video size is $videoWidth x $videoHeight")
|
|
|
|
|
+
|
|
|
|
|
+ // 判断是否需要对齐,这样直接判断有风险,后期想办法改
|
|
|
|
|
+ needAlign = !(videoWidth % 16 == 0)
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (!prepareRender(needAlign)) {
|
|
|
|
|
+ throw RuntimeException("render create fail")
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (t: Throwable) {
|
|
|
|
|
+ onFailed(Constant.REPORT_ERROR_TYPE_CREATE_RENDER, "${Constant.ERROR_MSG_CREATE_RENDER} e=$t")
|
|
|
|
|
+ release(null, null)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
preparePlay(videoWidth, videoHeight)
|
|
preparePlay(videoWidth, videoHeight)
|
|
|
|
|
|
|
|
render?.apply {
|
|
render?.apply {
|
|
@@ -128,7 +149,16 @@ class HardDecoder(player: AnimPlayer) : Decoder(player), SurfaceTexture.OnFrameA
|
|
|
val mime = format.getString(MediaFormat.KEY_MIME) ?: ""
|
|
val mime = format.getString(MediaFormat.KEY_MIME) ?: ""
|
|
|
ALog.i(TAG, "Video MIME is $mime")
|
|
ALog.i(TAG, "Video MIME is $mime")
|
|
|
decoder = MediaCodec.createDecoderByType(mime).apply {
|
|
decoder = MediaCodec.createDecoderByType(mime).apply {
|
|
|
- configure(format, Surface(glTexture), null, 0)
|
|
|
|
|
|
|
+ if (needAlign) {
|
|
|
|
|
+ format.setInteger(
|
|
|
|
|
+ MediaFormat.KEY_COLOR_FORMAT,
|
|
|
|
|
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar
|
|
|
|
|
+ )
|
|
|
|
|
+ configure(format, null, null, 0)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ configure(format, Surface(glTexture), null, 0)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
start()
|
|
start()
|
|
|
decodeThread.handler?.post {
|
|
decodeThread.handler?.post {
|
|
|
try {
|
|
try {
|
|
@@ -194,6 +224,12 @@ class HardDecoder(player: AnimPlayer) : Decoder(player), SurfaceTexture.OnFrameA
|
|
|
decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED -> ALog.d(TAG, "decoder output buffers changed")
|
|
decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED -> ALog.d(TAG, "decoder output buffers changed")
|
|
|
decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
|
|
decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
|
|
|
val format = decoder.outputFormat
|
|
val format = decoder.outputFormat
|
|
|
|
|
+ try {
|
|
|
|
|
+ alignWidth = format.getInteger(MediaFormat.KEY_STRIDE)
|
|
|
|
|
+ alignHeight = format.getInteger(MediaFormat.KEY_SLICE_HEIGHT)
|
|
|
|
|
+ } catch (t: Throwable) {
|
|
|
|
|
+ ALog.e(TAG, "formatChange $t", t)
|
|
|
|
|
+ }
|
|
|
ALog.i(TAG, "decoder output format changed: $format")
|
|
ALog.i(TAG, "decoder output format changed: $format")
|
|
|
// val (w,h) = formatChange(format)
|
|
// val (w,h) = formatChange(format)
|
|
|
// videoSizeChange(w, h)
|
|
// videoSizeChange(w, h)
|
|
@@ -213,8 +249,12 @@ class HardDecoder(player: AnimPlayer) : Decoder(player), SurfaceTexture.OnFrameA
|
|
|
speedControlUtil.preRender(bufferInfo.presentationTimeUs)
|
|
speedControlUtil.preRender(bufferInfo.presentationTimeUs)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (needAlign) {
|
|
|
|
|
+ yuvProcess(decoder, decoderStatus)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// release & render
|
|
// release & render
|
|
|
- decoder.releaseOutputBuffer(decoderStatus, doRender)
|
|
|
|
|
|
|
+ decoder.releaseOutputBuffer(decoderStatus, doRender && !needAlign)
|
|
|
|
|
|
|
|
if (frameIndex == 0) {
|
|
if (frameIndex == 0) {
|
|
|
onVideoStart()
|
|
onVideoStart()
|
|
@@ -242,6 +282,32 @@ class HardDecoder(player: AnimPlayer) : Decoder(player), SurfaceTexture.OnFrameA
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private fun yuvProcess(decoder: MediaCodec, outputIndex: Int) {
|
|
|
|
|
+ val outputBuffer = decoder.outputBuffers[outputIndex]
|
|
|
|
|
+ outputBuffer?.let {
|
|
|
|
|
+ it.position(0)
|
|
|
|
|
+ it.limit(bufferInfo.offset + bufferInfo.size)
|
|
|
|
|
+ var yuvData = ByteArray(outputBuffer.remaining())
|
|
|
|
|
+ outputBuffer.get(yuvData)
|
|
|
|
|
+ if (yuvData.isNotEmpty()) {
|
|
|
|
|
+ var yData = ByteArray(videoWidth * videoHeight)
|
|
|
|
|
+ var uvData = ByteArray(videoWidth * videoHeight / 2)
|
|
|
|
|
+ yuvCopy(yuvData, 0, alignWidth, alignHeight, yData, videoWidth, videoHeight)
|
|
|
|
|
+ yuvCopy(yuvData, alignWidth * alignHeight, alignWidth, alignHeight / 2, uvData, videoWidth, videoHeight / 2)
|
|
|
|
|
+ render?.setYUVData(videoWidth, videoHeight, yData, uvData)
|
|
|
|
|
+ renderData()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun yuvCopy(src: ByteArray, srcOffset: Int, inWidth: Int, inHeight: Int, dest: ByteArray, outWidth: Int, outHeight: Int) {
|
|
|
|
|
+ for (h in 0 until inHeight) {
|
|
|
|
|
+ if (h < outHeight) {
|
|
|
|
|
+ System.arraycopy(src, srcOffset + h * inWidth, dest, h * outWidth, outWidth)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private fun formatChange(format: MediaFormat): Pair<Int, Int> {
|
|
private fun formatChange(format: MediaFormat): Pair<Int, Int> {
|
|
|
try {
|
|
try {
|
|
|
// 实际视频的纹理大小
|
|
// 实际视频的纹理大小
|
|
@@ -293,7 +359,7 @@ class HardDecoder(player: AnimPlayer) : Decoder(player), SurfaceTexture.OnFrameA
|
|
|
private fun destroyInner() {
|
|
private fun destroyInner() {
|
|
|
renderThread.handler?.post {
|
|
renderThread.handler?.post {
|
|
|
player.pluginManager.onDestroy()
|
|
player.pluginManager.onDestroy()
|
|
|
- render?.destroy()
|
|
|
|
|
|
|
+ render?.destroyRender()
|
|
|
render = null
|
|
render = null
|
|
|
onVideoDestroy()
|
|
onVideoDestroy()
|
|
|
destroyThread()
|
|
destroyThread()
|