Add Mp4OrientationData class

Mp4Muxer now has a single method to accept different
types of metadata.

PiperOrigin-RevId: 614646331
This commit is contained in:
sheenachhabra 2024-03-11 06:43:00 -07:00 committed by Copybara-Service
parent e4a55844d0
commit aba395ca8b
8 changed files with 164 additions and 19 deletions

View File

@ -0,0 +1,97 @@
/*
* Copyright 2024 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 androidx.media3.container;
import static androidx.media3.common.util.Assertions.checkArgument;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.Nullable;
import androidx.media3.common.Metadata;
import androidx.media3.common.util.UnstableApi;
/** Stores the orientation hint for the video playback. */
@UnstableApi
public final class Mp4OrientationData implements Metadata.Entry {
public final int orientation;
/**
* Creates an instance.
*
* @param orientation The orientation, in degrees. The supported values are 0, 90, 180 and 270
* (degrees).
*/
public Mp4OrientationData(int orientation) {
checkArgument(
orientation == 0 || orientation == 90 || orientation == 180 || orientation == 270,
"Unsupported orientation");
this.orientation = orientation;
}
private Mp4OrientationData(Parcel in) {
orientation = in.readInt();
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Mp4OrientationData)) {
return false;
}
Mp4OrientationData other = (Mp4OrientationData) obj;
return orientation == other.orientation;
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + Integer.hashCode(orientation);
return result;
}
@Override
public String toString() {
return "Orientation= " + orientation;
}
// Parcelable implementation.
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(orientation);
}
public static final Parcelable.Creator<Mp4OrientationData> CREATOR =
new Parcelable.Creator<Mp4OrientationData>() {
@Override
public Mp4OrientationData createFromParcel(Parcel in) {
return new Mp4OrientationData(in);
}
@Override
public Mp4OrientationData[] newArray(int size) {
return new Mp4OrientationData[size];
}
};
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2024 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 androidx.media3.container;
import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Test for {@link Mp4OrientationData}. */
@RunWith(AndroidJUnit4.class)
public final class Mp4OrientationDataTest {
@Test
public void parcelable() {
Mp4OrientationData mp4OrientationData = new Mp4OrientationData(/* orientation= */ 90);
Parcel parcel = Parcel.obtain();
mp4OrientationData.writeToParcel(parcel, /* flags= */ 0);
parcel.setDataPosition(0);
Mp4OrientationData mp4OrientationDataFromParcel =
Mp4OrientationData.CREATOR.createFromParcel(parcel);
assertThat(mp4OrientationDataFromParcel).isEqualTo(mp4OrientationData);
parcel.recycle();
}
}

View File

