Search in sources :

Example 16 with Entry

use of com.google.android.exoplayer2.metadata.Metadata.Entry in project ExoPlayer by google.

the class VorbisUtil method parseVorbisComments.

/**
 * Builds a {@link Metadata} instance from a list of Vorbis Comments.
 *
 * <p>METADATA_BLOCK_PICTURE comments will be transformed into {@link PictureFrame} entries. All
 * others will be transformed into {@link VorbisComment} entries.
 *
 * @param vorbisComments The raw input of comments, as a key-value pair KEY=VAL.
 * @return The fully parsed Metadata instance. Null if no vorbis comments could be parsed.
 */
@Nullable
public static Metadata parseVorbisComments(List<String> vorbisComments) {
    List<Entry> metadataEntries = new ArrayList<>();
    for (int i = 0; i < vorbisComments.size(); i++) {
        String vorbisComment = vorbisComments.get(i);
        String[] keyAndValue = Util.splitAtFirst(vorbisComment, "=");
        if (keyAndValue.length != 2) {
            Log.w(TAG, "Failed to parse Vorbis comment: " + vorbisComment);
            continue;
        }
        if (keyAndValue[0].equals("METADATA_BLOCK_PICTURE")) {
            // Decode it from Base64 and transform it into a PictureFrame.
            try {
                byte[] decoded = Base64.decode(keyAndValue[1], Base64.DEFAULT);
                metadataEntries.add(PictureFrame.fromPictureBlock(new ParsableByteArray(decoded)));
            } catch (RuntimeException e) {
                Log.w(TAG, "Failed to parse vorbis picture", e);
            }
        } else {
            VorbisComment entry = new VorbisComment(keyAndValue[0], keyAndValue[1]);
            metadataEntries.add(entry);
        }
    }
    return metadataEntries.isEmpty() ? null : new Metadata(metadataEntries);
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) Entry(com.google.android.exoplayer2.metadata.Metadata.Entry) ArrayList(java.util.ArrayList) Metadata(com.google.android.exoplayer2.metadata.Metadata) VorbisComment(com.google.android.exoplayer2.metadata.vorbis.VorbisComment) Nullable(androidx.annotation.Nullable)

Example 17 with Entry

use of com.google.android.exoplayer2.metadata.Metadata.Entry in project ExoPlayer by google.

the class HlsMediaPeriod method buildAndPrepareMainSampleStreamWrapper.

/**
 * This method creates and starts preparation of the main {@link HlsSampleStreamWrapper}.
 *
 * <p>The main sample stream wrapper is the first element of {@link #sampleStreamWrappers}. It
 * provides {@link SampleStream}s for the variant urls in the multivariant playlist. It may be
 * adaptive and may contain multiple muxed tracks.
 *
 * <p>If chunkless preparation is allowed, the media period will try preparation without segment
 * downloads. This is only possible if variants contain the CODECS attribute. If not, traditional
 * preparation with segment downloads will take place. The following points apply to chunkless
 * preparation:
 *
 * <ul>
 *   <li>A muxed audio track will be exposed if the codecs list contain an audio entry and the
 *       multivariant playlist either contains an EXT-X-MEDIA tag without the URI attribute or
 *       does not contain any EXT-X-MEDIA tag.
 *   <li>Closed captions will only be exposed if they are declared by the multivariant playlist.
 *   <li>An ID3 track is exposed preemptively, in case the segments contain an ID3 track.
 * </ul>
 *
 * @param multivariantPlaylist The HLS multivariant playlist.
 * @param positionUs If preparation requires any chunk downloads, the position in microseconds at
 *     which downloading should start. Ignored otherwise.
 * @param sampleStreamWrappers List to which the built main sample stream wrapper should be added.
 * @param manifestUrlIndicesPerWrapper List to which the selected variant indices should be added.
 * @param overridingDrmInitData Overriding {@link DrmInitData}, keyed by protection scheme type
 *     (i.e. {@link DrmInitData#schemeType}).
 */
