Remove unused MMediaMuxer

filterfix
Laurent 5 years ago
parent f79f2f60cd
commit 6c36863185
  1. 433
      app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt

@ -1,433 +0,0 @@
package net.pokeranalytics.android.util.video
import android.app.Activity
import android.app.ProgressDialog
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.media.*
import android.media.MediaCodecInfo.CodecCapabilities
import android.os.Environment
import android.util.Log
import net.pokeranalytics.android.util.extensions.dateTimeFileFormatted
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.util.*
class MMediaMuxer {
private var mediaCodec: MediaCodec? = null
private var mediaMuxer: MediaMuxer? = null
private var mRunning = false
private var generateIndex = 0
private var mTrackIndex = 0
private var MAX_FRAME_VIDEO = 0
private var bitList: MutableList<ByteArray>? = null
private var bitFirst: MutableList<ByteArray>? = null
private var bitLast: MutableList<ByteArray>? = null
private var currentIndexFrame = 0
private var outputPath: String? = null
private var _activity: Activity? = null
private var pd: ProgressDialog? = null
private var _title: String? = null
private var _mess: String? = null
private var completion: ((String) -> (Unit))? = null
fun init(
activity: Activity?,
width: Int,
height: Int,
title: String?,
mess: String?
) {
_title = title
_mess = mess
_activity = activity
_width = width
_height = height
Logd("MMediaMuxer Init")
ShowProgressBar()
}
fun addFrame(byteFrame: ByteArray) {
checkDataListState()
Thread(Runnable {
Logd("Android get Frame")
val bitmap = BitmapFactory.decodeByteArray(byteFrame, 0, byteFrame.size)
Logd("Android convert Bitmap")
val byteConvertFrame =
getNV21(bitmap.width, bitmap.height, bitmap)
Logd("Android convert getNV21")
bitList!!.add(byteConvertFrame)
}).start()
}
fun addFrame(byteFrame: ByteArray, count: Int, isLast: Boolean) {
var bf = byteFrame
checkDataListState()
Logd("Android get Frames = $count")
val bitmap = BitmapFactory.decodeByteArray(bf, 0, bf.size)
Logd("Android convert Bitmap")
bf = getNV21(bitmap.width, bitmap.height, bitmap)
Logd("Android convert getNV21")
for (i in 0 until count) {
if (isLast) {
bitLast!!.add(bf)
} else {
bitFirst!!.add(bf)
}
}
}
fun createVideo(handler: (String) -> (Unit)) {
this.completion = handler
currentIndexFrame = 0
Logd("Prepare Frames Data")
bitFirst!!.addAll(bitList!!)
bitFirst!!.addAll(bitLast!!)
MAX_FRAME_VIDEO = bitFirst!!.size
Logd("CreateVideo")
mRunning = true
bufferEncoder()
}
fun GetStateEncoder(): Boolean {
return mRunning
}
fun GetPath(): String? {
return outputPath
}
fun onBackPressed() {
mRunning = false
}
fun ShowProgressBar() {
_activity?.runOnUiThread {
pd = ProgressDialog(_activity)
pd!!.setTitle(_title)
pd!!.setCancelable(false)
pd!!.setMessage(_mess)
pd!!.setCanceledOnTouchOutside(false)
pd!!.show()
}
}
fun HideProgressBar() {
Thread(Runnable { _activity?.runOnUiThread { pd!!.dismiss() } }).start()
}
private fun bufferEncoder() {
val runnable = Runnable {
try {
Logd("PrepareEncoder start")
prepareEncoder()
Logd("PrepareEncoder end")
} catch (e: IOException) {
Loge(e.message)
}
try {
while (mRunning) {
encode()
}
} finally {
Logd("release")
Release()
HideProgressBar()
bitFirst = null
bitLast = null
}
}
val thread = Thread(runnable)
thread.start()
}
fun ClearTask() {
bitList = null
bitFirst = null
bitLast = null
}
@Throws(IOException::class)
private fun prepareEncoder() {
// val codecInfo = selectCodec(MIME_TYPE)
// if (codecInfo == null) {
// Loge("Unable to find an appropriate codec for $MIME_TYPE")
// }
val codec = MediaCodec.createEncoderByType(MIME_TYPE)
val colorFormat: Int
colorFormat = try {
selectColorFormat(codec.codecInfo, MIME_TYPE)
} catch (e: Exception) {
Timber.d(">>> color format exception: $e")
CodecCapabilities.COLOR_FormatYUV420SemiPlanar
}
Logd("Selected codec: " + codec.name)
Logd("Selected color format: $colorFormat")
// mediaCodec = MediaCodec.createByCodecName(codecInfo.name)
mediaCodec = codec
val mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, _width, _height)
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE)
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE)
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat)
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL)
mediaCodec?.let {
it.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
it.start()
Timber.d("format2: ${it.outputFormat}")
}
try {
val formattedDate = Date().dateTimeFileFormatted
val path = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
"video_${formattedDate}.mp4"
).toString()
outputPath = path
mediaMuxer = MediaMuxer(path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
} catch (ioe: IOException) {
Loge("MediaMuxer creation failed: ${ioe.message}")
}
}
private fun encode() {
while (true) {
if (!mRunning) {
break
}
Logd("Encode start")
val TIMEOUT_USEC: Long = 5000
val inputBufIndex = mediaCodec!!.dequeueInputBuffer(TIMEOUT_USEC)
val ptsUsec =
computePresentationTime(generateIndex.toLong(),
FRAME_RATE
)
if (inputBufIndex >= 0) {
val input = bitFirst!![currentIndexFrame]
val inputBuffer = mediaCodec!!.getInputBuffer(inputBufIndex)
inputBuffer?.clear()
inputBuffer?.put(input)
mediaCodec!!.queueInputBuffer(inputBufIndex, 0, input.size, ptsUsec, 0)
generateIndex++
}
val mBufferInfo = MediaCodec.BufferInfo()
val encoderStatus = mediaCodec!!.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC)
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { // no output available yet
Loge(
"No output from encoder available"
)
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { // not expected for an encoder
val newFormat = mediaCodec!!.outputFormat
mTrackIndex = mediaMuxer!!.addTrack(newFormat)
mediaMuxer!!.start()
} else if (encoderStatus < 0) {
Loge(
"unexpected result from encoder.dequeueOutputBuffer: $encoderStatus"
)
} else if (mBufferInfo.size != 0) {
val encodedData = mediaCodec!!.getOutputBuffer(encoderStatus)
if (encodedData == null) {
Loge(
"encoderOutputBuffer $encoderStatus was null"
)
} else {
encodedData.position(mBufferInfo.offset)
encodedData.limit(mBufferInfo.offset + mBufferInfo.size)
mediaMuxer!!.writeSampleData(mTrackIndex, encodedData, mBufferInfo)
mediaCodec!!.releaseOutputBuffer(encoderStatus, false)
}
}
currentIndexFrame++
if (currentIndexFrame > MAX_FRAME_VIDEO - 1) {
Log.d(TAG, "mRunning = false;")
mRunning = false
}
Logd("Encode end")
}
}
private fun Release() {
if (mediaCodec != null) {
mediaCodec!!.stop()
mediaCodec!!.release()
mediaCodec = null
Logd("RELEASE CODEC")
}
if (mediaMuxer != null) {
mediaMuxer!!.stop()
mediaMuxer!!.release()
mediaMuxer = null
Logd("RELEASE MUXER, path = $outputPath")
}
this.outputPath?.let { path ->
this.completion?.invoke(path)
} ?: run {
Logd("no path")
}
}
private fun getNV21(inputWidth: Int, inputHeight: Int, scaled: Bitmap): ByteArray {
val argb = IntArray(inputWidth * inputHeight)
scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight)
val yuv = ByteArray(inputWidth * inputHeight * 3 / 2)
encodeYUV420SP(yuv, argb, inputWidth, inputHeight)
scaled.recycle()
return yuv
}
private fun encodeYUV420SP(yuv420sp: ByteArray, argb: IntArray, width: Int, height: Int) {
val frameSize = width * height
var yIndex = 0
var uvIndex = frameSize
var a: Int
var R: Int
var G: Int
var B: Int
var Y: Int
var U: Int
var V: Int
var index = 0
for (j in 0 until height) {
for (i in 0 until width) {
a = argb[index] and -0x1000000 shr 24 // a is not used obviously
R = argb[index] and 0xff0000 shr 16
G = argb[index] and 0xff00 shr 8
B = argb[index] and 0xff shr 0
Y = (66 * R + 129 * G + 25 * B + 128 shr 8) + 16
U = (-38 * R - 74 * G + 112 * B + 128 shr 8) + 128
V = (112 * R - 94 * G - 18 * B + 128 shr 8) + 128
yuv420sp[yIndex++] = (if (Y < 0) 0 else if (Y > 255) 255 else Y).toByte()
if (j % 2 == 0 && index % 2 == 0) {
yuv420sp[uvIndex++] = (if (U < 0) 0 else if (U > 255) 255 else U).toByte()
yuv420sp[uvIndex++] = (if (V < 0) 0 else if (V > 255) 255 else V).toByte()
}
index++
}
}
}
private fun checkDataListState() {
if (bitList == null) {
bitList = ArrayList()
}
if (bitFirst == null) {
bitFirst = ArrayList()
}
if (bitLast == null) {
bitLast = ArrayList()
}
}
private fun computePresentationTime(frameIndex: Long, framerate: Int): Long {
return 132 + frameIndex * 1000000 / framerate
}
companion object {
private const val MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC // H.264 Advanced Video Coding
private var _width = 512
private var _height = 512
private const val BIT_RATE = 2000000 // 800000
private const val IFRAME_INTERVAL = 1
private const val FRAME_RATE = 30 // 50
// private const val DEBUG = false
private const val TAG = "CODEC"
/**
* Returns the first codec capable of encoding the specified MIME type, or
* null if no match was found.
*/
private fun selectCodec(mimeType: String): MediaCodecInfo? {
val numCodecs = MediaCodecList.getCodecCount()
val validCodecs = mutableListOf<MediaCodecInfo>()
for (i in 0 until numCodecs) {
val codecInfo = MediaCodecList.getCodecInfoAt(i)
if (!codecInfo.isEncoder) {
continue
}
val types = codecInfo.supportedTypes
for (j in types.indices) {
if (types[j].equals(mimeType, ignoreCase = true)) {
validCodecs.add(codecInfo)
// return codecInfo
}
}
}
/**
* OMX.qcom.video.encoder.avc
* OMX.google.h264.encoder
*/
validCodecs.forEach {
Timber.d("VALID CODEC name = ${it.name}")
}
return validCodecs.firstOrNull()
}
/**
* Returns a color format that is supported by the codec and by this test
* code. If no match is found, this throws a test failure -- the set of
* formats known to the test should be expanded for new platforms.
*/
private fun selectColorFormat(codecInfo: MediaCodecInfo, mimeType: String): Int {
val capabilities = codecInfo.getCapabilitiesForType(mimeType)
capabilities.colorFormats.forEach {
Timber.d(">>> Color Format = $it")
}
for (i in capabilities.colorFormats.indices) {
val colorFormat = capabilities.colorFormats[i]
if (isRecognizedFormat(colorFormat)) {
return colorFormat
}
}
return 0 // not reached
}
/**
* Returns true if this is a color format that this test code understands
* (i.e. we know how to read and generate frames in this format).
*/
private fun isRecognizedFormat(colorFormat: Int): Boolean {
return when (colorFormat) {
CodecCapabilities.COLOR_FormatYUV420Planar,
CodecCapabilities.COLOR_FormatYUV420PackedPlanar,
CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar,
CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar -> true
else -> false
}
}
private fun Logd(Mess: String) {
Timber.d(Mess)
// if (DEBUG) {
// Log.d(TAG, Mess)
// }
}
private fun Loge(Mess: String?) {
Timber.e("$Mess")
// Log.e(TAG, Mess)
}
}
}
Loading…
Cancel
Save