@ -20,6 +20,7 @@ import static androidx.media3.container.Mp4TimestampData.unixTimeToMp4TimeSecond
import androidx.media3.common.Metadata;
import androidx.media3.container.MdtaMetadataEntry;
import androidx.media3.container.Mp4LocationData;
import androidx.media3.container.Mp4OrientationData;
import androidx.media3.container.Mp4TimestampData;
import androidx.media3.container.XmpData;
import java.util.ArrayList;
@ -28,14 +29,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Collects and provides metadata: location, FPS, XMP data, etc. */
/* package */ final class MetadataCollector {
public int orientation;
public Mp4OrientationData orientationData;
public @MonotonicNonNull Mp4LocationData locationData;
public List<MdtaMetadataEntry> metadataEntries;
public Mp4TimestampData timestampData;
public @MonotonicNonNull XmpData xmpData;
/** Creates an instance. */
public MetadataCollector() {
orientation = 0;
orientationData = new Mp4OrientationData(/* orientation= */ 0);
metadataEntries = new ArrayList<>();
long currentTimeInMp4TimeSeconds = unixTimeToMp4TimeSeconds(System.currentTimeMillis());
timestampData =
@ -44,12 +46,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/* modificationTimestampSeconds= */ currentTimeInMp4TimeSeconds);
}
public void setOrientation(int orientation) {
this.orientation = orientation;
}
/** Adds metadata for the output file. */
public void addMetadata(Metadata.Entry metadata) {
if (metadata instanceof Mp4LocationData) {
if (metadata instanceof Mp4OrientationData) {
orientationData = (Mp4OrientationData) metadata;
} else if (metadata instanceof Mp4LocationData) {
locationData = (Mp4LocationData) metadata;
} else if (metadata instanceof Mp4TimestampData) {
timestampData = (Mp4TimestampData) metadata;

View File

@ -157,7 +157,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
(int) Mp4Utils.vuFromUs(trackDurationUs, MVHD_TIMEBASE),
creationTimestampSeconds,
modificationTimestampSeconds,
metadataCollector.orientation,
metadataCollector.orientationData.orientation,
format),
Boxes.mdia(
Boxes.mdhd(

View File

@ -31,6 +31,7 @@ import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.container.MdtaMetadataEntry;
import androidx.media3.container.Mp4LocationData;
import androidx.media3.container.Mp4OrientationData;
import androidx.media3.container.Mp4TimestampData;
import androidx.media3.container.XmpData;
import com.google.common.collect.ImmutableList;
@ -216,7 +217,8 @@ public final class Mp4Muxer {
* <p>For the list of supported metadata refer to {@link Mp4Muxer#addMetadata(Metadata.Entry)}.
*/
public static boolean isMetadataSupported(Metadata.Entry metadata) {
return metadata instanceof Mp4LocationData
return metadata instanceof Mp4OrientationData
|| metadata instanceof Mp4LocationData
|| (metadata instanceof Mp4TimestampData
&& isMp4TimestampDataSupported((Mp4TimestampData) metadata))
|| (metadata instanceof MdtaMetadataEntry
@ -225,12 +227,11 @@ public final class Mp4Muxer {
}
/**
* Sets the orientation hint for the video playback.
*
* @param orientation The orientation, in degrees.
* @deprecated Use {@link #addMetadata(Metadata.Entry)} with {@link Mp4OrientationData} instead.
*/
@Deprecated
public void setOrientation(int orientation) {
metadataCollector.setOrientation(orientation);
addMetadata(new Mp4OrientationData(orientation));
}
/**
@ -289,6 +290,7 @@ public final class Mp4Muxer {
* <p>List of supported {@linkplain Metadata.Entry metadata entries}:
*
* <ul>
* <li>{@link Mp4OrientationData}
* <li>{@link Mp4LocationData}
* <li>{@link Mp4TimestampData}
* <li>{@link MdtaMetadataEntry}: Only {@linkplain MdtaMetadataEntry#TYPE_INDICATOR_STRING

View File

@ -27,6 +27,7 @@ import android.util.Pair;
import androidx.media3.common.util.Util;
import androidx.media3.container.MdtaMetadataEntry;
import androidx.media3.container.Mp4LocationData;
import androidx.media3.container.Mp4OrientationData;
import androidx.media3.container.Mp4TimestampData;
import androidx.media3.container.XmpData;
import androidx.media3.extractor.mp4.Mp4Extractor;
@ -59,7 +60,7 @@ public class Mp4MuxerEndToEndTest {
try {
mp4Muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
mp4Muxer.setOrientation(90);
mp4Muxer.addMetadata(new Mp4OrientationData(/* orientation= */ 90));
mp4Muxer.addMetadata(
new MdtaMetadataEntry(
"key",
@ -219,7 +220,7 @@ public class Mp4MuxerEndToEndTest {
byte[] xmpBytes = TestUtil.getByteArray(context, XMP_SAMPLE_DATA);
try {
muxer.setOrientation(90);
muxer.addMetadata(new Mp4OrientationData(/* orientation= */ 90));
muxer.addMetadata(new Mp4LocationData(/* latitude= */ 33.0f, /* longitude= */ -120f));
float captureFps = 120.0f;
muxer.addMetadata(

View File

@ -21,6 +21,7 @@ import static androidx.media3.muxer.MuxerTestUtil.XMP_SAMPLE_DATA;
import android.content.Context;
import android.media.MediaCodec.BufferInfo;
import android.util.Pair;
import androidx.media3.container.Mp4OrientationData;
import androidx.media3.container.Mp4TimestampData;
import androidx.media3.extractor.mp4.Mp4Extractor;
import androidx.media3.muxer.Mp4Muxer.TrackToken;
@ -84,7 +85,7 @@ public class Mp4MuxerMetadataTest {
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.setOrientation(90);
muxer.addMetadata(new Mp4OrientationData(/* orientation= */ 90));
} finally {
muxer.close();
}
@ -111,7 +112,7 @@ public class Mp4MuxerMetadataTest {
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.setOrientation(180);
muxer.addMetadata(new Mp4OrientationData(/* orientation= */ 180));
} finally {
muxer.close();
}
@ -138,7 +139,7 @@ public class Mp4MuxerMetadataTest {
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.setOrientation(270);
muxer.addMetadata(new Mp4OrientationData(/* orientation= */ 270));
} finally {
muxer.close();
}

View File

@ -24,6 +24,7 @@ import androidx.media3.common.Format;
import androidx.media3.common.Metadata;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.container.Mp4OrientationData;
import androidx.media3.muxer.Mp4Muxer;
import androidx.media3.muxer.Mp4Muxer.TrackToken;
import com.google.common.collect.ImmutableList;
@ -184,7 +185,7 @@ public final class InAppMuxer implements Muxer {
trackTokenList.add(trackToken);
if (MimeTypes.isVideo(format.sampleMimeType)) {
mp4Muxer.setOrientation(format.rotationDegrees);
mp4Muxer.addMetadata(new Mp4OrientationData(format.rotationDegrees));
}
return trackTokenList.size() - 1;