private void buildAndPrepareMainSampleStreamWrapper(HlsMultivariantPlaylist multivariantPlaylist, long positionUs, List<HlsSampleStreamWrapper> sampleStreamWrappers, List<int[]> manifestUrlIndicesPerWrapper, Map<String, DrmInitData> overridingDrmInitData) {
    int[] variantTypes = new int[multivariantPlaylist.variants.size()];
    int videoVariantCount = 0;
    int audioVariantCount = 0;
    for (int i = 0; i < multivariantPlaylist.variants.size(); i++) {
        Variant variant = multivariantPlaylist.variants.get(i);
        Format format = variant.format;
        if (format.height > 0 || Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_VIDEO) != null) {
            variantTypes[i] = C.TRACK_TYPE_VIDEO;
            videoVariantCount++;
        } else if (Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_AUDIO) != null) {
            variantTypes[i] = C.TRACK_TYPE_AUDIO;
            audioVariantCount++;
        } else {
            variantTypes[i] = C.TRACK_TYPE_UNKNOWN;
        }
    }
    boolean useVideoVariantsOnly = false;
    boolean useNonAudioVariantsOnly = false;
    int selectedVariantsCount = variantTypes.length;
    if (videoVariantCount > 0) {
        // We've identified some variants as definitely containing video. Assume variants within the
        // multivariant playlist are marked consistently, and hence that we have the full set. Filter
        // out any other variants, which are likely to be audio only.
        useVideoVariantsOnly = true;
        selectedVariantsCount = videoVariantCount;
    } else if (audioVariantCount < variantTypes.length) {
        // We've identified some variants, but not all, as being audio only. Filter them out to leave
        // the remaining variants, which are likely to contain video.
        useNonAudioVariantsOnly = true;
        selectedVariantsCount = variantTypes.length - audioVariantCount;
    }
    Uri[] selectedPlaylistUrls = new Uri[selectedVariantsCount];
    Format[] selectedPlaylistFormats = new Format[selectedVariantsCount];
    int[] selectedVariantIndices = new int[selectedVariantsCount];
    int outIndex = 0;
    for (int i = 0; i < multivariantPlaylist.variants.size(); i++) {
        if ((!useVideoVariantsOnly || variantTypes[i] == C.TRACK_TYPE_VIDEO) && (!useNonAudioVariantsOnly || variantTypes[i] != C.TRACK_TYPE_AUDIO)) {
            Variant variant = multivariantPlaylist.variants.get(i);
            selectedPlaylistUrls[outIndex] = variant.url;
            selectedPlaylistFormats[outIndex] = variant.format;
            selectedVariantIndices[outIndex++] = i;
        }
    }
    String codecs = selectedPlaylistFormats[0].codecs;
    int numberOfVideoCodecs = Util.getCodecCountOfType(codecs, C.TRACK_TYPE_VIDEO);
    int numberOfAudioCodecs = Util.getCodecCountOfType(codecs, C.TRACK_TYPE_AUDIO);
    boolean codecsStringAllowsChunklessPreparation = numberOfAudioCodecs <= 1 && numberOfVideoCodecs <= 1 && numberOfAudioCodecs + numberOfVideoCodecs > 0;
    @C.TrackType int trackType = !useVideoVariantsOnly && numberOfAudioCodecs > 0 ? C.TRACK_TYPE_AUDIO : C.TRACK_TYPE_DEFAULT;
    String sampleStreamWrapperUid = "main";
    HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(sampleStreamWrapperUid, trackType, selectedPlaylistUrls, selectedPlaylistFormats, multivariantPlaylist.muxedAudioFormat, multivariantPlaylist.muxedCaptionFormats, overridingDrmInitData, positionUs);
    sampleStreamWrappers.add(sampleStreamWrapper);
    manifestUrlIndicesPerWrapper.add(selectedVariantIndices);
    if (allowChunklessPreparation && codecsStringAllowsChunklessPreparation) {
        List<TrackGroup> muxedTrackGroups = new ArrayList<>();
        if (numberOfVideoCodecs > 0) {
            Format[] videoFormats = new Format[selectedVariantsCount];
            for (int i = 0; i < videoFormats.length; i++) {
                videoFormats[i] = deriveVideoFormat(selectedPlaylistFormats[i]);
            }
            muxedTrackGroups.add(new TrackGroup(sampleStreamWrapperUid, videoFormats));
            if (numberOfAudioCodecs > 0 && (multivariantPlaylist.muxedAudioFormat != null || multivariantPlaylist.audios.isEmpty())) {
                muxedTrackGroups.add(new TrackGroup(/* id= */
                sampleStreamWrapperUid + ":audio", deriveAudioFormat(selectedPlaylistFormats[0], multivariantPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
                false)));
            }
            List<Format> ccFormats = multivariantPlaylist.muxedCaptionFormats;
            if (ccFormats != null) {
                for (int i = 0; i < ccFormats.size(); i++) {
                    String ccId = sampleStreamWrapperUid + ":cc:" + i;
                    muxedTrackGroups.add(new TrackGroup(ccId, ccFormats.get(i)));
                }
            }
        } else /* numberOfAudioCodecs > 0 */
        {
            // Variants only contain audio.
            Format[] audioFormats = new Format[selectedVariantsCount];
            for (int i = 0; i < audioFormats.length; i++) {
                audioFormats[i] = deriveAudioFormat(/* variantFormat= */
                selectedPlaylistFormats[i], multivariantPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
                true);
            }
            muxedTrackGroups.add(new TrackGroup(sampleStreamWrapperUid, audioFormats));
        }
        TrackGroup id3TrackGroup = new TrackGroup(/* id= */
        sampleStreamWrapperUid + ":id3", new Format.Builder().setId("ID3").setSampleMimeType(MimeTypes.APPLICATION_ID3).build());
        muxedTrackGroups.add(id3TrackGroup);
        sampleStreamWrapper.prepareWithMultivariantPlaylistInfo(muxedTrackGroups.toArray(new TrackGroup[0]), /* primaryTrackGroupIndex= */
        0, /* optionalTrackGroupsIndices...= */
        muxedTrackGroups.indexOf(id3TrackGroup));
    }
}
Also used : ArrayList(java.util.ArrayList) Uri(android.net.Uri) Variant(com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant) Format(com.google.android.exoplayer2.Format) TrackGroup(com.google.android.exoplayer2.source.TrackGroup)

