fix(android): strip DV init data
This commit is contained in:
+150
-37
@@ -28,7 +28,8 @@ import androidx.media3.extractor.TrackOutput
|
|||||||
*/
|
*/
|
||||||
class DoviConvertingTrackOutput(
|
class DoviConvertingTrackOutput(
|
||||||
private val delegate: TrackOutput,
|
private val delegate: TrackOutput,
|
||||||
private val dvMode: DvConversionMode = DvConversionMode.HEVC_STRIP
|
private val dvMode: DvConversionMode = DvConversionMode.HEVC_STRIP,
|
||||||
|
private val emitLog: ((String, String, String) -> Unit)? = null
|
||||||
) : TrackOutput {
|
) : TrackOutput {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -45,6 +46,8 @@ class DoviConvertingTrackOutput(
|
|||||||
private set
|
private set
|
||||||
var strippedNalCount = 0L
|
var strippedNalCount = 0L
|
||||||
private set
|
private set
|
||||||
|
var strippedInitNalCount = 0L
|
||||||
|
private set
|
||||||
var convertedRpuCount = 0L
|
var convertedRpuCount = 0L
|
||||||
private set
|
private set
|
||||||
var rpuConversionFailureCount = 0L
|
var rpuConversionFailureCount = 0L
|
||||||
@@ -75,13 +78,13 @@ class DoviConvertingTrackOutput(
|
|||||||
val codecs = format.codecs
|
val codecs = format.codecs
|
||||||
if (codecs != null && codecs.startsWith("dvhe.07")) {
|
if (codecs != null && codecs.startsWith("dvhe.07")) {
|
||||||
conversionActive = true
|
conversionActive = true
|
||||||
Log.i(TAG, "DV Profile 7 detected ($codecs), mode=$dvMode")
|
logInfo("DV Profile 7 detected ($codecs), mode=$dvMode")
|
||||||
Log.i(
|
logInfo(
|
||||||
TAG,
|
|
||||||
"Original format: mime=${format.sampleMimeType}, codecs=$codecs, " +
|
"Original format: mime=${format.sampleMimeType}, codecs=$codecs, " +
|
||||||
"initData=${format.initializationData.size} entries " +
|
"initData=${format.initializationData.size} entries " +
|
||||||
"(${format.initializationData.mapIndexed { i, d -> "$i:${d.size}B" }.joinToString()})"
|
"(${format.initializationData.mapIndexed { i, d -> "$i:${d.size}B" }.joinToString()})"
|
||||||
)
|
)
|
||||||
|
val strippedInitializationData = stripInitializationData(format.initializationData)
|
||||||
|
|
||||||
val newFormat = when (dvMode) {
|
val newFormat = when (dvMode) {
|
||||||
DvConversionMode.DV81 -> {
|
DvConversionMode.DV81 -> {
|
||||||
@@ -89,39 +92,26 @@ class DoviConvertingTrackOutput(
|
|||||||
val level = codecs.split('.').getOrNull(2)?.toIntOrNull() ?: 6
|
val level = codecs.split('.').getOrNull(2)?.toIntOrNull() ?: 6
|
||||||
val newCodecs = "dvhe.08.%02d".format(level)
|
val newCodecs = "dvhe.08.%02d".format(level)
|
||||||
val dvConfigRecord = buildDv81ConfigRecord(level)
|
val dvConfigRecord = buildDv81ConfigRecord(level)
|
||||||
Log.i(TAG, "DV81: rewriting to $newCodecs, config=${dvConfigRecord.size}B")
|
logInfo("DV81: rewriting to $newCodecs, config=${dvConfigRecord.size}B")
|
||||||
|
|
||||||
format.buildUpon()
|
format.buildUpon()
|
||||||
.setSampleMimeType(MimeTypes.VIDEO_DOLBY_VISION)
|
.setSampleMimeType(MimeTypes.VIDEO_DOLBY_VISION)
|
||||||
.setCodecs(newCodecs)
|
.setCodecs(newCodecs)
|
||||||
.setInitializationData(
|
.setInitializationData(strippedInitializationData + dvConfigRecord)
|
||||||
if (format.initializationData.isNotEmpty()) {
|
|
||||||
listOf(format.initializationData[0], dvConfigRecord)
|
|
||||||
} else {
|
|
||||||
listOf(ByteArray(0), dvConfigRecord)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// HEVC_STRIP: present as plain HEVC
|
// HEVC_STRIP: present as plain HEVC
|
||||||
Log.i(TAG, "HEVC_STRIP: rewriting to video/hevc")
|
logInfo("HEVC_STRIP: rewriting to video/hevc")
|
||||||
format.buildUpon()
|
format.buildUpon()
|
||||||
.setSampleMimeType(MimeTypes.VIDEO_H265)
|
.setSampleMimeType(MimeTypes.VIDEO_H265)
|
||||||
.setCodecs(null)
|
.setCodecs(null)
|
||||||
.setInitializationData(
|
.setInitializationData(strippedInitializationData)
|
||||||
if (format.initializationData.isNotEmpty()) {
|
|
||||||
listOf(format.initializationData[0])
|
|
||||||
} else {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i(
|
logInfo(
|
||||||
TAG,
|
|
||||||
"Rewritten format: mime=${newFormat.sampleMimeType}, " +
|
"Rewritten format: mime=${newFormat.sampleMimeType}, " +
|
||||||
"codecs=${newFormat.codecs}, initData=${newFormat.initializationData.size} entries"
|
"codecs=${newFormat.codecs}, initData=${newFormat.initializationData.size} entries"
|
||||||
)
|
)
|
||||||
@@ -186,7 +176,7 @@ class DoviConvertingTrackOutput(
|
|||||||
processNalUnits(srcLen)
|
processNalUnits(srcLen)
|
||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "NAL processing failed, passing raw sample", e)
|
logError("NAL processing failed, passing raw sample", e)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
if (success) {
|
if (success) {
|
||||||
@@ -240,8 +230,7 @@ class DoviConvertingTrackOutput(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (sampleCount == 0L) {
|
if (sampleCount == 0L) {
|
||||||
Log.d(
|
logDebug(
|
||||||
TAG,
|
|
||||||
"NAL format detected: ${if (isAnnexB) "Annex B" else "length-prefixed"}, " +
|
"NAL format detected: ${if (isAnnexB) "Annex B" else "length-prefixed"}, " +
|
||||||
"first bytes: ${sampleBuf.take(8).joinToString(" ") { "%02X".format(it) }}"
|
"first bytes: ${sampleBuf.take(8).joinToString(" ") { "%02X".format(it) }}"
|
||||||
)
|
)
|
||||||
@@ -340,8 +329,7 @@ class DoviConvertingTrackOutput(
|
|||||||
|
|
||||||
sampleCount++
|
sampleCount++
|
||||||
if (sampleCount <= 3 || (sampleCount % 500 == 0L)) {
|
if (sampleCount <= 3 || (sampleCount % 500 == 0L)) {
|
||||||
Log.d(
|
logDebug(
|
||||||
TAG,
|
|
||||||
"Sample #$sampleCount (AnnexB): ${dataLen}B -> ${outputLen}B, " +
|
"Sample #$sampleCount (AnnexB): ${dataLen}B -> ${outputLen}B, " +
|
||||||
"kept=$kept stripped=$stripped NALs"
|
"kept=$kept stripped=$stripped NALs"
|
||||||
)
|
)
|
||||||
@@ -363,7 +351,7 @@ class DoviConvertingTrackOutput(
|
|||||||
|
|
||||||
if (nalLen <= 0 || pos + 4 + nalLen > dataLen) {
|
if (nalLen <= 0 || pos + 4 + nalLen > dataLen) {
|
||||||
if (sampleCount < 5) {
|
if (sampleCount < 5) {
|
||||||
Log.w(TAG, "Bad NAL length $nalLen at pos $pos (data.size=$dataLen)")
|
logWarn("Bad NAL length $nalLen at pos $pos (data.size=$dataLen)")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -401,8 +389,7 @@ class DoviConvertingTrackOutput(
|
|||||||
|
|
||||||
sampleCount++
|
sampleCount++
|
||||||
if (sampleCount <= 3 || (sampleCount % 500 == 0L)) {
|
if (sampleCount <= 3 || (sampleCount % 500 == 0L)) {
|
||||||
Log.d(
|
logDebug(
|
||||||
TAG,
|
|
||||||
"Sample #$sampleCount (LenPrefix): ${dataLen}B -> ${outputLen}B, " +
|
"Sample #$sampleCount (LenPrefix): ${dataLen}B -> ${outputLen}B, " +
|
||||||
"kept=$kept stripped=$stripped NALs"
|
"kept=$kept stripped=$stripped NALs"
|
||||||
)
|
)
|
||||||
@@ -449,8 +436,7 @@ class DoviConvertingTrackOutput(
|
|||||||
private fun recordSampleProcessing(elapsedUs: Long) {
|
private fun recordSampleProcessing(elapsedUs: Long) {
|
||||||
totalSampleProcessingTimeUs += elapsedUs
|
totalSampleProcessingTimeUs += elapsedUs
|
||||||
if (sampleCount <= 3 || (sampleCount > 0 && sampleCount % 500 == 0L)) {
|
if (sampleCount <= 3 || (sampleCount > 0 && sampleCount % 500 == 0L)) {
|
||||||
Log.d(
|
logDebug(
|
||||||
TAG,
|
|
||||||
"Perf: avgSample=${averageSampleProcessingTimeUs}us, " +
|
"Perf: avgSample=${averageSampleProcessingTimeUs}us, " +
|
||||||
"avgRpu=${averageRpuConversionTimeUs}us, converted=$convertedRpuCount, " +
|
"avgRpu=${averageRpuConversionTimeUs}us, converted=$convertedRpuCount, " +
|
||||||
"rpuFailures=$rpuConversionFailureCount, rpuTooSmall=$rpuOutputTooSmallCount"
|
"rpuFailures=$rpuConversionFailureCount, rpuTooSmall=$rpuOutputTooSmallCount"
|
||||||
@@ -458,14 +444,121 @@ class DoviConvertingTrackOutput(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun stripInitializationData(initializationData: List<ByteArray>): List<ByteArray> {
|
||||||
|
if (initializationData.isEmpty()) return emptyList()
|
||||||
|
|
||||||
|
val stripped = ArrayList<ByteArray>(initializationData.size)
|
||||||
|
var beforeBytes = 0
|
||||||
|
var afterBytes = 0
|
||||||
|
var droppedBuffers = 0
|
||||||
|
|
||||||
|
for (data in initializationData) {
|
||||||
|
beforeBytes += data.size
|
||||||
|
val processed = stripInitializationAnnexB(data)
|
||||||
|
if (processed == null) {
|
||||||
|
// Media3's HEVC init data is normally Annex B. Non-AnnexB entries in
|
||||||
|
// Dolby Vision formats are typically dvcC/dvvC records, which must not
|
||||||
|
// be forwarded when advertising plain HEVC.
|
||||||
|
droppedBuffers++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (processed.isEmpty()) {
|
||||||
|
droppedBuffers++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
afterBytes += processed.size
|
||||||
|
stripped.add(processed)
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo(
|
||||||
|
"Init data stripped: ${initializationData.size} entries/${beforeBytes}B -> " +
|
||||||
|
"${stripped.size} entries/${afterBytes}B, strippedNals=$strippedInitNalCount, " +
|
||||||
|
"droppedBuffers=$droppedBuffers"
|
||||||
|
)
|
||||||
|
|
||||||
|
return stripped
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stripInitializationAnnexB(data: ByteArray): ByteArray? {
|
||||||
|
if (data.isEmpty()) return data
|
||||||
|
|
||||||
|
val firstStartCodeEnd = findStartCodeEnd(data, 0) ?: return null
|
||||||
|
ensureOutputCapacity(data.size)
|
||||||
|
outputLen = 0
|
||||||
|
|
||||||
|
var kept = 0
|
||||||
|
var stripped = 0
|
||||||
|
var nalStart = firstStartCodeEnd
|
||||||
|
|
||||||
|
while (nalStart < data.size) {
|
||||||
|
val nalEnd = findNextStartCodeOffset(data, nalStart) ?: data.size
|
||||||
|
val nalLen = nalEnd - nalStart
|
||||||
|
if (nalLen > 0) {
|
||||||
|
when (classifyNal(data, nalStart, nalLen, convertRpu = false)) {
|
||||||
|
NalAction.KEEP -> {
|
||||||
|
ensureOutputCapacity(outputLen + 4 + nalLen)
|
||||||
|
System.arraycopy(ANNEX_B_START_CODE, 0, outputBuf, outputLen, 4)
|
||||||
|
outputLen += 4
|
||||||
|
System.arraycopy(data, nalStart, outputBuf, outputLen, nalLen)
|
||||||
|
normalizeLayerId(outputBuf, outputLen)
|
||||||
|
outputLen += nalLen
|
||||||
|
kept++
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
strippedInitNalCount++
|
||||||
|
stripped++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nalEnd >= data.size) break
|
||||||
|
nalStart = findStartCodeEnd(data, nalEnd) ?: break
|
||||||
|
}
|
||||||
|
|
||||||
|
logDebug("Init data sample: ${data.size}B -> ${outputLen}B, kept=$kept stripped=$stripped NALs")
|
||||||
|
return outputBuf.copyOf(outputLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findStartCodeEnd(data: ByteArray, from: Int): Int? {
|
||||||
|
var i = from
|
||||||
|
while (i < data.size - 2) {
|
||||||
|
if (data[i] == 0.toByte() && data[i + 1] == 0.toByte()) {
|
||||||
|
if (i + 3 < data.size && data[i + 2] == 0.toByte() && data[i + 3] == 1.toByte()) {
|
||||||
|
return i + 4
|
||||||
|
} else if (data[i + 2] == 1.toByte()) {
|
||||||
|
return i + 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findNextStartCodeOffset(data: ByteArray, from: Int): Int? {
|
||||||
|
var i = from
|
||||||
|
while (i < data.size - 2) {
|
||||||
|
if (data[i] == 0.toByte() && data[i + 1] == 0.toByte()) {
|
||||||
|
if (i + 3 < data.size && data[i + 2] == 0.toByte() && data[i + 3] == 1.toByte()) {
|
||||||
|
return i
|
||||||
|
} else if (data[i + 2] == 1.toByte()) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
/** Classify a NAL at sampleBuf[offset..offset+len) without copying. */
|
/** Classify a NAL at sampleBuf[offset..offset+len) without copying. */
|
||||||
private fun processNalInline(offset: Int, len: Int): NalAction {
|
private fun processNalInline(offset: Int, len: Int): NalAction = classifyNal(sampleBuf, offset, len, convertRpu = dvMode == DvConversionMode.DV81)
|
||||||
|
|
||||||
|
private fun classifyNal(data: ByteArray, offset: Int, len: Int, convertRpu: Boolean): NalAction {
|
||||||
if (len < 2) return NalAction.KEEP
|
if (len < 2) return NalAction.KEEP
|
||||||
val nalType = (sampleBuf[offset].toInt() ushr 1) and 0x3F
|
val nalType = (data[offset].toInt() ushr 1) and 0x3F
|
||||||
val nuhLayerId = ((sampleBuf[offset].toInt() and 1) shl 5) or
|
val nuhLayerId = ((data[offset].toInt() and 1) shl 5) or
|
||||||
((sampleBuf[offset + 1].toInt() ushr 3) and 0x1F)
|
((data[offset + 1].toInt() ushr 3) and 0x1F)
|
||||||
return when {
|
return when {
|
||||||
nalType == NAL_TYPE_UNSPEC62 && dvMode == DvConversionMode.DV81 -> NalAction.CONVERT
|
nalType == NAL_TYPE_UNSPEC62 && convertRpu -> NalAction.CONVERT
|
||||||
nalType == NAL_TYPE_UNSPEC62 || nalType == NAL_TYPE_UNSPEC63 || nuhLayerId > 0 -> NalAction.STRIP
|
nalType == NAL_TYPE_UNSPEC62 || nalType == NAL_TYPE_UNSPEC63 || nuhLayerId > 0 -> NalAction.STRIP
|
||||||
else -> NalAction.KEEP
|
else -> NalAction.KEEP
|
||||||
}
|
}
|
||||||
@@ -497,6 +590,26 @@ class DoviConvertingTrackOutput(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun logDebug(message: String) {
|
||||||
|
Log.d(TAG, message)
|
||||||
|
emitLog?.invoke("debug", "dv-convert", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun logInfo(message: String) {
|
||||||
|
Log.i(TAG, message)
|
||||||
|
emitLog?.invoke("info", "dv-convert", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun logWarn(message: String) {
|
||||||
|
Log.w(TAG, message)
|
||||||
|
emitLog?.invoke("warn", "dv-convert", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun logError(message: String, throwable: Throwable) {
|
||||||
|
Log.e(TAG, message, throwable)
|
||||||
|
emitLog?.invoke("error", "dv-convert", "$message: ${throwable.message}")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a 24-byte DOVIDecoderConfigurationRecord for DV Profile 8.1.
|
* Build a 24-byte DOVIDecoderConfigurationRecord for DV Profile 8.1.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -16,12 +16,13 @@ import androidx.media3.extractor.TrackOutput
|
|||||||
class DoviExtractorOutputWrapper(
|
class DoviExtractorOutputWrapper(
|
||||||
private val delegate: ExtractorOutput,
|
private val delegate: ExtractorOutput,
|
||||||
private val dvMode: DvConversionMode,
|
private val dvMode: DvConversionMode,
|
||||||
|
private val emitLog: ((String, String, String) -> Unit)?,
|
||||||
private val onVideoTrackWrapped: (DoviConvertingTrackOutput) -> Unit
|
private val onVideoTrackWrapped: (DoviConvertingTrackOutput) -> Unit
|
||||||
) : ExtractorOutput {
|
) : ExtractorOutput {
|
||||||
override fun track(id: Int, type: Int): TrackOutput {
|
override fun track(id: Int, type: Int): TrackOutput {
|
||||||
val original = delegate.track(id, type)
|
val original = delegate.track(id, type)
|
||||||
if (type == C.TRACK_TYPE_VIDEO) {
|
if (type == C.TRACK_TYPE_VIDEO) {
|
||||||
val wrapper = DoviConvertingTrackOutput(original, dvMode)
|
val wrapper = DoviConvertingTrackOutput(original, dvMode, emitLog)
|
||||||
onVideoTrackWrapped(wrapper)
|
onVideoTrackWrapped(wrapper)
|
||||||
return wrapper
|
return wrapper
|
||||||
}
|
}
|
||||||
@@ -39,7 +40,8 @@ class DoviExtractorOutputWrapper(
|
|||||||
*/
|
*/
|
||||||
class DoviExtractorWrapper(
|
class DoviExtractorWrapper(
|
||||||
private val delegate: Extractor,
|
private val delegate: Extractor,
|
||||||
private val dvMode: DvConversionMode = DvConversionMode.HEVC_STRIP
|
private val dvMode: DvConversionMode = DvConversionMode.HEVC_STRIP,
|
||||||
|
private val emitLog: ((String, String, String) -> Unit)? = null
|
||||||
) : Extractor {
|
) : Extractor {
|
||||||
|
|
||||||
@Volatile var doviTrackOutput: DoviConvertingTrackOutput? = null
|
@Volatile var doviTrackOutput: DoviConvertingTrackOutput? = null
|
||||||
@@ -48,7 +50,7 @@ class DoviExtractorWrapper(
|
|||||||
override fun sniff(input: ExtractorInput): Boolean = delegate.sniff(input)
|
override fun sniff(input: ExtractorInput): Boolean = delegate.sniff(input)
|
||||||
|
|
||||||
override fun init(output: ExtractorOutput) {
|
override fun init(output: ExtractorOutput) {
|
||||||
delegate.init(DoviExtractorOutputWrapper(output, dvMode) { doviTrackOutput = it })
|
delegate.init(DoviExtractorOutputWrapper(output, dvMode, emitLog) { doviTrackOutput = it })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(input: ExtractorInput, seekPosition: PositionHolder): Int = delegate.read(input, seekPosition)
|
override fun read(input: ExtractorInput, seekPosition: PositionHolder): Int = delegate.read(input, seekPosition)
|
||||||
|
|||||||
@@ -438,7 +438,9 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
|
|||||||
extractor is MatroskaExtractor -> {
|
extractor is MatroskaExtractor -> {
|
||||||
val assExtractor = ZlibMatroskaExtractor(assParserFactory, handler)
|
val assExtractor = ZlibMatroskaExtractor(assParserFactory, handler)
|
||||||
val inner = if (doviEnabled) {
|
val inner = if (doviEnabled) {
|
||||||
DoviExtractorWrapper(assExtractor, currentDvMode).also {
|
DoviExtractorWrapper(assExtractor, currentDvMode) { level, prefix, message ->
|
||||||
|
emitLog(level, prefix, message)
|
||||||
|
}.also {
|
||||||
activeDoviMkvWrapper = it
|
activeDoviMkvWrapper = it
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -448,7 +450,9 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
|
|||||||
CuelessSeekExtractorWrapper(inner)
|
CuelessSeekExtractorWrapper(inner)
|
||||||
}
|
}
|
||||||
doviEnabled && (extractor is Mp4Extractor || extractor is FragmentedMp4Extractor) -> {
|
doviEnabled && (extractor is Mp4Extractor || extractor is FragmentedMp4Extractor) -> {
|
||||||
DoviExtractorWrapper(extractor, currentDvMode).also {
|
DoviExtractorWrapper(extractor, currentDvMode) { level, prefix, message ->
|
||||||
|
emitLog(level, prefix, message)
|
||||||
|
}.also {
|
||||||
activeDoviMp4Wrapper = it
|
activeDoviMp4Wrapper = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1909,6 +1913,7 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
|
|||||||
"dvConversionActive" to (dovi?.conversionActive == true),
|
"dvConversionActive" to (dovi?.conversionActive == true),
|
||||||
"dvConversionMode" to dvMode.name,
|
"dvConversionMode" to dvMode.name,
|
||||||
"dvConversionDebugMode" to (debugDvModeOverride?.name ?: "AUTO"),
|
"dvConversionDebugMode" to (debugDvModeOverride?.name ?: "AUTO"),
|
||||||
|
"dvStrippedInitNals" to (dovi?.strippedInitNalCount ?: 0L),
|
||||||
"dvStrippedNals" to (dovi?.strippedNalCount ?: 0L),
|
"dvStrippedNals" to (dovi?.strippedNalCount ?: 0L),
|
||||||
"dvConvertedRpus" to (dovi?.convertedRpuCount ?: 0L),
|
"dvConvertedRpus" to (dovi?.convertedRpuCount ?: 0L),
|
||||||
"dvRpuConversionFailures" to (dovi?.rpuConversionFailureCount ?: 0L),
|
"dvRpuConversionFailures" to (dovi?.rpuConversionFailureCount ?: 0L),
|
||||||
|
|||||||
Reference in New Issue
Block a user