MediaCodec使用之编码为MP4(四)

MediaCodec使用之编码为MP4(四)

将上面一节mp4生成的yuv编码为h264,逆过程
为什么要编码,原因就是02:30s的mp4视频才12.02MB, yuv格式竟然要我11.60GB!!!
上一节解码的视频可以使用以下命令播放

 ffplay -f rawvideo -pixel_format nv12 -video_size 672x1280 -framerate 60 output.yuv

注意
MediaCode编码h264的时候,喂的数据必须是nv21,记得转换一下

//  ffplay -x 1280 -y 720 -f rawvideo -pixel_format yuv420p -video_size 3840x2176 -framerate 60 output1.yuv
internal suspend fun yuvToh264(context: Context, yuvUri: Uri, h264Uri: Uri): Unit = suspendCancellableCoroutine{ continuation ->

	val mediaCodecList = MediaCodecList(MediaCodecList.ALL_CODECS)

	// 视频解码器材
	val mkvDecoders: List<MediaCodecInfo> = mediaCodecList.codecInfos
		.filter { !it.isEncoder && MediaFormat.MIMETYPE_VIDEO_HEVC in it.supportedTypes }

	if (mkvDecoders.isEmpty()){
		Log.i(TAG, "videoToYuvPcm -> 不支持mkv解码")
		if (continuation.isActive){
			continuation.resume(Unit)
		}
		return@suspendCancellableCoroutine
	}

	// 拿解码器
	val mkvDecoder: MediaCodecInfo = mkvDecoders.firstOrNull {
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
			it.isHardwareAccelerated
		} else {
			true
		}
	} ?: mkvDecoders.first()

	// 重试解码器 android 8 不支持  hevc (Main 10)
	// val mkvDecoder: MediaCodecInfo = mkvDecoders[1]
	// MediaCodec.createByCodecName(h264Decoder.name)
	Log.i(TAG, "videoToYuvPcm1 -> mkvDecoderName: ${mkvDecoder.name}")

	mkvDecoders.forEach { mediaCodecInfo ->
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
			Log.i(TAG, "mkvDecoders -> name: ${mediaCodecInfo.name}, canonicalName: ${mediaCodecInfo.canonicalName}, isAlias: ${mediaCodecInfo.isAlias}, isVendor: ${mediaCodecInfo.isVendor}, isHardwareAccelerated: ${mediaCodecInfo.isHardwareAccelerated}, isEncoder: ${mediaCodecInfo.isEncoder}, isSoftwareOnly: ${mediaCodecInfo.isSoftwareOnly}, supportedTypes: ${mediaCodecInfo.supportedTypes.joinToString()}")
		} else {
			Log.i(TAG, "mkvDecoders -> name: ${mediaCodecInfo.name}, isEncoder: ${mediaCodecInfo.isEncoder}, supportedTypes: ${mediaCodecInfo.supportedTypes.joinToString()}")
		}
		mediaCodecInfo.supportedTypes.forEach { mimeType: String ->
			if (mimeType.lowercase() == MediaFormat.MIMETYPE_VIDEO_HEVC){
				val mediaCodecInfoCodecCapabilities: MediaCodecInfo.CodecCapabilities = mediaCodecInfo.getCapabilitiesForType(mimeType)
				mediaCodecInfoCodecCapabilities.profileLevels.forEach { codecProfileLevel ->
					if (codecProfileLevel.profile == MediaCodecInfo.CodecProfileLevel.HEVCProfileMain){
						if (codecProfileLevel.level >= MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel62){
							Log.i(TAG, "h265ToYuvPcm -> 支持 8k h265 name: ${mediaCodecInfo.name}")
						} else {
							Log.i(TAG, "h265ToYuvPcm -> 不支持 8k h265 name: ${mediaCodecInfo.name}")
						}
					}
				}
			}
		}
	}

	val width = 672      /* 662 */
	val height = 1280

	val frameRate = 60
	val frameSize = width * height * 3 / 2

	val mediaFormat: MediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height).apply {
		setInteger(MediaFormat.KEY_FRAME_RATE, frameRate)
		setInteger(MediaFormat.KEY_BIT_RATE, 2_000_000) // 可根据分辨率调整
		setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible)
		setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1)
	}

	val mediaCodec: MediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)
	mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)

	val yuvInputStream: InputStream = context.contentResolver.openInputStream(yuvUri)!!
	val h264OutputStream: OutputStream = context.contentResolver.openOutputStream(h264Uri)!!
	val bytes = ByteArray(frameSize)

	mediaCodec.setCallback(object : MediaCodec.Callback() {
		override fun onError(
			codec: MediaCodec,
			e: MediaCodec.CodecException
		) {
			Log.e(
				TAG,
				"onError name: ${codec.name}, thread: ${Thread.currentThread()}, error: ${e.message}",
				e
			)
		}

		override fun onInputBufferAvailable(
			codec: MediaCodec,
			index: Int
		) {
			Log.i(
				TAG,
				"onInputBufferAvailable -> name: ${mediaCodec.name}, index: $index, thread: ${Thread.currentThread()}"
			)
			val inputBuffer: ByteBuffer = codec.getInputBuffer(index) ?: return
			val size: Int = yuvInputStream.read(bytes, 0, frameSize)
			if (size == frameSize) {
				inputBuffer.put(bytes, 0, size)
				codec.queueInputBuffer(index, 0, size, System.nanoTime() / 1000, 0)
			} else {
				codec.queueInputBuffer(
					index,
					0,
					0,
					System.nanoTime() / 1000,
					MediaCodec.BUFFER_FLAG_END_OF_STREAM
				)
			}
		}

		override fun onOutputBufferAvailable(
			codec: MediaCodec,
			index: Int,
			info: MediaCodec.BufferInfo
		) {
			Log.i(
				TAG,
				"onOutputBufferAvailable -> name: ${codec.name}, index: $index, info: ${info.size}, thread: ${Thread.currentThread()}"
			)

			val outputBuffer: ByteBuffer = codec.getOutputBuffer(index) ?: return

			outputBuffer.get(bytes, 0, info.size)
			h264OutputStream.write(bytes, 0, info.size)

			codec.releaseOutputBuffer(index, false)

			if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
				Log.i(TAG, "onOutputBufferAvailable -> == 编码结束...") // todo
				yuvInputStream.close()
				h264OutputStream.close()
				if (continuation.isActive) {
					Log.i(TAG, "pcmToAac -> 解码完成 resume before...")
					continuation.resume(Unit)
					Log.i(TAG, "pcmToAac -> 解码完成 resume after...")
				}
			}
		}


		override fun onOutputFormatChanged(
			codec: MediaCodec,
			format: MediaFormat
		) {
			Log.i(
				TAG,
				"onOutputFormatChanged -> name: ${codec.name}, format: $format"
			)
		}
	})
	Log.i(TAG, "pcmToAac -> before start...")
	mediaCodec.start()
	Log.i(TAG, "pcmToAac -> after start...")
}

/**
     * yuv420p 转 nv21
     */
fun yuv420pToNv21(yuv420p: ByteArray, nv12: ByteArray, width: Int, height: Int) {

	val frameSize: Int = width * height
	val qFrameSize: Int = frameSize / 4
	// Y 拷贝
	System.arraycopy(yuv420p, 0, nv12, 0, frameSize)

	val uStart: Int = frameSize
	val vStart: Int = frameSize + qFrameSize
	var uvIndex: Int = frameSize

	for (i in 0 until qFrameSize) {
		nv12[uvIndex++] = yuv420p[vStart + i] // V
		nv12[uvIndex++] = yuv420p[uStart + i] // U
	}
}
posted @ 2025-08-17 22:58  爱情丶眨眼而去  阅读(12)  评论(0)    收藏  举报