Example 18 with Entry

use of com.google.android.exoplayer2.metadata.Metadata.Entry in project ExoPlayer by google.

the class MappingTrackSelector method selectTracks.

@Override
public final TrackSelectorResult selectTracks(RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups, MediaPeriodId periodId, Timeline timeline) throws ExoPlaybackException {
    // Structures into which data will be written during the selection. The extra item at the end
    // of each array is to store data associated with track groups that cannot be associated with
    // any renderer.
    int[] rendererTrackGroupCounts = new int[rendererCapabilities.length + 1];
    TrackGroup[][] rendererTrackGroups = new TrackGroup[rendererCapabilities.length + 1][];
    @Capabilities int[][][] rendererFormatSupports = new int[rendererCapabilities.length + 1][][];
    for (int i = 0; i < rendererTrackGroups.length; i++) {
        rendererTrackGroups[i] = new TrackGroup[trackGroups.length];
        rendererFormatSupports[i] = new int[trackGroups.length][];
    }
    // Determine the extent to which each renderer supports mixed mimeType adaptation.
    @AdaptiveSupport int[] rendererMixedMimeTypeAdaptationSupports = getMixedMimeTypeAdaptationSupports(rendererCapabilities);
    // renderer provides for each track in the group.
    for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
        TrackGroup group = trackGroups.get(groupIndex);
        // Associate the group to a preferred renderer.
        boolean preferUnassociatedRenderer = MimeTypes.getTrackType(group.getFormat(0).sampleMimeType) == C.TRACK_TYPE_METADATA;
        int rendererIndex = findRenderer(rendererCapabilities, group, rendererTrackGroupCounts, preferUnassociatedRenderer);
        // Evaluate the support that the renderer provides for each track in the group.
        @Capabilities int[] rendererFormatSupport = rendererIndex == rendererCapabilities.length ? new int[group.length] : getFormatSupport(rendererCapabilities[rendererIndex], group);
        // Stash the results.
        int rendererTrackGroupCount = rendererTrackGroupCounts[rendererIndex];
        rendererTrackGroups[rendererIndex][rendererTrackGroupCount] = group;
        rendererFormatSupports[rendererIndex][rendererTrackGroupCount] = rendererFormatSupport;
        rendererTrackGroupCounts[rendererIndex]++;
    }
    // Create a track group array for each renderer, and trim each rendererFormatSupports entry.
    TrackGroupArray[] rendererTrackGroupArrays = new TrackGroupArray[rendererCapabilities.length];
    String[] rendererNames = new String[rendererCapabilities.length];
    int[] rendererTrackTypes = new int[rendererCapabilities.length];
    for (int i = 0; i < rendererCapabilities.length; i++) {
        int rendererTrackGroupCount = rendererTrackGroupCounts[i];
        rendererTrackGroupArrays[i] = new TrackGroupArray(Util.nullSafeArrayCopy(rendererTrackGroups[i], rendererTrackGroupCount));
        rendererFormatSupports[i] = Util.nullSafeArrayCopy(rendererFormatSupports[i], rendererTrackGroupCount);
        rendererNames[i] = rendererCapabilities[i].getName();
        rendererTrackTypes[i] = rendererCapabilities[i].getTrackType();
    }
    // Create a track group array for track groups not mapped to a renderer.
    int unmappedTrackGroupCount = rendererTrackGroupCounts[rendererCapabilities.length];
    TrackGroupArray unmappedTrackGroupArray = new TrackGroupArray(Util.nullSafeArrayCopy(rendererTrackGroups[rendererCapabilities.length], unmappedTrackGroupCount));
    // Package up the track information and selections.
    MappedTrackInfo mappedTrackInfo = new MappedTrackInfo(rendererNames, rendererTrackTypes, rendererTrackGroupArrays, rendererMixedMimeTypeAdaptationSupports, rendererFormatSupports, unmappedTrackGroupArray);
    Pair<@NullableType RendererConfiguration[], @NullableType ExoTrackSelection[]> result = selectTracks(mappedTrackInfo, rendererFormatSupports, rendererMixedMimeTypeAdaptationSupports, periodId, timeline);
    TracksInfo tracksInfo = buildTracksInfo(result.second, mappedTrackInfo);
    return new TrackSelectorResult(result.first, result.second, tracksInfo, mappedTrackInfo);
}
Also used : TrackGroupArray(com.google.android.exoplayer2.source.TrackGroupArray) NullableType(org.checkerframework.checker.nullness.compatqual.NullableType) TracksInfo(com.google.android.exoplayer2.TracksInfo) TrackGroup(com.google.android.exoplayer2.source.TrackGroup) RendererCapabilities(com.google.android.exoplayer2.RendererCapabilities) Capabilities(com.google.android.exoplayer2.RendererCapabilities.Capabilities) AdaptiveSupport(com.google.android.exoplayer2.RendererCapabilities.AdaptiveSupport)

