MediaCodec使用之视频解码(三)
准备三个视频,一个是普通的h264视频,一个是8k h265的,一个是电影的mkv
使用fflay播放并查看详细信息。
普通视频- Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '.\video1.mp4': 0B
- Metadata:
- major_brand : isom
- minor_version : 512
- compatible_brands: isomiso2avc1mp41
- encoder : Lavf58.29.100
- Duration: 00:02:29.94, start: 0.000000, bitrate: 641 kb/s
- Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 662x1280 [SAR 1:1 DAR 331:640], 499 kb/s, 59.96 fps, 60 tbr, 15360 tbn (default)
- Metadata:
- handler_name : VideoHandler
- vendor_id : [0][0][0][0]
- Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default)
- Metadata:
- handler_name : SoundHandler
- vendor_id : [0][0][0][0]
- 5.30 A-V: -0.031 fd= 24 aq= 16KB vq= 55KB sq= 0B
复制代码 h265的- Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '.\video2.mp4': 0B
- Metadata:
- major_brand : mp42
- minor_version : 0
- compatible_brands: isommp42
- creation_time : 2025-08-17T03:21:22.000000Z
- location : +40.0770+116.4052/
- location-eng : +40.0770+116.4052/
- com.android.version: 15
- Duration: 00:00:15.90, start: 0.000000, bitrate: 81199 kb/s
- Stream #0:0[0x1](eng): Video: hevc (Main) (hvc1 / 0x31637668), yuv420p(tv, bt709), 7680x4320, 80753 kb/s, 30 fps, 30 tbr, 90k tbn (default)
- Metadata:
- creation_time : 2025-08-17T03:21:22.000000Z
- handler_name : VideoHandle
- vendor_id : [0][0][0][0]
- Stream #0:1[0x2](eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 256 kb/s (default)
- Metadata:
- creation_time : 2025-08-17T03:21:22.000000Z
- handler_name : SoundHandle
- vendor_id : [0][0][0][0]
复制代码 电影- ffplay -x 1280 -y 720 -ss 00:50:00 video3.mkv
- Input #0, matroska,webm, from '.\video3.mkv':
- Metadata:
- title : Black.Widow.2021.2160p.BluRay.REMUX.HEVC.DTS-HD.MA.7.1.TrueHD.7.1.Atmos.DTS-HD.MA.7.1.zh-老K
- encoder : libebml v1.4.2 + libmatroska v1.6.4
- creation_time : 2024-04-06T01:57:58.000000Z
- Duration: 02:13:46.79, start: 0.000000, bitrate: 57664 kb/s
- Chapters:
- Chapter #0:0: start 0.000000, end 295.169867
- Metadata:
- title : 第 01 章
- Chapter #0:1: start 295.169867, end 790.080956
- Metadata:
- title : 第 02 章
- ...
- Stream #0:0: Video: hevc (Main 10), yuv420p10le(tv, bt2020nc/bt2020/smpte2084), 3840x2160 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn (default)
- Metadata:
- BPS : 46197111
- DURATION : 02:13:46.769000000
- NUMBER_OF_FRAMES: 192450
- NUMBER_OF_BYTES : 46351692620
- SOURCE_ID : 001011
- _STATISTICS_WRITING_APP: mkvmerge v68.0.0 ('The Curtain') 64-bit
- _STATISTICS_WRITING_DATE_UTC: 2024-04-06 01:57:58
- _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES SOURCE_ID
- Stream #0:1(eng): Audio: truehd (Dolby TrueHD + Dolby Atmos), 48000 Hz, 7.1, s32 (24 bit) (default)
- Metadata:
- title : TrueHD Atmos 7.1
- BPS : 4284386
- DURATION : 02:13:46.769000000
- NUMBER_OF_FRAMES: 9632122
- NUMBER_OF_BYTES : 4298722162
- SOURCE_ID : 001100
- _STATISTICS_WRITING_APP: mkvmerge v68.0.0 ('The Curtain') 64-bit
- _STATISTICS_WRITING_DATE_UTC: 2024-04-06 01:57:58
- _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES SOURCE_ID
- Stream #0:2(eng): Audio: dts (dca) (DTS-HD MA), 48000 Hz, 7.1, s32p (24 bit)
- Metadata:
- title : DTS-HD MA 7.1
- BPS : 4411433
- DURATION : 02:13:46.794000000
- NUMBER_OF_FRAMES: 752481
- NUMBER_OF_BYTES : 4426208460
- _STATISTICS_WRITING_APP: mkvmerge v68.0.0 ('The Curtain') 64-bit
- _STATISTICS_WRITING_DATE_UTC: 2024-04-06 01:57:58
- _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
- Stream #0:3(chi): Audio: dts (dca) (DTS-HD MA), 48000 Hz, 7.1, s16p
- Metadata:
- title : 国语 DTS-HD MA 7.1
- BPS : 2647732
- DURATION : 02:13:46.773000000
- NUMBER_OF_FRAMES: 752510
- NUMBER_OF_BYTES : 2656593104
- SOURCE_ID : 001105
- _STATISTICS_WRITING_APP: mkvmerge v68.0.0 ('The Curtain') 64-bit
- _STATISTICS_WRITING_DATE_UTC: 2024-04-06 01:57:58
- _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES SOURCE_ID
- Stream #0:4(chi): Subtitle: ass (ssa) (default)
- Metadata:
- title : 简英
- BPS : 199
- DURATION : 02:12:27.160000000
- NUMBER_OF_FRAMES: 1376
- NUMBER_OF_BYTES : 198336
- _STATISTICS_WRITING_APP: mkvmerge v68.0.0 ('The Curtain') 64-bit
- _STATISTICS_WRITING_DATE_UTC: 2024-04-06 01:57:58
- _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
- Stream #0:5(chi): Subtitle: hdmv_pgs_subtitle (pgssub)
- Metadata:
- title : 国语 简体
- BPS : 93951
- DURATION : 02:13:00.556000000
- NUMBER_OF_FRAMES: 6703
- NUMBER_OF_BYTES : 93723446
- SOURCE_ID : 0012a4
- _STATISTICS_WRITING_APP: mkvmerge v68.0.0 ('The Curtain') 64-bit
- _STATISTICS_WRITING_DATE_UTC: 2024-04-06 01:57:58
- _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES SOURCE_ID
- Stream #0:6: Video: mjpeg (Progressive), yuvj420p(pc, bt470bg/unknown/unknown), 254x254 [SAR 1:1 DAR 1:1], 90k tbr, 90k tbn (attached pic)
- Metadata:
- filename : 老K QQ 195383233.jpg
- mimetype : image/jpeg
复制代码 解码普通视频
- internal suspend fun videoToYuvPcm(context: Context, videoUri: Uri, yuvUri: Uri, pcmUri: Uri): Unit {
- val mediaCodecList = MediaCodecList(MediaCodecList.ALL_CODECS)
- // 视频解码器材
- val h264Decoders: List<MediaCodecInfo> = mediaCodecList.codecInfos
- .filter { !it.isEncoder && MediaFormat.MIMETYPE_VIDEO_AVC in it.supportedTypes }
- if (h264Decoders.isEmpty()){
- Log.i(TAG, "videoToYuvPcm -> 不支持h264解码")
- return
- }
- // 拿解码器
- val h264Decoder: MediaCodecInfo = h264Decoders.firstOrNull {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- it.isHardwareAccelerated
- } else {
- true
- }
- } ?: h264Decoders.first()
- // MediaCodec.createByCodecName(h264Decoder.name)
- h264Decoders.forEach { mediaCodecInfo ->
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- Log.i(TAG, "h264Decoders -> 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, "h264Decoders -> name: ${mediaCodecInfo.name}, isEncoder: ${mediaCodecInfo.isEncoder}, supportedTypes: ${mediaCodecInfo.supportedTypes.joinToString()}")
- }
- }
- // 音频解码器
- val aacDecoders: List<MediaCodecInfo> = mediaCodecList.codecInfos.filter { !it.isEncoder }.filter {
- it.supportedTypes.contains(element = MediaFormat.MIMETYPE_AUDIO_AAC)
- }
- if (aacDecoders.isEmpty()){
- Log.i(TAG, "videoToYuvPcm -> 不支持aac解码")
- return
- }
- val aacDecoder: MediaCodecInfo = aacDecoders.firstOrNull {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- it.isHardwareAccelerated
- } else {
- true
- }
- } ?: aacDecoders.first()
- aacDecoders.forEach { mediaCodecInfo ->
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- Log.i(TAG, "aacDecoders -> 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, "aacDecoders -> name: ${mediaCodecInfo.name}, isEncoder: ${mediaCodecInfo.isEncoder}, supportedTypes: ${mediaCodecInfo.supportedTypes.joinToString()}")
- }
- }
- val mediaExtractor = MediaExtractor()
- mediaExtractor.setDataSource(context, videoUri, mapOf<String, String>())
- for (i in 0 until mediaExtractor.trackCount){
- Log.i(TAG, "flacToPcm -> format: ${mediaExtractor.getTrackFormat(i)}")
- }
- if (mediaExtractor.trackCount < 2){
- mediaExtractor.release()
- return
- }
- Log.i(TAG, "videoToYuvPcm -> decode before...")
- //
- decode(context, 0, yuvUri, mediaExtractor, h264Decoder.name)
- // ffplay -f s16le -ar 44100 -ch_layout stereo -i output.pcm
- decode(context, 1, pcmUri, mediaExtractor, aacDecoder.name)
- Log.i(TAG, "videoToYuvPcm -> decode after...")
- mediaExtractor.release()
- Log.i(TAG, "videoToYuvPcm -> end...")
- }
- private suspend fun decode(
- context: Context,
- index: Int,
- output: Uri,
- mediaExtractor: MediaExtractor,
- decodeName: String
- ): Unit = suspendCancellableCoroutine { continuation ->
- val mediaFormat: MediaFormat = mediaExtractor.getTrackFormat(index)
- mediaExtractor.selectTrack(index)
- val mediaCodec: MediaCodec = MediaCodec.createByCodecName(decodeName)
- mediaCodec.configure(mediaFormat, null, null, 0)
- val aacOutputStream: OutputStream = context.contentResolver.openOutputStream(output)!!
- val bytes = ByteArray(1024 * 1024 * 2)
- 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
- ) {
- val inputBuffer: ByteBuffer = codec.getInputBuffer(index) ?: return
- val size: Int = mediaExtractor.readSampleData(inputBuffer, 0)
- Log.i(
- TAG,
- "onInputBufferAvailable -> name: ${mediaCodec.name}, index: $index, thread: ${Thread.currentThread()}, size: $size"
- )
- if (size > 0) {
- codec.queueInputBuffer(index, 0, size, mediaExtractor.sampleTime, mediaExtractor.sampleFlags)
- mediaExtractor.advance()
- } else {
- codec.queueInputBuffer(
- index,
- 0,
- 0,
- 0,
- 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, infoSize: ${info.size}, thread: ${Thread.currentThread()}"
- )
- val outputBuffer: ByteBuffer = codec.getOutputBuffer(index) ?: return
- outputBuffer.get(bytes, 0, info.size)
- aacOutputStream.write(bytes, 0, info.size)
- codec.releaseOutputBuffer(index, false)
- if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
- Log.i(TAG, "onOutputBufferAvailable -> == 编码结束...") // todo
- aacOutputStream.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...")
- }
复制代码 播放生成的音视频- ffplay -f s16le -ar 44100 -ch_layout stereo -i output.pcm
- ffplay -f rawvideo -pixel_format yuv420p -video_size 672x1280 -framerate 60 output.yuv
复制代码 8k h265
记得视频搞短一点,有些解码器会吞掉EOS标识,不清楚为什么- private suspend fun decode(
- context: Context,
- index: Int,
- output: Uri,
- mediaExtractor: MediaExtractor,
- decodeName: String
- ): Unit = suspendCancellableCoroutine { continuation ->
- val mediaFormat: MediaFormat = mediaExtractor.getTrackFormat(index)
- mediaExtractor.selectTrack(index)
- val mediaCodec: MediaCodec = MediaCodec.createByCodecName(decodeName)
- mediaCodec.configure(mediaFormat, null, null, 0)
- val aacOutputStream: OutputStream = context.contentResolver.openOutputStream(output)!!
- val bytes = ByteArray(1024 * 1024 * 100)
- 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
- ) {
- val inputBuffer: ByteBuffer = codec.getInputBuffer(index) ?: return
- val size: Int = mediaExtractor.readSampleData(inputBuffer, 0)
- Log.i(
- TAG,
- "onInputBufferAvailable -> name: ${mediaCodec.name}, index: $index, thread: ${Thread.currentThread()}, size: $size"
- )
- if (size > 0) {
- codec.queueInputBuffer(index, 0, size, mediaExtractor.sampleTime, mediaExtractor.sampleFlags)
- mediaExtractor.advance()
- } else {
- codec.queueInputBuffer(
- index,
- 0,
- 0,
- 0,
- MediaCodec.BUFFER_FLAG_END_OF_STREAM
- )
- Log.i(TAG, "onInputBufferAvailable -> BUFFER_FLAG_END_OF_STREAM")
- }
- }
- override fun onOutputBufferAvailable(
- codec: MediaCodec,
- index: Int,
- info: MediaCodec.BufferInfo
- ) {
- Log.i(
- TAG,
- "onOutputBufferAvailable -> name: ${codec.name}, index: $index, infoSize: ${info.size}, thread: ${Thread.currentThread()}"
- )
- val outputBuffer: ByteBuffer = codec.getOutputBuffer(index) ?: return
- outputBuffer.get(bytes, 0, info.size)
- aacOutputStream.write(bytes, 0, info.size)
- codec.releaseOutputBuffer(index, false)
- if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
- Log.i(TAG, "onOutputBufferAvailable -> == 编码结束...") // todo
- aacOutputStream.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...")
- }
- internal suspend fun h265ToYuvPcm(context: Context, videoUri: Uri, yuvUri: Uri, pcmUri: Uri){
- 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解码")
- return
- }
- // 拿解码器
- 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 aacDecoders: List<MediaCodecInfo> = mediaCodecList.codecInfos.filter { !it.isEncoder }.filter {
- it.supportedTypes.contains(element = MediaFormat.MIMETYPE_AUDIO_AAC)
- }
- if (aacDecoders.isEmpty()){
- Log.i(TAG, "videoToYuvPcm -> 不支持aac解码")
- return
- }
- val aacDecoder: MediaCodecInfo = aacDecoders.firstOrNull {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- it.isHardwareAccelerated
- } else {
- true
- }
- } ?: aacDecoders.first()
- aacDecoders.forEach { mediaCodecInfo ->
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- Log.i(TAG, "aacDecoders -> 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, "aacDecoders -> name: ${mediaCodecInfo.name}, isEncoder: ${mediaCodecInfo.isEncoder}, supportedTypes: ${mediaCodecInfo.supportedTypes.joinToString()}")
- }
- }
- val mediaExtractor = MediaExtractor()
- mediaExtractor.setDataSource(context, videoUri, mapOf<String, String>())
- for (i in 0 until mediaExtractor.trackCount){
- Log.i(TAG, "flacToPcm -> format: ${mediaExtractor.getTrackFormat(i)}")
- }
- if (mediaExtractor.trackCount < 1){
- mediaExtractor.release()
- return
- }
- Log.i(TAG, "videoToYuvPcm -> decode before...")
- //
- decode(context, 0, yuvUri, mediaExtractor, mkvDecoder.name)
- decode(context, 1, pcmUri, mediaExtractor, aacDecoder.name)
- // ffplay -f s16le -ar 44100 -ch_layout stereo -i output.pcm
- Log.i(TAG, "videoToYuvPcm -> decode after...")
- mediaExtractor.release()
- Log.i(TAG, "videoToYuvPcm -> end...")
- }
复制代码 使用下面的命令- # 8K 265
- ffplay -x 1280 -y 720 -f rawvideo -pixel_format nv12 -video_size 7680x4320 -framerate 30 output2.yuv
- # 4k 256
- ffplay -x 1280 -y 720 -f rawvideo -pixel_format yuv420p -video_size 3840x2176 -framerate 60 output1.yuv
- ffplay -f s16le -ar 48000 -ch_layout stereo -i output.pcm
复制代码 否则会有下面的错误- 2025-08-17 16:34:05.005 10483-10483 AndroidRuntime edu.tyut.helloktorfit E FATAL EXCEPTION: main
- Process: edu.tyut.helloktorfit, PID: 10483
- java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
- at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631)
- at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:957)
- Caused by: java.lang.reflect.InvocationTargetException
- at java.lang.reflect.Method.invoke(Native Method)
- at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:621)
- at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:957)
- Caused by: java.io.IOException: write failed: ENOSPC (No space left on device)
- at libcore.io.IoBridge.write(IoBridge.java:651)
- at java.io.FileOutputStream.write(FileOutputStream.java:432)
- at edu.tyut.helloktorfit.manager.AudioExtractManager$decode$2$1.onOutputBufferAvailable(AudioExtractManager.kt:1051)
- at android.media.MediaCodec$EventHandler.handleCallback(MediaCodec.java:2011)
- at android.media.MediaCodec$EventHandler.handleMessage(MediaCodec.java:1887)
- at android.os.Handler.dispatchMessage(Handler.java:109)
- at android.os.Looper.loopOnce(Looper.java:250)
- at android.os.Looper.loop(Looper.java:340)
- at android.app.ActivityThread.main(ActivityThread.java:9865)
- at java.lang.reflect.Method.invoke(Native Method)
- at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:621)
- at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:957)
- Caused by: android.system.ErrnoException: write failed: ENOSPC (No space left on device)
- at libcore.io.Linux.writeBytes(Native Method)
- at libcore.io.Linux.write(Linux.java:296)
- at libcore.io.ForwardingOs.write(ForwardingOs.java:943)
- at libcore.io.BlockGuardOs.write(BlockGuardOs.java:448)
- at libcore.io.ForwardingOs.write(ForwardingOs.java:943)
复制代码 mkv电影
很多机型均无法解析4k mkv电影- private suspend fun decode(
- context: Context,
- index: Int,
- output: Uri,
- mediaExtractor: MediaExtractor,
- decodeName: String
- ): Unit = suspendCancellableCoroutine { continuation ->
- val mediaFormat: MediaFormat = mediaExtractor.getTrackFormat(index)
- mediaExtractor.selectTrack(index)
- val mediaCodec: MediaCodec = MediaCodec.createByCodecName(decodeName)
- mediaCodec.configure(mediaFormat, null, null, 0)
- val aacOutputStream: OutputStream = context.contentResolver.openOutputStream(output)!!
- val bytes = ByteArray(1024 * 1024 * 100)
- 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
- ) {
- val inputBuffer: ByteBuffer = codec.getInputBuffer(index) ?: return
- val size: Int = mediaExtractor.readSampleData(inputBuffer, 0)
- Log.i(
- TAG,
- "onInputBufferAvailable -> name: ${mediaCodec.name}, index: $index, thread: ${Thread.currentThread()}, size: $size"
- )
- if (size > 0) {
- codec.queueInputBuffer(index, 0, size, mediaExtractor.sampleTime, mediaExtractor.sampleFlags)
- mediaExtractor.advance()
- } else {
- codec.queueInputBuffer(
- index,
- 0,
- 0,
- 0,
- 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, infoSize: ${info.size}, thread: ${Thread.currentThread()}"
- )
- val outputBuffer: ByteBuffer = codec.getOutputBuffer(index) ?: return
- outputBuffer.get(bytes, 0, info.size)
- aacOutputStream.write(bytes, 0, info.size)
- codec.releaseOutputBuffer(index, false)
- if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
- Log.i(TAG, "onOutputBufferAvailable -> == 编码结束...") // todo
- aacOutputStream.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...")
- }
- internal suspend fun videoToYuvPcm1(context: Context, videoUri: Uri, yuvUri: Uri): Unit {
- 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解码")
- return
- }
- // 拿解码器
- 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[2]
- // 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()}")
- }
- }
- // 音频解码器
- val aacDecoders: List<MediaCodecInfo> = mediaCodecList.codecInfos.filter { !it.isEncoder }.filter {
- it.supportedTypes.contains(element = MediaFormat.MIMETYPE_AUDIO_AAC)
- }
- if (aacDecoders.isEmpty()){
- Log.i(TAG, "videoToYuvPcm -> 不支持aac解码")
- return
- }
- val aacDecoder: MediaCodecInfo = aacDecoders.firstOrNull {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- it.isHardwareAccelerated
- } else {
- true
- }
- } ?: aacDecoders.first()
- aacDecoders.forEach { mediaCodecInfo ->
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- Log.i(TAG, "aacDecoders -> 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, "aacDecoders -> name: ${mediaCodecInfo.name}, isEncoder: ${mediaCodecInfo.isEncoder}, supportedTypes: ${mediaCodecInfo.supportedTypes.joinToString()}")
- }
- }
- val mediaExtractor = MediaExtractor()
- mediaExtractor.setDataSource(context, videoUri, mapOf<String, String>())
- for (i in 0 until mediaExtractor.trackCount){
- Log.i(TAG, "flacToPcm -> format: ${mediaExtractor.getTrackFormat(i)}")
- }
- if (mediaExtractor.trackCount < 1){
- mediaExtractor.release()
- return
- }
- Log.i(TAG, "videoToYuvPcm -> decode before...")
- //
- decode(context, 0, yuvUri, mediaExtractor, mkvDecoder.name)
- // ffplay -f s16le -ar 44100 -ch_layout stereo -i output.pcm
- Log.i(TAG, "videoToYuvPcm -> decode after...")
- mediaExtractor.release()
- Log.i(TAG, "videoToYuvPcm -> end...")
- }
复制代码 来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |