change HardwareDecode to kotlin

pull/166/head
xufuji456 4 years ago
parent c668a359a5
commit 48f8bd8018
  1. 218
      app/src/main/java/com/frank/ffmpeg/hardware/HardwareDecode.java
  2. 200
      app/src/main/java/com/frank/ffmpeg/hardware/HardwareDecode.kt

@ -1,218 +0,0 @@
package com.frank.ffmpeg.hardware;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.SystemClock;
import android.util.Log;
import android.view.Surface;
import java.nio.ByteBuffer;
/**
* Extract by MediaExtractor, decode by MediaCodec, and render to Surface
* Created by frank on 2019/11/16.
*/
public class HardwareDecode {
private final static String TAG = HardwareDecode.class.getSimpleName();
private final static long DEQUEUE_TIME = 10 * 1000;
private final static int SLEEP_TIME = 10;
private final static int RATIO_1080 = 1080;
private final static int RATIO_480 = 480;
private final static int RATIO_240 = 240;
private Surface mSurface;
private String mFilePath;
private VideoDecodeThread videoDecodeThread;
private OnDataCallback mCallback;
public interface OnDataCallback {
void onData(long duration);
}
public HardwareDecode(Surface surface, String filePath, OnDataCallback onDataCallback) {
this.mSurface = surface;
this.mFilePath = filePath;
this.mCallback = onDataCallback;
}
public void decode() {
videoDecodeThread = new VideoDecodeThread();
videoDecodeThread.start();
}
public void seekTo(long seekPosition) {
if (videoDecodeThread != null && !videoDecodeThread.isInterrupted()) {
videoDecodeThread.seekTo(seekPosition);
}
}
public void setPreviewing(boolean previewing) {
if (videoDecodeThread != null) {
videoDecodeThread.setPreviewing(previewing);
}
}
public void release() {
if (videoDecodeThread != null && !videoDecodeThread.isInterrupted()) {
videoDecodeThread.interrupt();
videoDecodeThread.release();
videoDecodeThread = null;
}
}
private class VideoDecodeThread extends Thread {
private MediaExtractor mediaExtractor;
private MediaCodec mediaCodec;
private boolean isPreviewing;
void setPreviewing(boolean previewing) {
this.isPreviewing = previewing;
}
void seekTo(long seekPosition) {
try {
if (mediaExtractor != null) {
mediaExtractor.seekTo(seekPosition, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
}
} catch (IllegalStateException e) {
Log.e(TAG, "seekTo error=" + e.toString());
}
}
void release() {
try {
if (mediaCodec != null) {
mediaCodec.stop();
mediaCodec.release();
}
if (mediaExtractor != null) {
mediaExtractor.release();
}
} catch (Exception e) {
Log.e(TAG, "release error=" + e.toString());
}
}
/**
* setting the preview resolution according to video aspect ratio
*
* @param mediaFormat mediaFormat
*/
private void setPreviewRatio(MediaFormat mediaFormat) {
if (mediaFormat == null) {
return;
}
int videoWidth = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
int videoHeight = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
int previewRatio;
if (videoWidth >= RATIO_1080) {
previewRatio = 10;
} else if (videoWidth >= RATIO_480) {
previewRatio = 6;
} else if (videoWidth >= RATIO_240) {
previewRatio = 4;
} else {
previewRatio = 1;
}
int previewWidth = videoWidth / previewRatio;
int previewHeight = videoHeight / previewRatio;
Log.e(TAG, "videoWidth=" + videoWidth + "--videoHeight=" + videoHeight
+ "--previewWidth=" + previewWidth + "--previewHeight=" + previewHeight);
mediaFormat.setInteger(MediaFormat.KEY_WIDTH, previewWidth);
mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, previewHeight);
}
@Override
public void run() {
super.run();
mediaExtractor = new MediaExtractor();
MediaFormat mediaFormat = null;
String mimeType = "";
try {
mediaExtractor.setDataSource(mFilePath);
for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
mediaFormat = mediaExtractor.getTrackFormat(i);
mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
if (mimeType != null && mimeType.startsWith("video/")) {
mediaExtractor.selectTrack(i);
break;
}
}
if (mediaFormat == null || mimeType == null) {
return;
}
int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
long duration = mediaFormat.getLong(MediaFormat.KEY_DURATION);
if (mCallback != null) {
mCallback.onData(duration);
}
Log.i(TAG, "width=" + width + "--height=" + height + "--duration==" + duration);
//setting preview resolution
setPreviewRatio(mediaFormat);
Log.i(TAG, "mediaFormat=" + mediaFormat.toString());
//config MediaCodec, and start
mediaCodec = MediaCodec.createDecoderByType(mimeType);
mediaCodec.configure(mediaFormat, mSurface, null, 0);
mediaCodec.start();
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
while (!isInterrupted()) {
if (!isPreviewing) {
SystemClock.sleep(SLEEP_TIME);
continue;
}
//dequeue from input buffer
int inputIndex = mediaCodec.dequeueInputBuffer(DEQUEUE_TIME);
if (inputIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputIndex];
int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);
//enqueue to input buffer
if (sampleSize < 0) {
mediaCodec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
} else {
mediaCodec.queueInputBuffer(inputIndex, 0, sampleSize, mediaExtractor.getSampleTime(), 0);
mediaExtractor.advance();
}
}
//dequeue from output buffer
int outputIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, DEQUEUE_TIME);
switch (outputIndex) {
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
Log.i(TAG, "output format changed...");
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
Log.i(TAG, "try again later...");
break;
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.i(TAG, "output buffer changed...");
break;
default:
//render to surface
mediaCodec.releaseOutputBuffer(outputIndex, true);
break;
}
}
} catch (Exception e) {
Log.e(TAG, "setDataSource error=" + e.toString());
}
}
}
}