Example 19 with Entry

use of com.google.android.exoplayer2.metadata.Metadata.Entry in project ExoPlayer by google.

the class PictureFrameTest method populateMediaMetadata_setsBuilderValues.

@Test
public void populateMediaMetadata_setsBuilderValues() {
    byte[] pictureData = new byte[] { -12, 52, 33, 85, 34, 22, 1, -55 };
    Metadata.Entry entry = new PictureFrame(/* pictureType= */
    MediaMetadata.PICTURE_TYPE_FRONT_COVER, /* mimeType= */
    MimeTypes.IMAGE_JPEG, /* description= */
    "an image", /* width= */
    4, /* height= */
    2, /* depth= */
    1, /* colors= */
    1, pictureData);
    MediaMetadata.Builder builder = MediaMetadata.EMPTY.buildUpon();
    entry.populateMediaMetadata(builder);
    MediaMetadata mediaMetadata = builder.build();
    assertThat(mediaMetadata.artworkData).isEqualTo(pictureData);
    assertThat(mediaMetadata.artworkDataType).isEqualTo(MediaMetadata.PICTURE_TYPE_FRONT_COVER);
}
Also used : MediaMetadata(com.google.android.exoplayer2.MediaMetadata) Metadata(com.google.android.exoplayer2.metadata.Metadata) MediaMetadata(com.google.android.exoplayer2.MediaMetadata) Test(org.junit.Test)

Example 20 with Entry

