Skip to content

Commit 28937b2

Browse files
committed
Fix TranscoderOption accessing data source without initialization
1 parent 5897074 commit 28937b2

File tree

4 files changed

+74
-76
lines changed

4 files changed

+74
-76
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.otaliastudios.transcoder
2+
3+
import com.otaliastudios.transcoder.source.DataSource
4+
5+
class ThumbnailerOptions {
6+
7+
class Builder {
8+
9+
private val dataSources = mutableListOf<DataSource>()
10+
11+
fun addDataSource()
12+
13+
fun build() = ThumbnailerOptions()
14+
}
15+
}

lib/src/main/java/com/otaliastudios/transcoder/TranscoderOptions.java

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -334,42 +334,6 @@ public Builder setAudioResampler(@NonNull AudioResampler audioResampler) {
334334
return this;
335335
}
336336

337-
/**
338-
* Generates muted audio data sources if needed
339-
* @return The list of audio data sources including the muted sources
340-
*/
341-
private List<DataSource> buildAudioDataSources()
342-
{
343-
// Check if we have a mix of empty and non-empty data sources
344-
// This would cause an error in Engine::computeTrackStatus
345-
boolean hasMissingAudioDataSources = false;
346-
boolean hasAudioDataSources = false;
347-
boolean hasValidAudioDataSources = true;
348-
for (DataSource dataSource : audioDataSources) {
349-
if (dataSource.getTrackFormat(TrackType.AUDIO) == null) {
350-
hasMissingAudioDataSources = true;
351-
} else {
352-
hasAudioDataSources = true;
353-
}
354-
if (hasAudioDataSources && hasMissingAudioDataSources) {
355-
hasValidAudioDataSources = false;
356-
break;
357-
}
358-
}
359-
if (hasValidAudioDataSources) {
360-
return audioDataSources;
361-
}
362-
// Fix the audioDataSources by replacing the empty data source by muted data source
363-
List<DataSource> result = new ArrayList<>();
364-
for (DataSource dataSource : audioDataSources) {
365-
if (dataSource.getTrackFormat(TrackType.AUDIO) != null) {
366-
result.add(dataSource);
367-
} else {
368-
result.add(new BlankAudioDataSource(dataSource.getDurationUs()));
369-
}
370-
}
371-
return result;
372-
}
373337

