Skip to content

Commit 9406508

Browse files
committed
rewrite HardwareDecode with kotlin
1 parent 586e261 commit 9406508

1 file changed

Lines changed: 171 additions & 0 deletions

File tree

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package com.frank.ffmpeg.kotlin.hardware
2+
3+
import android.media.MediaCodec
4+
import android.media.MediaExtractor
5+
import android.media.MediaFormat
6+
import android.os.SystemClock
7+
import android.util.Log
8+
import android.view.Surface
9+
import java.lang.Exception
10+
import java.nio.ByteBuffer
11+
12+
/**
13+
* Extract by MediaExtractor, decode by MediaCodec, and render to Surface
14+
* Created by frank on 2020/04/06.
15+
*/
16+
17+
class HardwareKotlinDecode constructor(surface : Surface, filePath : String, onDataCallback : OnDataCallback){
18+
19+
private var mSurface : Surface ?= null
20+
private var mFilePath : String = ""
21+
private var mVideoDecodeThread : VideoDecodeThread ?= null
22+
private var mCallback : OnDataCallback ?= null
23+
24+
init {
25+
this.mSurface = surface
26+
this.mFilePath = filePath
27+
this.mCallback = onDataCallback
28+
}
29+
30+
interface OnDataCallback {
31+
fun onData(duration : Long)
32+
}
33+
34+
public fun decode() {
35+
mVideoDecodeThread = VideoDecodeThread(mFilePath)
36+
mVideoDecodeThread!!.start()
37+
}
38+
39+
public fun seekTo(seekPosition : Long) {
40+
if (!mVideoDecodeThread!!.isInterrupted) {
41+
mVideoDecodeThread!!.seekTo(seekPosition)
42+
}
43+
}
44+
45+
public fun setPreviewing(previewing : Boolean) {
46+
mVideoDecodeThread!!.isPreviewing = previewing;
47+
}
48+
49+
public fun release() {
50+
if (!mVideoDecodeThread!!.isInterrupted) {
51+
mVideoDecodeThread!!.interrupt()
52+
mVideoDecodeThread!!.release()
53+
mVideoDecodeThread = null
54+
}
55+
}
56+
57+
private inner class VideoDecodeThread(filePath: String) : Thread() {
58+
59+
private var mediaExtractor : MediaExtractor ?= null
60+
private var mediaCodec : MediaCodec ?= null
61+
var isPreviewing : Boolean = false
62+
private var mFilePath : String = filePath
63+
64+
fun seekTo(seekPosition: Long) {
65+
try {
66+
mediaExtractor!!.seekTo(seekPosition, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
67+
} catch (e: IllegalStateException) {
68+
Log.e(TAG, "seekTo error=$e")
69+
}
70+
}
71+
72+
fun release() {
73+
try {
74+
mediaCodec!!.stop()
75+
mediaCodec!!.release()
76+
mediaExtractor!!.release()
77+
}catch (e : Exception) {
78+
Log.e(TAG, "release error=$e")
79+
}
80+
}
81+
82+
fun setPreviewRatio(mediaFormat : MediaFormat) {
83+
val videoWidth = mediaFormat.getInteger(MediaFormat.KEY_WIDTH)
84+
val videoHeight = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)
85+
val previewRatio = when {
86+
videoWidth >= RATIO_1080 -> 10
87+
videoWidth >= RATIO_480 -> 6
88+
videoWidth >= RATIO_240 -> 4
89+
else -> 1
90+
}
91+
val previewWidth = videoWidth / previewRatio
92+
val previewHeight = videoHeight / previewRatio
93+
mediaFormat.setInteger(MediaFormat.KEY_WIDTH, previewWidth)
94+
mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, previewHeight)
95+
}
96+
97+
override fun run() {
98+
super.run()
99+
mediaExtractor = MediaExtractor()
100+
var mediaFormat : MediaFormat ?= null
101+
var mimeType = ""
102+
103+
try {
104+
mediaExtractor!!.setDataSource(mFilePath)
105+
val trackCount = mediaExtractor!!.trackCount
106+
for (i in 0..trackCount) {
107+
mediaFormat = mediaExtractor!!.getTrackFormat(i)
108+
mimeType = mediaFormat.getString(MediaFormat.KEY_MIME)
109+
if (mimeType != null && mimeType.startsWith("video/")) {
110+
mediaExtractor!!.selectTrack(i)
111+
break
112+
}
113+
}
114+
115+
val width : Int = mediaFormat!!.getInteger(MediaFormat.KEY_WIDTH)
116+
val height : Int = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)
117+
val duration : Long = mediaFormat.getLong(MediaFormat.KEY_DURATION)
118+
mCallback!!.onData(duration)
119+
Log.i(TAG, "width=$width--height=$height--duration=$duration")
120+
setPreviewRatio(mediaFormat)
121+
mediaCodec = MediaCodec.createDecoderByType(mimeType)
122+
mediaCodec!!.configure(mediaFormat, mSurface, null, 0)
123+
mediaCodec!!.start()
124+
val inputBuffers = mediaCodec!!.getInputBuffers()
125+
val bufferInfo : MediaCodec.BufferInfo = MediaCodec.BufferInfo()
126+
127+
while (!isInterrupted) {
128+
if (!isPreviewing) {
129+
SystemClock.sleep(SLEEP_TIME)
130+
continue
131+
}
132+
val inputIndex = mediaCodec!!.dequeueInputBuffer(DEQUEUE_TIME)
133+
if (inputIndex >= 0) {
134+
val inputBuffer : ByteBuffer = inputBuffers[inputIndex]
135+
val sampleSize = mediaExtractor!!.readSampleData(inputBuffer, 0)
136+
if (sampleSize < 0) {
137+
mediaCodec!!.queueInputBuffer(inputIndex, 0, 0, 0,
138+
MediaCodec.BUFFER_FLAG_END_OF_STREAM)
139+
} else {
140+
mediaCodec!!.queueInputBuffer(inputIndex, 0, sampleSize, mediaExtractor!!.sampleTime, 0)
141+
mediaExtractor!!.advance()
142+
}
143+
}
144+
val outputIndex = mediaCodec!!.dequeueOutputBuffer(bufferInfo, DEQUEUE_TIME)
145+
when (outputIndex) {
146+
MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> Log.i(TAG, "output format changed...")
147+
MediaCodec.INFO_TRY_AGAIN_LATER -> Log.i(TAG, "try again later...")
148+
MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED -> Log.i(TAG, "output buffer changed...")
149+
else ->
150+
//render to surface
151+
mediaCodec!!.releaseOutputBuffer(outputIndex, true)
152+
}
153+
}
154+
} catch (e : Exception) {
155+
Log.e(TAG, "decode error=$e")
156+
}
157+
}
158+
159+
}
160+
161+
companion object {
162+
private val TAG = HardwareKotlinDecode::class.java.simpleName
163+
164+
private const val DEQUEUE_TIME : Long = 10 * 1000
165+
private const val SLEEP_TIME : Long = 10
166+
167+
private const val RATIO_1080 = 1080
168+
private const val RATIO_480 = 480
169+
private const val RATIO_240 = 240
170+
}
171+
}

0 commit comments

Comments
 (0)