use of androidx.media3.common.Metadata.Entry in project media by androidx.
the class ServerSideAdInsertionMediaSource method setAdPlaybackStates.
/**
* Sets the map of {@link AdPlaybackState ad playback states} published by this source. The key is
* the period UID of a period in the {@link
* AdPlaybackStateUpdater#onAdPlaybackStateUpdateRequested(Timeline)} content timeline}.
*
* <p>Each period has an {@link AdPlaybackState} that tells where in the period the ad groups
* start and end. Must only contain server-side inserted ad groups. The number of ad groups and
* the number of ads within an ad group may only increase. The durations of ads may change and the
* positions of future ad groups may change. Post-roll ad groups with {@link C#TIME_END_OF_SOURCE}
* must be empty and can be used as a placeholder for a future ad group.
*
* <p>May be called from any thread.
*
* @param adPlaybackStates The map of {@link AdPlaybackState} keyed by their period UID.
*/
public void setAdPlaybackStates(ImmutableMap<Object, AdPlaybackState> adPlaybackStates) {
checkArgument(!adPlaybackStates.isEmpty());
Object adsId = checkNotNull(adPlaybackStates.values().asList().get(0).adsId);
for (Map.Entry<Object, AdPlaybackState> entry : adPlaybackStates.entrySet()) {
Object periodUid = entry.getKey();
AdPlaybackState adPlaybackState = entry.getValue();
checkArgument(Util.areEqual(adsId, adPlaybackState.adsId));
@Nullable AdPlaybackState oldAdPlaybackState = this.adPlaybackStates.get(periodUid);
if (oldAdPlaybackState != null) {
for (int i = adPlaybackState.removedAdGroupCount; i < adPlaybackState.adGroupCount; i++) {
AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(i);
checkArgument(adGroup.isServerSideInserted);
if (i < oldAdPlaybackState.adGroupCount) {
checkArgument(getAdCountInGroup(adPlaybackState, /* adGroupIndex= */
i) >= getAdCountInGroup(oldAdPlaybackState, /* adGroupIndex= */
i));
}
if (adGroup.timeUs == C.TIME_END_OF_SOURCE) {
checkArgument(getAdCountInGroup(adPlaybackState, /* adGroupIndex= */
i) == 0);
}
}
}
}
synchronized (this) {
if (playbackHandler == null) {
this.adPlaybackStates = adPlaybackStates;
} else {
playbackHandler.post(() -> {
for (SharedMediaPeriod mediaPeriod : mediaPeriods.values()) {
@Nullable AdPlaybackState adPlaybackState = adPlaybackStates.get(mediaPeriod.periodUid);
if (adPlaybackState != null) {
mediaPeriod.updateAdPlaybackState(adPlaybackState);
}
}
if (lastUsedMediaPeriod != null) {
@Nullable AdPlaybackState adPlaybackState = adPlaybackStates.get(lastUsedMediaPeriod.periodUid);
if (adPlaybackState != null) {
lastUsedMediaPeriod.updateAdPlaybackState(adPlaybackState);
}
}
this.adPlaybackStates = adPlaybackStates;
if (contentTimeline != null) {
refreshSourceInfo(new ServerSideAdInsertionTimeline(contentTimeline, adPlaybackStates));
}
});
}
}
}
use of androidx.media3.common.Metadata.Entry in project media by androidx.
the class VbriSeeker method create.
/**
* Returns a {@link VbriSeeker} for seeking in the stream, if required information is present.
* Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the
* caller should reset it.
*
* @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown.
* @param position The position of the start of this frame in the stream.
* @param mpegAudioHeader The MPEG audio header associated with the frame.
* @param frame The data in this audio frame, with its position set to immediately after the
* 'VBRI' tag.
* @return A {@link VbriSeeker} for seeking in the stream, or {@code null} if the required
* information is not present.
*/
@Nullable
public static VbriSeeker create(long inputLength, long position, MpegAudioUtil.Header mpegAudioHeader, ParsableByteArray frame) {
frame.skipBytes(10);
int numFrames = frame.readInt();
if (numFrames <= 0) {
return null;
}
int sampleRate = mpegAudioHeader.sampleRate;
long durationUs = Util.scaleLargeTimestamp(numFrames, C.MICROS_PER_SECOND * (sampleRate >= 32000 ? 1152 : 576), sampleRate);
int entryCount = frame.readUnsignedShort();
int scale = frame.readUnsignedShort();
int entrySize = frame.readUnsignedShort();
frame.skipBytes(2);
long minPosition = position + mpegAudioHeader.frameSize;
// Read table of contents entries.
long[] timesUs = new long[entryCount];
long[] positions = new long[entryCount];
for (int index = 0; index < entryCount; index++) {
timesUs[index] = (index * durationUs) / entryCount;
// Ensure positions do not fall within the frame containing the VBRI header. This constraint
// will normally only apply to the first entry in the table.
positions[index] = max(position, minPosition);
int segmentSize;
switch(entrySize) {
case 1:
segmentSize = frame.readUnsignedByte();
break;
case 2:
segmentSize = frame.readUnsignedShort();
break;
case 3:
segmentSize = frame.readUnsignedInt24();
break;
case 4:
segmentSize = frame.readUnsignedIntToInt();
break;
default:
return null;
}
position += segmentSize * scale;
}
if (inputLength != C.LENGTH_UNSET && inputLength != position) {
Log.w(TAG, "VBRI data size mismatch: " + inputLength + ", " + position);
}
return new VbriSeeker(timesUs, positions, durationUs, /* dataEndPosition= */
position);
}
use of androidx.media3.common.Metadata.Entry in project media by androidx.
the class AtomParsers method parseMdtaFromMeta.
/**
* Parses a metadata meta atom if it contains metadata with handler 'mdta'.
*
* @param meta The metadata atom to decode.
* @return Parsed metadata, or null.
*/
@Nullable
public static Metadata parseMdtaFromMeta(Atom.ContainerAtom meta) {
@Nullable Atom.LeafAtom hdlrAtom = meta.getLeafAtomOfType(Atom.TYPE_hdlr);
@Nullable Atom.LeafAtom keysAtom = meta.getLeafAtomOfType(Atom.TYPE_keys);
@Nullable Atom.LeafAtom ilstAtom = meta.getLeafAtomOfType(Atom.TYPE_ilst);
if (hdlrAtom == null || keysAtom == null || ilstAtom == null || parseHdlr(hdlrAtom.data) != TYPE_mdta) {
// There isn't enough information to parse the metadata, or the handler type is unexpected.
return null;
}
// Parse metadata keys.
ParsableByteArray keys = keysAtom.data;
keys.setPosition(Atom.FULL_HEADER_SIZE);
int entryCount = keys.readInt();
String[] keyNames = new String[entryCount];
for (int i = 0; i < entryCount; i++) {
int entrySize = keys.readInt();
// keyNamespace
keys.skipBytes(4);
int keySize = entrySize - 8;
keyNames[i] = keys.readString(keySize);
}
// Parse metadata items.
ParsableByteArray ilst = ilstAtom.data;
ilst.setPosition(Atom.HEADER_SIZE);
ArrayList<Metadata.Entry> entries = new ArrayList<>();
while (ilst.bytesLeft() > Atom.HEADER_SIZE) {
int atomPosition = ilst.getPosition();
int atomSize = ilst.readInt();
int keyIndex = ilst.readInt() - 1;
if (keyIndex >= 0 && keyIndex < keyNames.length) {
String key = keyNames[keyIndex];
@Nullable Metadata.Entry entry = MetadataUtil.parseMdtaMetadataEntryFromIlst(ilst, atomPosition + atomSize, key);
if (entry != null) {
entries.add(entry);
}
} else {
Log.w(TAG, "Skipped metadata with unknown key index: " + keyIndex);
}
ilst.setPosition(atomPosition + atomSize);
}
return entries.isEmpty() ? null : new Metadata(entries);
}
use of androidx.media3.common.Metadata.Entry in project media by androidx.
the class TextInformationFrameTest method populateMediaMetadata_setsBuilderValues.
@Test
public void populateMediaMetadata_setsBuilderValues() {
String title = "the title";
String artist = "artist";
String albumTitle = "album title";
String albumArtist = "album Artist";
String trackNumberInfo = "11/17";
String recordingYear = "2000";
String recordingMonth = "07";
String recordingDay = "10";
String releaseDate = "2001-01-02T00:00:00";
String composer = "composer";
String conductor = "conductor";
String writer = "writer";
List<Metadata.Entry> entries = ImmutableList.of(new TextInformationFrame(/* id= */
"TT2", /* description= */
null, /* value= */
title), new TextInformationFrame(/* id= */
"TP1", /* description= */
null, /* value= */
artist), new TextInformationFrame(/* id= */
"TAL", /* description= */
null, /* value= */
albumTitle), new TextInformationFrame(/* id= */
"TP2", /* description= */
null, /* value= */
albumArtist), new TextInformationFrame(/* id= */
"TRK", /* description= */
null, /* value= */
trackNumberInfo), new TextInformationFrame(/* id= */
"TYE", /* description= */
null, /* value= */
recordingYear), new TextInformationFrame(/* id= */
"TDA", /* description= */
null, /* value= */
recordingDay + recordingMonth), new TextInformationFrame(/* id= */
"TDRL", /* description= */
null, /* value= */
releaseDate), new TextInformationFrame(/* id= */
"TCM", /* description= */
null, /* value= */
composer), new TextInformationFrame(/* id= */
"TP3", /* description= */
null, /* value= */
conductor), new TextInformationFrame(/* id= */
"TXT", /* description= */
null, /* value= */
writer));
MediaMetadata.Builder builder = MediaMetadata.EMPTY.buildUpon();
for (Metadata.Entry entry : entries) {
entry.populateMediaMetadata(builder);
}
MediaMetadata mediaMetadata = builder.build();
assertThat(mediaMetadata.title.toString()).isEqualTo(title);
assertThat(mediaMetadata.artist.toString()).isEqualTo(artist);
assertThat(mediaMetadata.albumTitle.toString()).isEqualTo(albumTitle);
assertThat(mediaMetadata.albumArtist.toString()).isEqualTo(albumArtist);
assertThat(mediaMetadata.trackNumber).isEqualTo(11);
assertThat(mediaMetadata.totalTrackCount).isEqualTo(17);
assertThat(mediaMetadata.recordingYear).isEqualTo(2000);
assertThat(mediaMetadata.recordingMonth).isEqualTo(7);
assertThat(mediaMetadata.recordingDay).isEqualTo(10);
assertThat(mediaMetadata.releaseYear).isEqualTo(2001);
assertThat(mediaMetadata.releaseMonth).isEqualTo(1);
assertThat(mediaMetadata.releaseDay).isEqualTo(2);
assertThat(mediaMetadata.composer.toString()).isEqualTo(composer);
assertThat(mediaMetadata.conductor.toString()).isEqualTo(conductor);
assertThat(mediaMetadata.writer.toString()).isEqualTo(writer);
}
use of androidx.media3.common.Metadata.Entry in project media by androidx.
the class VorbisCommentTest method populateMediaMetadata_setsMediaMetadataValues.
@Test
public void populateMediaMetadata_setsMediaMetadataValues() {
String title = "the title";
String artist = "artist";
String albumTitle = "album title";
String albumArtist = "album Artist";
String description = "a description about the audio.";
List<Metadata.Entry> entries = ImmutableList.of(new VorbisComment("TITLE", title), new VorbisComment("ARTIST", artist), new VorbisComment("ALBUM", albumTitle), new VorbisComment("ALBUMARTIST", albumArtist), new VorbisComment("DESCRIPTION", description));
MediaMetadata.Builder builder = MediaMetadata.EMPTY.buildUpon();
for (Metadata.Entry entry : entries) {
entry.populateMediaMetadata(builder);
}
MediaMetadata mediaMetadata = builder.build();
assertThat(mediaMetadata.title.toString()).isEqualTo(title);
assertThat(mediaMetadata.artist.toString()).isEqualTo(artist);
assertThat(mediaMetadata.albumTitle.toString()).isEqualTo(albumTitle);
assertThat(mediaMetadata.albumArtist.toString()).isEqualTo(albumArtist);
assertThat(mediaMetadata.description.toString()).isEqualTo(description);
}
Aggregations