|
|
|
|
@ -34,7 +34,7 @@ class MMediaMuxer { |
|
|
|
|
private var _mess: String? = null |
|
|
|
|
private var completion: ((String) -> (Unit))? = null |
|
|
|
|
|
|
|
|
|
fun Init( |
|
|
|
|
fun init( |
|
|
|
|
activity: Activity?, |
|
|
|
|
width: Int, |
|
|
|
|
height: Int, |
|
|
|
|
@ -50,26 +50,26 @@ class MMediaMuxer { |
|
|
|
|
ShowProgressBar() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun AddFrame(byteFrame: ByteArray) { |
|
|
|
|
CheckDataListState() |
|
|
|
|
fun addFrame(byteFrame: ByteArray) { |
|
|
|
|
checkDataListState() |
|
|
|
|
Thread(Runnable { |
|
|
|
|
Logd("Android get Frame") |
|
|
|
|
val bit = BitmapFactory.decodeByteArray(byteFrame, 0, byteFrame.size) |
|
|
|
|
val bitmap = BitmapFactory.decodeByteArray(byteFrame, 0, byteFrame.size) |
|
|
|
|
Logd("Android convert Bitmap") |
|
|
|
|
val byteConvertFrame = |
|
|
|
|
getNV21(bit.width, bit.height, bit) |
|
|
|
|
getNV21(bitmap.width, bitmap.height, bitmap) |
|
|
|
|
Logd("Android convert getNV21") |
|
|
|
|
bitList!!.add(byteConvertFrame) |
|
|
|
|
}).start() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun AddFrame(byteFrame: ByteArray, count: Int, isLast: Boolean) { |
|
|
|
|
fun addFrame(byteFrame: ByteArray, count: Int, isLast: Boolean) { |
|
|
|
|
var bf = byteFrame |
|
|
|
|
CheckDataListState() |
|
|
|
|
checkDataListState() |
|
|
|
|
Logd("Android get Frames = $count") |
|
|
|
|
val bit = BitmapFactory.decodeByteArray(bf, 0, bf.size) |
|
|
|
|
val bitmap = BitmapFactory.decodeByteArray(bf, 0, bf.size) |
|
|
|
|
Logd("Android convert Bitmap") |
|
|
|
|
bf = getNV21(bit.width, bit.height, bit) |
|
|
|
|
bf = getNV21(bitmap.width, bitmap.height, bitmap) |
|
|
|
|
Logd("Android convert getNV21") |
|
|
|
|
for (i in 0 until count) { |
|
|
|
|
if (isLast) { |
|
|
|
|
@ -80,7 +80,7 @@ class MMediaMuxer { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun CreateVideo(handler: (String) -> (Unit)) { |
|
|
|
|
fun createVideo(handler: (String) -> (Unit)) { |
|
|
|
|
this.completion = handler |
|
|
|
|
currentIndexFrame = 0 |
|
|
|
|
Logd("Prepare Frames Data") |
|
|
|
|
@ -122,26 +122,18 @@ class MMediaMuxer { |
|
|
|
|
private fun bufferEncoder() { |
|
|
|
|
val runnable = Runnable { |
|
|
|
|
try { |
|
|
|
|
Logd( |
|
|
|
|
"PrepareEncoder start" |
|
|
|
|
) |
|
|
|
|
PrepareEncoder() |
|
|
|
|
Logd( |
|
|
|
|
"PrepareEncoder end" |
|
|
|
|
) |
|
|
|
|
Logd("PrepareEncoder start") |
|
|
|
|
prepareEncoder() |
|
|
|
|
Logd("PrepareEncoder end") |
|
|
|
|
} catch (e: IOException) { |
|
|
|
|
Loge( |
|
|
|
|
e.message |
|
|
|
|
) |
|
|
|
|
Loge(e.message) |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
while (mRunning) { |
|
|
|
|
Encode() |
|
|
|
|
encode() |
|
|
|
|
} |
|
|
|
|
} finally { |
|
|
|
|
Logd( |
|
|
|
|
"release" |
|
|
|
|
) |
|
|
|
|
Logd("release") |
|
|
|
|
Release() |
|
|
|
|
HideProgressBar() |
|
|
|
|
bitFirst = null |
|
|
|
|
@ -159,45 +151,41 @@ class MMediaMuxer { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Throws(IOException::class) |
|
|
|
|
private fun PrepareEncoder() { |
|
|
|
|
val codecInfo = |
|
|
|
|
selectCodec( |
|
|
|
|
MIME_TYPE |
|
|
|
|
) |
|
|
|
|
if (codecInfo == null) { |
|
|
|
|
Loge("Unable to find an appropriate codec for $MIME_TYPE") |
|
|
|
|
} |
|
|
|
|
Logd("found codec: " + codecInfo!!.name) |
|
|
|
|
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( |
|
|
|
|
codecInfo, |
|
|
|
|
MIME_TYPE |
|
|
|
|
) |
|
|
|
|
selectColorFormat(codec.codecInfo, MIME_TYPE) |
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
Timber.d(">>> color format exception: $e") |
|
|
|
|
CodecCapabilities.COLOR_FormatYUV420SemiPlanar |
|
|
|
|
} |
|
|
|
|
mediaCodec = MediaCodec.createByCodecName(codecInfo.name) |
|
|
|
|
val mediaFormat = MediaFormat.createVideoFormat( |
|
|
|
|
MIME_TYPE, |
|
|
|
|
_width, |
|
|
|
|
_height |
|
|
|
|
) |
|
|
|
|
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, |
|
|
|
|
BIT_RATE |
|
|
|
|
) |
|
|
|
|
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, |
|
|
|
|
FRAME_RATE |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
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, |
|
|
|
|
INFLAME_INTERVAL |
|
|
|
|
) |
|
|
|
|
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 |
|
|
|
|
outputPath = File( |
|
|
|
|
@ -210,7 +198,7 @@ class MMediaMuxer { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun Encode() { |
|
|
|
|
private fun encode() { |
|
|
|
|
while (true) { |
|
|
|
|
if (!mRunning) { |
|
|
|
|
break |
|
|
|
|
@ -297,12 +285,8 @@ class MMediaMuxer { |
|
|
|
|
return yuv |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun encodeYUV420SP( |
|
|
|
|
yuv420sp: ByteArray, |
|
|
|
|
argb: IntArray, |
|
|
|
|
width: Int, |
|
|
|
|
height: Int |
|
|
|
|
) { |
|
|
|
|
private fun encodeYUV420SP(yuv420sp: ByteArray, argb: IntArray, width: Int, height: Int) { |
|
|
|
|
|
|
|
|
|
val frameSize = width * height |
|
|
|
|
var yIndex = 0 |
|
|
|
|
var uvIndex = frameSize |
|
|
|
|
@ -337,7 +321,7 @@ class MMediaMuxer { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun CheckDataListState() { |
|
|
|
|
private fun checkDataListState() { |
|
|
|
|
if (bitList == null) { |
|
|
|
|
bitList = ArrayList() |
|
|
|
|
} |
|
|
|
|
@ -354,12 +338,12 @@ class MMediaMuxer { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
companion object { |
|
|
|
|
private const val MIME_TYPE = "video/avc" // H.264 Advanced Video Coding |
|
|
|
|
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 = 800000 |
|
|
|
|
private const val INFLAME_INTERVAL = 1 |
|
|
|
|
private const val FRAME_RATE = 50 |
|
|
|
|
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" |
|
|
|
|
/** |
|
|
|
|
@ -368,19 +352,32 @@ class MMediaMuxer { |
|
|
|
|
*/ |
|
|
|
|
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)) { |
|
|
|
|
return codecInfo |
|
|
|
|
validCodecs.add(codecInfo) |
|
|
|
|
// return codecInfo |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null |
|
|
|
|
/** |
|
|
|
|
* OMX.qcom.video.encoder.avc |
|
|
|
|
* OMX.google.h264.encoder |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
validCodecs.forEach { |
|
|
|
|
Timber.d("VALID CODEC name = ${it.name}") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return validCodecs.firstOrNull() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -388,18 +385,17 @@ class MMediaMuxer { |
|
|
|
|
* 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) |
|
|
|
|
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 |
|
|
|
|
) |
|
|
|
|
) { |
|
|
|
|
if (isRecognizedFormat(colorFormat)) { |
|
|
|
|
return colorFormat |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|