374338
@NonNull
375339
public TranscoderOptions build() {
@@ -407,7 +371,7 @@ public TranscoderOptions build() {
407371
}
408372
TranscoderOptions options = new TranscoderOptions();
409373
options.listener = listener;
410-
options.audioDataSources = buildAudioDataSources();
374+
options.audioDataSources = audioDataSources;
411375
options.videoDataSources = videoDataSources;
412376
options.dataSink = dataSink;
413377
options.listenerHandler = listenerHandler;

lib/src/main/java/com/otaliastudios/transcoder/internal/DataSources.kt

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,51 @@ import com.otaliastudios.transcoder.common.TrackType
55
import com.otaliastudios.transcoder.internal.utils.Logger
66
import com.otaliastudios.transcoder.internal.utils.TrackMap
77
import com.otaliastudios.transcoder.internal.utils.trackMapOf
8+
import com.otaliastudios.transcoder.source.BlankAudioDataSource
89
import com.otaliastudios.transcoder.source.DataSource
910

1011
internal class DataSources private constructor(
11-
private val videoSources: List<DataSource>,
12-
private val audioSources: List<DataSource>,
12+
videoSources: List<DataSource>,
13+
audioSources: List<DataSource>,
1314
) : TrackMap<List<DataSource>> {
1415

1516
constructor(options: TranscoderOptions) : this(options.videoDataSources, options.audioDataSources)
1617

1718
private val log = Logger("DataSources")
1819

19-
fun all() = (audio + video).distinct()
20+
private fun DataSource.init() = if (!isInitialized) initialize() else Unit
21+
private fun DataSource.deinit() = if (isInitialized) deinitialize() else Unit
22+
private fun List<DataSource>.init() = forEach { it.init() }
23+
private fun List<DataSource>.deinit() = forEach { it.deinit() }
24+
25+
init {
26+
videoSources.init()
27+
audioSources.init()
28+
}
29+
30+
private val videoSources: List<DataSource> = run {
31+
val valid = videoSources.count { it.getTrackFormat(TrackType.VIDEO) != null }
32+
when (valid) {
33+
0 -> listOf<DataSource>().also { videoSources.deinit() }
34+
videoSources.size -> videoSources
35+
else -> videoSources // Tracks will crash
36+
}
37+
}
38+
39+
private val audioSources: List<DataSource> = run {
40+
val valid = audioSources.count { it.getTrackFormat(TrackType.AUDIO) != null }
41+
when (valid) {
42+
0 -> listOf<DataSource>().also { audioSources.deinit() }
43+
audioSources.size -> audioSources
44+
else -> {
45+
// Some tracks do not have audio, while some do. Replace with BlankAudio.
46+
audioSources.map { source ->
47+
if (source.getTrackFormat(TrackType.AUDIO) != null) source
48+
else BlankAudioDataSource(source.durationUs).also { source.deinit() }
49+
}
50+
}
51+
}
52+
}
2053

2154
override fun get(type: TrackType) = when (type) {
2255
TrackType.AUDIO -> audioSources
@@ -25,25 +58,12 @@ internal class DataSources private constructor(
2558

2659
override fun has(type: TrackType) = this[type].isNotEmpty()
2760

28-
init {
29-
log.i("Initializing...")
30-
all().forEach {
31-
log.v("Initializing source $it...")
32-
if (!it.isInitialized) {
33-
it.initialize()
34-
}
35-
}
36-
log.i("Initialized.")
37-
}
61+
fun all() = (audio + video).distinct()
3862

3963
fun release() {
4064
log.i("release(): releasing...")
41-
all().forEach {
42-
log.i("release(): releasing source $it...")
43-
if (it.isInitialized) {
44-
it.deinitialize()
45-
}
46-
}
65+
video.forEach { it.deinit() }
66+
audio.forEach { it.deinit() }
4767
log.i("release(): released.")
4868
}
4969
}

lib/src/main/java/com/otaliastudios/transcoder/internal/Tracks.kt

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,26 @@ internal class Tracks(
5050
strategy: TrackStrategy,
5151
sources: List<DataSource>? // null or not-empty
5252
): Pair<MediaFormat, TrackStatus> {
53-
return if (sources == null) {
54-
MediaFormat() to TrackStatus.ABSENT
55-
} else {
56-
var status = TrackStatus.ABSENT
57-
val outputFormat = MediaFormat()
58-
val provider = MediaFormatProvider()
59-
val inputFormats = sources.mapNotNull {
60-
val format = it.getTrackFormat(type)
61-
format?.run { it to this }
62-
}.map { (source, format) ->
63-
provider.provideMediaFormat(source, type, format)
64-
}
65-
if (inputFormats.size == sources.size) {
66-
status = strategy.createOutputFormat(inputFormats, outputFormat)
67-
} else {
68-
require(inputFormats.isEmpty()) {
69-
"getTrackFormat returned null for ${sources.size - inputFormats.size}" +
70-
"/${sources.size} sources of type $type"
71-
}
53+
if (sources == null) {
54+
return MediaFormat() to TrackStatus.ABSENT
55+
}
56+
57+
val provider = MediaFormatProvider()
58+
val inputs = sources.mapNotNull {
59+
val format = it.getTrackFormat(type) ?: return@mapNotNull null
60+
provider.provideMediaFormat(it, type, format)
61+
}
62+
63+
// The DataSources class already tries to address this for audio, by inserting
64+
// a BlankAudioDataSource. However we still don't have a solution for video.
65+
return when (inputs.size) {
66+
0 -> MediaFormat() to TrackStatus.ABSENT
67+
sources.size -> {
68+
val output = MediaFormat()
69+
val status = strategy.createOutputFormat(inputs, output)
70+
output to status
7271
}
73-
outputFormat to status
72+
else -> error("Of all $type sources, some have a $type track, some don't.")
7473
}
7574
}
7675
}

0 commit comments

Comments
 (0)