Skip to content

Commit

Permalink
Fix ArrayIndexOutOfBoundsException in MP4 edit lists
Browse files Browse the repository at this point in the history
The exception occurred when an edit list started at a non-sync frame with no preceding sync frame. The fix searches forward for the next sync frame in such cases, preventing the out-of-bounds access.

Issue: #2062

#cherrypick

PiperOrigin-RevId: 720642687
  • Loading branch information
rohitjoins authored and copybara-github committed Jan 28, 2025
1 parent bb37aad commit e154383
Show file tree
Hide file tree
Showing 16 changed files with 6,249 additions and 7 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
* Extractors:
* Fix handling of NAL units with lengths expressed in 1 or 2 bytes (rather
than 4).
* Fix `ArrayIndexOutOfBoundsException` in MP4 edit lists when the edit
list starts at a non-sync frame with no preceding sync frame
([#2062](https://github.com/androidx/media/issues/2062)).
* DataSource:
* Audio:
* Do not bypass `SonicAudioProcessor` when `SpeedChangingAudioProcessor`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ public static ImmutableList<String> mediaSamples() {
"sample_opus.mp4",
"sample_partially_fragmented.mp4",
"testvid_1022ms.mp4",
"sample_edit_list.mp4");
"sample_edit_list.mp4",
"sample_edit_list_no_sync_frame_before_edit.mp4");
}

@Parameter public String inputFile;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -724,16 +724,15 @@ public static TrackSampleTable parseStbl(
// frames appear after their respective sync frames. This ensures that although the result
// of the binary search might not be entirely accurate (due to the out-of-order timestamps),
// the following logic ensures correctness for both start and end indices.
//

// The startIndices calculation finds the largest timestamp that is less than or equal to
// editMediaTime. It then walks backward to ensure the index points to a sync frame, since
// decoding must start from a keyframe.
// decoding must start from a keyframe. If a sync frame is not found by walking backward, it
// walks forward from the initially found index to find a sync frame.
startIndices[i] =
Util.binarySearchFloor(
timestamps, editMediaTime, /* inclusive= */ true, /* stayInBounds= */ true);
while (startIndices[i] >= 0 && (flags[startIndices[i]] & C.BUFFER_FLAG_KEY_FRAME) == 0) {
startIndices[i]--;
}

// The endIndices calculation finds the smallest timestamp that is greater than
// editMediaTime + editDuration, except when omitZeroDurationClippedSample is true, in which
// case it finds the smallest timestamp that is greater than or equal to editMediaTime +
Expand All @@ -744,7 +743,21 @@ public static TrackSampleTable parseStbl(
editMediaTime + editDuration,
/* inclusive= */ omitZeroDurationClippedSample,
/* stayInBounds= */ false);
if (track.type == C.TRACK_TYPE_VIDEO) {

int initialStartIndex = startIndices[i];
while (startIndices[i] >= 0 && (flags[startIndices[i]] & C.BUFFER_FLAG_KEY_FRAME) == 0) {
startIndices[i]--;
}

if (startIndices[i] < 0) {
startIndices[i] = initialStartIndex;
while (startIndices[i] < endIndices[i]
&& (flags[startIndices[i]] & C.BUFFER_FLAG_KEY_FRAME) == 0) {
startIndices[i]++;
}
}

if (track.type == C.TRACK_TYPE_VIDEO && startIndices[i] != endIndices[i]) {
// To account for out-of-order video frames that may have timestamps smaller than or equal
// to editMediaTime + editDuration, but still fall within the valid range, the loop walks
// forward through the timestamps array to ensure all frames with timestamps within the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ public void mp4SampleWithEditList() throws Exception {
assertExtractorBehavior("media/mp4/sample_edit_list.mp4");
}

@Test
public void mp4SampleWithEditListAndNoSyncFrameBeforeEdit() throws Exception {
assertExtractorBehavior("media/mp4/sample_edit_list_no_sync_frame_before_edit.mp4");
}

@Test
public void mp4SampleWithEmptyTrack() throws Exception {
assertExtractorBehavior("media/mp4/sample_empty_track.mp4");
Expand Down
Loading

0 comments on commit e154383

Please sign in to comment.