Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes a null pointer crash (Github Issue #269) #271

Merged
merged 2 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class MediaTransformer {

public static final int DEFAULT_KEY_FRAME_INTERVAL = 5;
private static final int DEFAULT_AUDIO_BITRATE = 256_000;
private static final int DEFAULT_VIDEO_BITRATE = 10_000_000;
private static final int DEFAULT_FRAME_RATE = 30;

private static final String TAG = MediaTransformer.class.getSimpleName();
Expand Down Expand Up @@ -412,6 +413,10 @@ private MediaFormat createTargetMediaFormat(@NonNull MediaSource mediaSource,
sourceMediaFormat.getInteger(MediaFormat.KEY_WIDTH),
sourceMediaFormat.getInteger(MediaFormat.KEY_HEIGHT));
int targetBitrate = TranscoderUtils.estimateVideoTrackBitrate(mediaSource, sourceTrackIndex);
if (targetBitrate <= 0) {
// Use a default value in case of failure to extract value from source media
targetBitrate = DEFAULT_VIDEO_BITRATE;
}
targetMediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, targetBitrate);

int targetKeyFrameInterval = DEFAULT_KEY_FRAME_INTERVAL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class MediaExtractorMediaSource implements MediaSource {

private int orientationHint;
private long size;
private final long duration;

public MediaExtractorMediaSource(@NonNull Context context, @NonNull Uri uri) throws MediaSourceException {
this(context, uri, new MediaRange(0, Long.MAX_VALUE));
Expand All @@ -52,6 +53,8 @@ public MediaExtractorMediaSource(@NonNull Context context, @NonNull Uri uri, @No
if (rotation != null) {
orientationHint = Integer.parseInt(rotation);
}
String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
duration = (durationStr != null) ? Long.parseLong(durationStr) : -1L;
size = TranscoderUtils.getSize(context, uri);
// Release unused anymore MediaMetadataRetriever instance
releaseQuietly(mediaMetadataRetriever);
Expand Down Expand Up @@ -124,6 +127,11 @@ public MediaRange getSelection() {
return mediaRange;
}

@Override
public long getDuration() {
return duration;
}

private void releaseQuietly(MediaMetadataRetriever mediaMetadataRetriever) {
try {
mediaMetadataRetriever.release();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,13 @@ public interface MediaSource {
default MediaRange getSelection() {
return new MediaRange(0, Long.MAX_VALUE);
}

/**
* Returns the playback duration of the media if applicable and known
*
* @return playback duration in milliseconds, -1 if unknown
*/
default long getDuration() {
return -1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ public static int estimateVideoTrackBitrate(@NonNull MediaSource mediaSource, in
if (videoTrackFormat.containsKey(MediaFormat.KEY_BIT_RATE)) {
return videoTrackFormat.getInteger(MediaFormat.KEY_BIT_RATE);
}
float videoTrackDuration = TimeUtils.microsToSeconds(videoTrackFormat.getLong(MediaFormat.KEY_DURATION));
if (videoTrackDuration == 0) {
return 0;
float videoTrackDuration = estimateVideoTrackDuration(mediaSource, videoTrackFormat);
if (videoTrackDuration <= 0) {
return -1;
}

float unallocatedSize = mediaSource.getSize();
Expand All @@ -149,9 +149,12 @@ public static int estimateVideoTrackBitrate(@NonNull MediaSource mediaSource, in
} else {
String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
if (mimeType.startsWith("video")) {
totalPixels += trackFormat.getInteger(MediaFormat.KEY_WIDTH)
* trackFormat.getInteger(MediaFormat.KEY_HEIGHT)
* TimeUtils.microsToSeconds(trackFormat.getLong(MediaFormat.KEY_DURATION));
float trackDuration = estimateVideoTrackDuration(mediaSource, trackFormat);
if (trackDuration > 0) {
totalPixels += trackFormat.getInteger(MediaFormat.KEY_WIDTH)
* trackFormat.getInteger(MediaFormat.KEY_HEIGHT)
* trackDuration;
}
}
}
}
Expand All @@ -165,6 +168,23 @@ public static int estimateVideoTrackBitrate(@NonNull MediaSource mediaSource, in
return (int) (trackSize * BITS_IN_BYTE / videoTrackDuration);
}

/**
* Returns the duration of the given video track. If the given track does not contain a valid
* duration value in its meta data, then the duration value associated with the media source
* shall be returned.
*
* @param mediaSource {@link MediaSource} which contains the video track
* @param videoTrackFormat MediaFormat of the track whose duration needs to be determined
*
* @return Duration of the given video track in seconds.
*/
@VisibleForTesting
static float estimateVideoTrackDuration(MediaSource mediaSource, MediaFormat videoTrackFormat) {
return videoTrackFormat.containsKey(MediaFormat.KEY_DURATION) ?
TimeUtils.microsToSeconds(videoTrackFormat.getLong(MediaFormat.KEY_DURATION)) :
TimeUtils.millisToSeconds(mediaSource.getDuration());
}

/**
* Get size of data abstracted by uri
* @param context context to access uri
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,34 @@ public void useVideoTrackBitrateAsEstimationWhenPresent() {
assertThat(estimatedBitrate, is(VIDEO_BIT_RATE));
}

@Test
public void useMediaDurationForVideoTrackBitrateEstimationWhenTrackMetadataDoesNotExist() {
long mediaDuration = 50000L; int bitrateUsingMediaDuration = 11400000;
when(mediaSource.getTrackFormat(0)).thenReturn(videoMediaFormat);
when(mediaSource.getDuration()).thenReturn(mediaDuration);
when(videoMediaFormat.containsKey(MediaFormat.KEY_BIT_RATE)).thenReturn(false);
when(videoMediaFormat.containsKey(MediaFormat.KEY_DURATION)).thenReturn(false);

when(mediaSource.getSize()).thenReturn(VIDEO_BIT_RATE * DURATION_S / 8);
when(mediaSource.getTrackCount()).thenReturn(1);

int estimatedBitrate = TranscoderUtils.estimateVideoTrackBitrate(mediaSource, 0);

assertThat(estimatedBitrate, is(bitrateUsingMediaDuration));
}

@Test
public void returnInvalidBitrateWhenNeitherTrackDurationNorMediaDurationIsAvailable() {
int invalidBitrate = -1;
when(mediaSource.getTrackFormat(0)).thenReturn(videoMediaFormat);
when(videoMediaFormat.containsKey(MediaFormat.KEY_BIT_RATE)).thenReturn(false);
when(videoMediaFormat.containsKey(MediaFormat.KEY_DURATION)).thenReturn(false);
when(mediaSource.getDuration()).thenReturn((long) invalidBitrate);

int estimatedBitrate = TranscoderUtils.estimateVideoTrackBitrate(mediaSource, 0);
assertThat(estimatedBitrate, is(invalidBitrate));
}

@Test
public void estimateVideoTrackBitrateWhenSingleVideoTrack() {
when(mediaSource.getSize()).thenReturn(VIDEO_BIT_RATE * DURATION_S / 8);
Expand Down Expand Up @@ -250,6 +278,20 @@ public void estimateVideoTrackBitrateWhenMultipleVideoTracks() {
assertEquals(estimatedBitrate, VIDEO_BIT_RATE, 1);
}

@Test
public void estimateVideoTrackDurationFromMetadata() {
float durationSecs = TranscoderUtils.estimateVideoTrackDuration(mediaSource, videoMediaFormat);
assertThat(durationSecs, is((float) DURATION_S));
}

@Test
public void useMediaDurationAsFallbackWhenEstimatingVideoTrackDuration() {
long mediaDuration = 50_000L;
when(mediaSource.getDuration()).thenReturn(mediaDuration);
when(videoMediaFormat.containsKey(MediaFormat.KEY_DURATION)).thenReturn(false);
float durationSecs = TranscoderUtils.estimateVideoTrackDuration(mediaSource, videoMediaFormat);
assertThat(durationSecs, is((float) mediaDuration/1000));
}

@Test
public void estimateWhenTrimmedFromBeginningToMiddle() {
Expand Down
Loading