问题描述
我正在尝试使用 MediaCodec
和 MediaExtractor
在 Android 中将视频解码到表面上。
首先,我使用 setDataSource(context,uri,null)
对象(下面的MediaExtractor
)上的 extractor
方法来设置数据源 uri。 uri 来自视频的 Android 文件选择器。这个调用似乎没有抛出任何异常。
在解码器的 onInputBufferAvailable()
调用中,我然后从提取器中读取了一个新样本,但 extractor.readSampleData(..)
调用随机无法读取正确的数据并开始返回大小为 -1 的缓冲区。理想情况下,读取整个文件后,大小应该仅为 -1。此外,一旦提取器开始返回 -1,无论我多次调用 extractor.advance()
,它都不会返回正确的样本。
每当发生此问题时,我都会在日志中观察到此警告消息:
W/NuMediaExtractor: read on track 0 Failed with error -2147483646
代码:
object : MediaCodec.Callback() {
override fun onInputBufferAvailable(codec: MediaCodec,index: Int) {
val buffer = codec.getInputBuffer(index)!!
try {
val size = extractor.readSampleData(buffer,0)
if (size > 0) {
decoder.queueInputBuffer(
index,size,extractor.sampleTime,sampleFlags
)
extractor.advance()
} else if (size == 0) {
Timber.d("Size 0 sample received from extractor")
} else if (size == -1) {
// Size is -1 when no more samples are available
} catch (exception: Exception) {
Timber.e(exception)
}
}
这种情况不会一直发生,但足够频繁。到目前为止,我只在 Android 9 操作系统上观察到了这一点。
编辑 1:为了防止竞争条件,我将空闲输入缓冲区保存到阻塞队列中。然后一个 while 循环从队列中取出这些缓冲区。
private val inputBuffersQueue: BlockingQueue<InputBufferData> = LinkedBlockingQueue()
private fun FeedSamplesToDecoder(extractor: MediaExtractor,trimstartUs: Long,trimEndUs: Long) {
inputHandler.post {
while (!waseosinputBufferFed) {
val inputBuffer = inputBuffersQueue.take()
val decoder = inputBuffer.codec
val index = inputBuffer.index
val sampleSize = extractor.readSampleData(inputBuffer.codec.getInputBuffer(inputBuffer.index)!!,0)
if (sampleSize > 0) {
val sampleTime = extractor.sampleTime
lastSampleTimestampus = sampleTime
val sampleFlags = extractor.sampleFlags
decoder.queueInputBuffer(
index,sampleSize,sampleTime - trimstartUs,sampleFlags
)
extractor.advance()
} else if (sampleSize == 0) {
Timber.d("Size 0 sample received from extractor")
} else {
decoder.queueInputBuffer(
index,BUFFER_FLAG_END_OF_STREAM
)
}
}
}
}
解决方法
我认为你的根本问题是 uri。我的建议是将其重新解析为被许多应用程序广泛接受的更常见的内容。
比如你的uri是这样的(以content://开头)content://com.android.providers.downloads.documents/document/3356
,试着把它改成file:///storage/emulated/0/document/3356
。 com.android.providers.downloads.documents
是包名,document/3356
是文件路径。您可以通过 /storage/emulated/0
方法获得 Environment.getExternalStorageDirectory()
。然后使用 Uri.parse("string")
获取 uri 实例。
Uri uri = Uri.parse("file:///storage/emulated/0/document/3356");
,
首先在谷歌上搜索“NuMediaExtractor:在轨道 0 上读取失败,错误为 -2147483646”。 我看到 2 个结果:A、B
他们写了关于在 Android 10 中修复的错误。关于比特率太大并且需要重新编码视频的内容。
我仍然建议找到可能的解决方法。不要使用 setDataSource(Context,Uri,Map),而是尝试调用 setDataSource(MediaDataSource),实现自己的 MediaDataSource,只有 2 个方法:getSize
返回文件大小的位置,以及 readAt
位置您从文件中完全读取请求的整个大小(在 InputStream 有时会执行部分读取后不要返回;仅在文件末尾才允许部分读取)。您可以通过使用 RandomAccessFile 加载固定文件来对此进行调试,如果您发现错误已解决,您可以实现自己的 MediaDataSource 读取 content:// 方案(由系统文件选择器返回)。
您可以在 MediaDataSource 中记录读取,并将日志与在另一台未发生错误的设备上成功运行的日志进行比较(在同一文件上测试)。也许你会看到 Android 在 Android 9 上请求不同的范围,或者它请求相同的读取但仍然失败。那么这是 Android 错误,您可能无法修复。
希望这能帮助您更接近于找到问题的原因,甚至解决问题。