diff --git a/library/common/src/main/java/com/google/android/exoplayer2/metadata/mp4/SefSlowMotion.java b/library/common/src/main/java/com/google/android/exoplayer2/metadata/mp4/SefSlowMotion.java new file mode 100644 index 0000000000..d3e9f29d5c --- /dev/null +++ b/library/common/src/main/java/com/google/android/exoplayer2/metadata/mp4/SefSlowMotion.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.metadata.mp4; + +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.Nullable; +import com.google.android.exoplayer2.metadata.Metadata; +import com.google.android.exoplayer2.util.Util; +import com.google.common.base.Objects; +import java.util.ArrayList; +import java.util.List; + +/** Holds information about the segments of slow motion playback within a track. */ +public final class SefSlowMotion implements Metadata.Entry { + + /** Holds information about a single segment of slow motion playback within a track. */ + public static final class Segment implements Parcelable { + + /** The start time, in milliseconds, of the track segment that is intended to be slow motion. */ + public final int startTimeMs; + /** The end time, in milliseconds, of the track segment that is intended to be slow motion. */ + public final int endTimeMs; + /** + * The speed reduction factor. + * + *

For example, 4 would mean the segment should be played at a quarter (1/4) of the normal + * speed. + */ + public final int speedDivisor; + + /** + * Creates an instance. + * + * @param startTimeMs See {@link #startTimeMs}. + * @param endTimeMs See {@link #endTimeMs}. + * @param speedDivisor See {@link #speedDivisor}. + */ + public Segment(int startTimeMs, int endTimeMs, int speedDivisor) { + this.startTimeMs = startTimeMs; + this.endTimeMs = endTimeMs; + this.speedDivisor = speedDivisor; + } + + @Override + public String toString() { + return Util.formatInvariant( + "Segment: startTimeMs=%d, endTimeMs=%d, speedDivisor=%d", + startTimeMs, endTimeMs, speedDivisor); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Segment segment = (Segment) o; + return startTimeMs == segment.startTimeMs + && endTimeMs == segment.endTimeMs + && speedDivisor == segment.speedDivisor; + } + + @Override + public int hashCode() { + return Objects.hashCode(startTimeMs, endTimeMs, speedDivisor); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(startTimeMs); + dest.writeInt(endTimeMs); + dest.writeInt(speedDivisor); + } + + public static final Creator CREATOR = + new Creator() { + + @Override + public Segment createFromParcel(Parcel in) { + int startTimeMs = in.readInt(); + int endTimeMs = in.readInt(); + int speedDivisor = in.readInt(); + return new Segment(startTimeMs, endTimeMs, speedDivisor); + } + + @Override + public Segment[] newArray(int size) { + return new Segment[size]; + } + }; + } + + public final List segments; + + /** Creates an instance with a list of {@link Segment}s. */ + public SefSlowMotion(List segments) { + this.segments = segments; + } + + @Override + public String toString() { + return "SefSlowMotion: segments=" + segments; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SefSlowMotion that = (SefSlowMotion) o; + return segments.equals(that.segments); + } + + @Override + public int hashCode() { + return segments.hashCode(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeList(segments); + } + + public static final Creator CREATOR = + new Creator() { + @Override + public SefSlowMotion createFromParcel(Parcel in) { + List slowMotionSegments = new ArrayList<>(); + in.readList(slowMotionSegments, Segment.class.getClassLoader()); + return new SefSlowMotion(slowMotionSegments); + } + + @Override + public SefSlowMotion[] newArray(int size) { + return new SefSlowMotion[size]; + } + }; +} diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/SefSlowMotionTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/SefSlowMotionTest.java new file mode 100644 index 0000000000..fe602a1790 --- /dev/null +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/SefSlowMotionTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.extractor.mp4; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.android.exoplayer2.metadata.mp4.SefSlowMotion; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit test for {@link SefSlowMotion} */ +@RunWith(AndroidJUnit4.class) +public class SefSlowMotionTest { + + @Test + public void parcelable() { + List segments = new ArrayList<>(); + segments.add( + new SefSlowMotion.Segment( + /* startTimeMs= */ 1000, /* endTimeMs= */ 2000, /* speedDivisor= */ 4)); + segments.add( + new SefSlowMotion.Segment( + /* startTimeMs= */ 2600, /* endTimeMs= */ 4000, /* speedDivisor= */ 8)); + segments.add( + new SefSlowMotion.Segment( + /* startTimeMs= */ 8765, /* endTimeMs= */ 12485, /* speedDivisor= */ 16)); + + SefSlowMotion sefSlowMotionToParcel = new SefSlowMotion(segments); + Parcel parcel = Parcel.obtain(); + sefSlowMotionToParcel.writeToParcel(parcel, /* flags= */ 0); + parcel.setDataPosition(0); + + SefSlowMotion sefSlowMotionFromParcel = SefSlowMotion.CREATOR.createFromParcel(parcel); + assertThat(sefSlowMotionFromParcel).isEqualTo(sefSlowMotionToParcel); + + parcel.recycle(); + } + + @Test + public void segment_parcelable() { + SefSlowMotion.Segment segmentToParcel = + new SefSlowMotion.Segment( + /* startTimeMs= */ 1000, /* endTimeMs= */ 2000, /* speedDivisor= */ 4); + + Parcel parcel = Parcel.obtain(); + segmentToParcel.writeToParcel(parcel, /* flags= */ 0); + parcel.setDataPosition(0); + + SefSlowMotion.Segment segmentFromParcel = + SefSlowMotion.Segment.CREATOR.createFromParcel(parcel); + assertThat(segmentFromParcel).isEqualTo(segmentToParcel); + + parcel.recycle(); + } +}