Skip to content

Commit

Permalink
Add Issue 137 tests (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
natario1 authored Aug 16, 2021
1 parent bf4ce2c commit ba8f098
Show file tree
Hide file tree
Showing 19 changed files with 178 additions and 47 deletions.
Binary file added lib/src/androidTest/assets/issue_137/0.amr
Binary file not shown.
Binary file added lib/src/androidTest/assets/issue_137/1.amr
Binary file not shown.
Binary file added lib/src/androidTest/assets/issue_137/2.amr
Binary file not shown.
Binary file added lib/src/androidTest/assets/issue_137/3.amr
Binary file not shown.
Binary file added lib/src/androidTest/assets/issue_137/4.amr
Binary file not shown.
Binary file added lib/src/androidTest/assets/issue_137/5.amr
Binary file not shown.
Binary file added lib/src/androidTest/assets/issue_137/6.amr
Binary file not shown.
Binary file added lib/src/androidTest/assets/issue_137/7.amr
Binary file not shown.
Binary file added lib/src/androidTest/assets/issue_137/8.amr
Binary file not shown.
Binary file added lib/src/androidTest/assets/issue_137/main.mp3
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.otaliastudios.transcoder.integration

import android.media.MediaMetadataRetriever.METADATA_KEY_DURATION
import android.media.MediaMetadataRetriever
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.otaliastudios.transcoder.Transcoder
import com.otaliastudios.transcoder.TranscoderListener
import com.otaliastudios.transcoder.TranscoderOptions
import com.otaliastudios.transcoder.internal.utils.Logger
import com.otaliastudios.transcoder.source.AssetFileDescriptorDataSource
import com.otaliastudios.transcoder.source.ClipDataSource
import com.otaliastudios.transcoder.source.FileDescriptorDataSource
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File

@RunWith(AndroidJUnit4::class)
class IssuesTests {

class Helper(val issue: Int) {

val log = Logger("Issue$issue")
val context = InstrumentationRegistry.getInstrumentation().context

fun output(
name: String = System.currentTimeMillis().toString(),
extension: String = "mp4"
) = File(context.cacheDir, "$name.$extension").also { it.parentFile!!.mkdirs() }

fun input(filename: String) = AssetFileDescriptorDataSource(
context.assets.openFd("issue_$issue/$filename")
)

fun transcode(
output: File = output(),
assertTranscoded: Boolean = true,
assertDuration: Boolean = true,
builder: TranscoderOptions.Builder.() -> Unit,
): File {
val transcoder = Transcoder.into(output.absolutePath)
transcoder.apply(builder)
transcoder.setListener(object : TranscoderListener {
override fun onTranscodeCanceled() = Unit
override fun onTranscodeCompleted(successCode: Int) {
if (assertTranscoded) {
require(successCode == Transcoder.SUCCESS_TRANSCODED)
}
}
override fun onTranscodeFailed(exception: Throwable) = Unit
override fun onTranscodeProgress(progress: Double) = Unit
})
transcoder.transcode().get()
if (assertDuration) {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(output.absolutePath)
val duration = retriever.extractMetadata(METADATA_KEY_DURATION)!!.toLong()
log.e("Video duration is $duration")
assert(duration > 0L)
retriever.release()
}
return output
}
}


@Test
fun issue137() = with(Helper(137)) {
transcode {
addDataSource(ClipDataSource(input("main.mp3"), 0L, 1000_000L))
addDataSource(input("0.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 0L, 1000_000L))
addDataSource(input("1.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 0L, 1000_000L))
addDataSource(input("2.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 0L, 1000_000L))
addDataSource(input("3.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 0L, 1000_000L))
addDataSource(input("4.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 0L, 1000_000L))
addDataSource(input("5.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 0L, 1000_000L))
addDataSource(input("6.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 0L, 1000_000L))
addDataSource(input("7.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 0L, 1000_000L))
addDataSource(input("8.amr"))
}
Unit
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.otaliastudios.transcoder.engine.internal;
package com.otaliastudios.transcoder.internal.utils;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.otaliastudios.transcoder.internal.utils.ISO6709LocationParser;

import org.junit.Test;
import org.junit.runner.RunWith;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.otaliastudios.transcoder;

import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
Expand All @@ -11,6 +12,7 @@
import com.otaliastudios.transcoder.resample.DefaultAudioResampler;
import com.otaliastudios.transcoder.sink.DataSink;
import com.otaliastudios.transcoder.sink.DefaultDataSink;
import com.otaliastudios.transcoder.source.AssetFileDescriptorDataSource;
import com.otaliastudios.transcoder.source.DataSource;
import com.otaliastudios.transcoder.source.FileDescriptorDataSource;
import com.otaliastudios.transcoder.source.FilePathDataSource;
Expand Down Expand Up @@ -162,37 +164,43 @@ public Builder addDataSource(@NonNull TrackType type, @NonNull DataSource dataSo
}

@NonNull
@SuppressWarnings("unused")
public Builder addDataSource(@NonNull FileDescriptor fileDescriptor) {
return addDataSource(new FileDescriptorDataSource(fileDescriptor));
}

@NonNull
@SuppressWarnings("unused")
public Builder addDataSource(@NonNull TrackType type, @NonNull FileDescriptor fileDescriptor) {
return addDataSource(type, new FileDescriptorDataSource(fileDescriptor));
}

@NonNull
@SuppressWarnings("unused")
public Builder addDataSource(@NonNull AssetFileDescriptor assetFileDescriptor) {
return addDataSource(new AssetFileDescriptorDataSource(assetFileDescriptor));
}

@NonNull
public Builder addDataSource(@NonNull TrackType type, @NonNull AssetFileDescriptor assetFileDescriptor) {
return addDataSource(type, new AssetFileDescriptorDataSource(assetFileDescriptor));
}

@NonNull
public Builder addDataSource(@NonNull String inPath) {
return addDataSource(new FilePathDataSource(inPath));
}

@NonNull
@SuppressWarnings("unused")
public Builder addDataSource(@NonNull TrackType type, @NonNull String inPath) {
return addDataSource(type, new FilePathDataSource(inPath));
}

@NonNull
@SuppressWarnings({"unused", "UnusedReturnValue"})
@SuppressWarnings({"UnusedReturnValue"})
public Builder addDataSource(@NonNull Context context, @NonNull Uri uri) {
return addDataSource(new UriDataSource(context, uri));
}

@NonNull
@SuppressWarnings({"unused", "UnusedReturnValue"})
@SuppressWarnings({"UnusedReturnValue"})
public Builder addDataSource(@NonNull TrackType type, @NonNull Context context, @NonNull Uri uri) {
return addDataSource(type, new UriDataSource(context, uri));
}
Expand All @@ -205,7 +213,6 @@ public Builder addDataSource(@NonNull TrackType type, @NonNull Context context,
* @return this for chaining
*/
@NonNull
@SuppressWarnings("unused")
public Builder setAudioTrackStrategy(@Nullable TrackStrategy trackStrategy) {
this.audioTrackStrategy = trackStrategy;
return this;
Expand All @@ -219,7 +226,6 @@ public Builder setAudioTrackStrategy(@Nullable TrackStrategy trackStrategy) {
* @return this for chaining
*/
@NonNull
@SuppressWarnings("unused")
public Builder setVideoTrackStrategy(@Nullable TrackStrategy trackStrategy) {
this.videoTrackStrategy = trackStrategy;
return this;
Expand Down Expand Up @@ -255,7 +261,6 @@ public Builder setListenerHandler(@Nullable Handler listenerHandler) {
* @return this for chaining
*/
@NonNull
@SuppressWarnings("unused")
public Builder setValidator(@Nullable Validator validator) {
this.validator = validator;
return this;
Expand All @@ -269,7 +274,6 @@ public Builder setValidator(@Nullable Validator validator) {
* @return this for chaining
*/
@NonNull
@SuppressWarnings("unused")
public Builder setVideoRotation(int rotation) {
this.videoRotation = rotation;
return this;
Expand Down Expand Up @@ -299,7 +303,6 @@ public Builder setTimeInterpolator(@NonNull TimeInterpolator timeInterpolator) {
* @return this for chaining
*/
@NonNull
@SuppressWarnings("unused")
public Builder setSpeed(float speedFactor) {
return setTimeInterpolator(new SpeedTimeInterpolator(speedFactor));
}
Expand All @@ -313,7 +316,6 @@ public Builder setSpeed(float speedFactor) {
* @return this for chaining
*/
@NonNull
@SuppressWarnings("unused")
public Builder setAudioStretcher(@NonNull AudioStretcher audioStretcher) {
this.audioStretcher = audioStretcher;
return this;
Expand All @@ -328,7 +330,6 @@ public Builder setAudioStretcher(@NonNull AudioStretcher audioStretcher) {
* @return this for chaining
*/
@NonNull
@SuppressWarnings("unused")
public Builder setAudioResampler(@NonNull AudioResampler audioResampler) {
this.audioResampler = audioResampler;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,48 @@ internal class DataSources private constructor(

private fun DataSource.init() = if (!isInitialized) initialize() else Unit
private fun DataSource.deinit() = if (isInitialized) deinitialize() else Unit
private fun List<DataSource>.init() = forEach { it.init() }
private fun List<DataSource>.deinit() = forEach { it.deinit() }
private fun List<DataSource>.init() = forEach {
log.i("initializing $it... (isInit=${it.isInitialized})")
it.init()
}
private fun List<DataSource>.deinit() = forEach {
log.i("deinitializing $it... (isInit=${it.isInitialized})")
it.deinit()
}

init {
log.i("initializing videoSources...")
videoSources.init()
log.i("initializing audioSources...")
audioSources.init()
}

// Save and deinit on release, because a source that is discarded for video
// might be active for audio. We don't want to deinit right away.
private val discarded = mutableListOf<DataSource>()

private val videoSources: List<DataSource> = run {
val valid = videoSources.count { it.getTrackFormat(TrackType.VIDEO) != null }
when (valid) {
0 -> listOf<DataSource>().also { videoSources.deinit() }
0 -> listOf<DataSource>().also { discarded += videoSources }
videoSources.size -> videoSources
else -> videoSources // Tracks will crash
}
}

private val audioSources: List<DataSource> = run {
val valid = audioSources.count { it.getTrackFormat(TrackType.AUDIO) != null }
log.i("computing audioSources, valid=$valid")
when (valid) {
0 -> listOf<DataSource>().also { audioSources.deinit() }
0 -> listOf<DataSource>().also { discarded += audioSources }
audioSources.size -> audioSources
else -> {
// Some tracks do not have audio, while some do. Replace with BlankAudio.
audioSources.map { source ->
if (source.getTrackFormat(TrackType.AUDIO) != null) source
else BlankAudioDataSource(source.durationUs).also { source.deinit() }
else BlankAudioDataSource(source.durationUs).also {
discarded += source
}
}
}
}
Expand All @@ -64,8 +79,9 @@ internal class DataSources private constructor(

fun release() {
log.i("release(): releasing...")
video.forEach { it.deinit() }
audio.forEach { it.deinit() }
video.deinit()
audio.deinit()
discarded.deinit()
log.i("release(): released.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ internal class Tracks(
strategy: TrackStrategy,
sources: List<DataSource>? // null or not-empty
): Pair<MediaFormat, TrackStatus> {
log.i("resolveTrack($type), sources=${sources?.size}, strategy=${strategy::class.simpleName}")
if (sources == null) {
return MediaFormat() to TrackStatus.ABSENT
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.otaliastudios.transcoder.source;

import android.content.res.AssetFileDescriptor;
import android.media.MediaExtractor;

import androidx.annotation.NonNull;

import java.io.FileInputStream;
import java.io.IOException;

/**
* It is the caller responsibility to close the file descriptor.
*/
public class AssetFileDescriptorDataSource extends DataSourceWrapper {
public AssetFileDescriptorDataSource(@NonNull AssetFileDescriptor assetFileDescriptor) {
super(new FileDescriptorDataSource(
assetFileDescriptor.getFileDescriptor(),
assetFileDescriptor.getStartOffset(),
assetFileDescriptor.getDeclaredLength()
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,28 @@
*/
public class DataSourceWrapper implements DataSource {

private final DataSource mSource;
private DataSource mSource;

@SuppressWarnings("WeakerAccess")
protected DataSourceWrapper(@NonNull DataSource source) {
mSource = source;
}

// Only use if you know what you are doing
protected DataSourceWrapper() {
mSource = null;
}

@NonNull
protected DataSource getSource() {
return mSource;
}

// Only use if you know what you are doing
protected void setSource(@NonNull DataSource source) {
mSource = source;
}

@Override
public int getOrientation() {
return mSource.getOrientation();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.otaliastudios.transcoder.source;

import android.content.res.AssetFileDescriptor;
import android.media.MediaExtractor;
import android.media.MediaMetadataRetriever;

Expand All @@ -10,23 +11,33 @@

/**
* A {@link DataSource} backed by a file descriptor.
* It is the caller responsibility to close the file descriptor.
*/
public class FileDescriptorDataSource extends DefaultDataSource {

@NonNull
private final FileDescriptor descriptor;
private final long offset;
private final long length;

public FileDescriptorDataSource(@NonNull FileDescriptor descriptor) {
// length is intentionally less than LONG_MAX, see retriever
this(descriptor, 0, 0x7ffffffffffffffL);
}

public FileDescriptorDataSource(@NonNull FileDescriptor descriptor, long offset, long length) {
this.descriptor = descriptor;
this.offset = offset;
this.length = length > 0 ? length : 0x7ffffffffffffffL;
}

@Override
protected void initializeExtractor(@NonNull MediaExtractor extractor) throws IOException {
extractor.setDataSource(descriptor);
extractor.setDataSource(descriptor, offset, length);
}

@Override
protected void initializeRetriever(@NonNull MediaMetadataRetriever retriever) {
retriever.setDataSource(descriptor);
retriever.setDataSource(descriptor, offset, length);
}
}
Loading

0 comments on commit ba8f098

Please sign in to comment.