use of com.google.android.exoplayer2.metadata.Metadata.Entry in project ExoPlayer by google.

the class TtmlNode method getCues.

public List<Cue> getCues(long timeUs, Map<String, TtmlStyle> globalStyles, Map<String, TtmlRegion> regionMap, Map<String, String> imageMap) {
    List<Pair<String, String>> regionImageOutputs = new ArrayList<>();
    traverseForImage(timeUs, regionId, regionImageOutputs);
    TreeMap<String, Cue.Builder> regionTextOutputs = new TreeMap<>();
    traverseForText(timeUs, false, regionId, regionTextOutputs);
    traverseForStyle(timeUs, globalStyles, regionMap, regionId, regionTextOutputs);
    List<Cue> cues = new ArrayList<>();
    // Create image based cues.
    for (Pair<String, String> regionImagePair : regionImageOutputs) {
        @Nullable String encodedBitmapData = imageMap.get(regionImagePair.second);
        if (encodedBitmapData == null) {
            // Image reference points to an invalid image. Do nothing.
            continue;
        }
        byte[] bitmapData = Base64.decode(encodedBitmapData, Base64.DEFAULT);
        Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, /* offset= */
        0, bitmapData.length);
        TtmlRegion region = Assertions.checkNotNull(regionMap.get(regionImagePair.first));
        cues.add(new Cue.Builder().setBitmap(bitmap).setPosition(region.position).setPositionAnchor(Cue.ANCHOR_TYPE_START).setLine(region.line, Cue.LINE_TYPE_FRACTION).setLineAnchor(region.lineAnchor).setSize(region.width).setBitmapHeight(region.height).setVerticalType(region.verticalType).build());
    }
    // Create text based cues.
    for (Map.Entry<String, Cue.Builder> entry : regionTextOutputs.entrySet()) {
        TtmlRegion region = Assertions.checkNotNull(regionMap.get(entry.getKey()));
        Cue.Builder regionOutput = entry.getValue();
        cleanUpText((SpannableStringBuilder) Assertions.checkNotNull(regionOutput.getText()));
        regionOutput.setLine(region.line, region.lineType);
        regionOutput.setLineAnchor(region.lineAnchor);
        regionOutput.setPosition(region.position);
        regionOutput.setSize(region.width);
        regionOutput.setTextSize(region.textSize, region.textSizeType);
        regionOutput.setVerticalType(region.verticalType);
        cues.add(regionOutput.build());
    }
    return cues;
}
Also used : SpannableStringBuilder(android.text.SpannableStringBuilder) ArrayList(java.util.ArrayList) TreeMap(java.util.TreeMap) Cue(com.google.android.exoplayer2.text.Cue) Bitmap(android.graphics.Bitmap) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap) Map(java.util.Map) Nullable(androidx.annotation.Nullable) Pair(android.util.Pair)

Aggregations

Metadata (com.google.android.exoplayer2.metadata.Metadata)12 Nullable (androidx.annotation.Nullable)9 ArrayList (java.util.ArrayList)8 MediaMetadata (com.google.android.exoplayer2.MediaMetadata)4 Test (org.junit.Test)4 SmtaMetadataEntry (com.google.android.exoplayer2.metadata.mp4.SmtaMetadataEntry)3 TrackGroup (com.google.android.exoplayer2.source.TrackGroup)3 ParsableByteArray (com.google.android.exoplayer2.util.ParsableByteArray)3 SpannableStringBuilder (android.text.SpannableStringBuilder)2 Format (com.google.android.exoplayer2.Format)2 RendererConfiguration (com.google.android.exoplayer2.RendererConfiguration)2 Id3Frame (com.google.android.exoplayer2.metadata.id3.Id3Frame)2 SlowMotionData (com.google.android.exoplayer2.metadata.mp4.SlowMotionData)2 TrackGroupArray (com.google.android.exoplayer2.source.TrackGroupArray)2 Cue (com.google.android.exoplayer2.text.Cue)2 TreeMap (java.util.TreeMap)2 Bitmap (android.graphics.Bitmap)1 Uri (android.net.Uri)1 Pair (android.util.Pair)1 ExoPlayerVideoDisplayComponent (com.brightcove.player.display.ExoPlayerVideoDisplayComponent)1