<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      MediaCodec使用之MP4解碼(三)

      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...")
      }
      
      posted @ 2025-08-17 17:36  愛情丶眨眼而去  閱讀(13)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 9久9久热精品视频在线观看 | 精品无码老熟妇magnet | 欧美日韩视频综合一区无弹窗| 亚洲第一香蕉视频啪啪爽| 狠狠躁夜夜躁无码中文字幕| 色欧美片视频在线观看| 欧美成人精精品一区二区三区| 亚洲国产成人午夜在线一区| 免费吃奶摸下激烈视频| 午夜福利看片在线观看| japan黑人极大黑炮| 成人精品老熟妇一区二区| 免费无码AV一区二区波多野结衣| a级黑人大硬长爽猛出猛进| 亚洲精品乱码久久久久久按摩高清 | 精人妻无码一区二区三区| 国产熟女高潮一区二区三区 | 国产精品国产精品无卡区| 久久久久综合中文字幕| 国产精品无码一区二区桃花视频 | 伊人欧美在线| 国产小视频一区二区三区| 亚洲 日本 欧洲 欧美 视频 | 弥渡县| 国产精品爱久久久久久久电影| 色噜噜亚洲男人的天堂| 徐水县| 思思久99久女女精品| 男女真人国产牲交a做片野外| 亚洲一区二区中文av| 中文字幕无码不卡在线| 九色国产精品一区二区久久| 国产精品久久中文字幕| 国产一区二区三区自拍视频| 国产成人精品免费视频大全| 老司机亚洲精品一区二区| 欧美熟妇性XXXX欧美熟人多毛| 久久精品不卡一区二区| 国产99在线 | 欧美| 日本欧美大码a在线观看| 99福利一区二区视频|