@ -0,0 +1,200 @@
package com.frank.ffmpeg.hardware
import android.media.MediaCodec
import android.media.MediaExtractor
import android.media.MediaFormat
import android.os.SystemClock
import android.util.Log
import android.view.Surface
/**
* Extract by MediaExtractor, decode by MediaCodec, and render to Surface
* Created by frank on 2019/11/16.
*/
class HardwareDecode(private val mSurface: Surface, private val mFilePath: String, private val mCallback: OnDataCallback?) {
private var videoDecodeThread: VideoDecodeThread? = null
interface OnDataCallback {
fun onData(duration: Long)
}
fun decode() {
videoDecodeThread = VideoDecodeThread()
videoDecodeThread!!.start()
}
fun seekTo(seekPosition: Long) {
if (videoDecodeThread != null && !videoDecodeThread!!.isInterrupted) {
videoDecodeThread!!.seekTo(seekPosition)
}
}
fun setPreviewing(previewing: Boolean) {
if (videoDecodeThread != null) {
videoDecodeThread!!.setPreviewing(previewing)
}
}
fun release() {
if (videoDecodeThread != null && !videoDecodeThread!!.isInterrupted) {
videoDecodeThread!!.interrupt()
videoDecodeThread!!.release()
videoDecodeThread = null
}
}
private inner class VideoDecodeThread : Thread() {
private var mediaExtractor: MediaExtractor? = null
private var mediaCodec: MediaCodec? = null
private var isPreviewing: Boolean = false
internal fun setPreviewing(previewing: Boolean) {
this.isPreviewing = previewing
}
internal fun seekTo(seekPosition: Long) {
try {
if (mediaExtractor != null) {
mediaExtractor!!.seekTo(seekPosition, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
}
} catch (e: IllegalStateException) {
Log.e(TAG, "seekTo error=$e")
}
}
internal fun release() {
try {
if (mediaCodec != null) {
mediaCodec!!.stop()
mediaCodec!!.release()
}
if (mediaExtractor != null) {
mediaExtractor!!.release()
}
} catch (e: Exception) {
Log.e(TAG, "release error=$e")
}
}
/**
* setting the preview resolution according to video aspect ratio
*
* @param mediaFormat mediaFormat
*/
private fun setPreviewRatio(mediaFormat: MediaFormat?) {
if (mediaFormat == null) {
return
}
val videoWidth = mediaFormat.getInteger(MediaFormat.KEY_WIDTH)
val videoHeight = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)
val previewRatio: Int
if (videoWidth >= RATIO_1080) {
previewRatio = 10
} else if (videoWidth >= RATIO_480) {
previewRatio = 6
} else if (videoWidth >= RATIO_240) {
previewRatio = 4
} else {
previewRatio = 1
}
val previewWidth = videoWidth / previewRatio
val previewHeight = videoHeight / previewRatio
Log.e(TAG, "videoWidth=" + videoWidth + "--videoHeight=" + videoHeight
+ "--previewWidth=" + previewWidth + "--previewHeight=" + previewHeight)
mediaFormat.setInteger(MediaFormat.KEY_WIDTH, previewWidth)
mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, previewHeight)
}
override fun run() {
super.run()
mediaExtractor = MediaExtractor()
var mediaFormat: MediaFormat? = null
var mimeType: String? = ""
try {
mediaExtractor!!.setDataSource(mFilePath)
for (i in 0 until mediaExtractor!!.trackCount) {
mediaFormat = mediaExtractor!!.getTrackFormat(i)
mimeType = mediaFormat!!.getString(MediaFormat.KEY_MIME)
if (mimeType != null && mimeType.startsWith("video/")) {
mediaExtractor!!.selectTrack(i)
break
}
}
if (mediaFormat == null || mimeType == null) {
return
}
val width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH)
val height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)
val duration = mediaFormat.getLong(MediaFormat.KEY_DURATION)
mCallback?.onData(duration)
Log.i(TAG, "width=$width--height=$height--duration==$duration")
//setting preview resolution
setPreviewRatio(mediaFormat)
Log.i(TAG, "mediaFormat=$mediaFormat")
//config MediaCodec, and start
mediaCodec = MediaCodec.createDecoderByType(mimeType)
mediaCodec!!.configure(mediaFormat, mSurface, null, 0)
mediaCodec!!.start()
val bufferInfo = MediaCodec.BufferInfo()
val inputBuffers = mediaCodec!!.inputBuffers
while (!isInterrupted) {
if (!isPreviewing) {
SystemClock.sleep(SLEEP_TIME.toLong())
continue
}
//dequeue from input buffer
val inputIndex = mediaCodec!!.dequeueInputBuffer(DEQUEUE_TIME)
if (inputIndex >= 0) {
val inputBuffer = inputBuffers[inputIndex]
val sampleSize = mediaExtractor!!.readSampleData(inputBuffer, 0)
//enqueue to input buffer
if (sampleSize < 0) {
mediaCodec!!.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
} else {
mediaCodec!!.queueInputBuffer(inputIndex, 0, sampleSize, mediaExtractor!!.sampleTime, 0)
mediaExtractor!!.advance()
}
}
//dequeue from output buffer
val outputIndex = mediaCodec!!.dequeueOutputBuffer(bufferInfo, DEQUEUE_TIME)
when (outputIndex) {
MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> Log.i(TAG, "output format changed...")
MediaCodec.INFO_TRY_AGAIN_LATER -> Log.i(TAG, "try again later...")
MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED -> Log.i(TAG, "output buffer changed...")
else ->
//render to surface
mediaCodec!!.releaseOutputBuffer(outputIndex, true)
}
}
} catch (e: Exception) {
Log.e(TAG, "setDataSource error=$e")
}
}
}
companion object {
private val TAG = HardwareDecode::class.java.simpleName
private const val DEQUEUE_TIME = (10 * 1000).toLong()
private const val SLEEP_TIME = 10
private const val RATIO_1080 = 1080
private const val RATIO_480 = 480
private const val RATIO_240 = 240
}
}
Loading…
Cancel
Save