All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowMediaExtractor Maven / Gradle / Ivy

The newest version!
package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Build.VERSION_CODES.O;
import static java.lang.Math.min;
import static org.robolectric.shadows.util.DataSource.toDataSource;

import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.MediaDataSource;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
import android.os.PersistableBundle;
import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.util.DataSource;

/**
 * A shadow for the MediaExtractor class.
 *
 * 

Returns data previously injected by {@link #addTrack(DataSource, MediaFormat, byte[])}. * *

Note several limitations, due to not using actual media codecs for decoding: * *

    *
  • Only one track may be selected at a time; multi-track selection is not supported. *
  • {@link #advance()} will advance by the size of the last read (i.e. the return value of the * last call to {@link #readSampleData(ByteBuffer, int)}). *
  • {@link MediaExtractor#getSampleTime()} and {@link MediaExtractor#getSampleSize()} are * unimplemented. *
  • {@link MediaExtractor#seekTo()} is unimplemented. *
*/ @Implements(MediaExtractor.class) public class ShadowMediaExtractor { private static class TrackInfo { MediaFormat format; byte[] sampleData; } private static final Map> tracksMap = new HashMap<>(); private static final Map metricsMap = new HashMap<>(); private List tracks; private PersistableBundle metrics; private int[] trackSampleReadPositions; private int[] trackLastReadSize; private int selectedTrackIndex = -1; /** * Adds a track of data to an associated {@link org.robolectric.shadows.util.DataSource}. * * @param format the format which will be returned by {@link MediaExtractor#getTrackFormat(int)} * @param sampleData the data which will be iterated upon and returned by {@link * MediaExtractor#readSampleData(ByteBuffer, int)}. */ public static void addTrack(DataSource dataSource, MediaFormat format, byte[] sampleData) { TrackInfo trackInfo = new TrackInfo(); trackInfo.format = format; trackInfo.sampleData = sampleData; tracksMap.putIfAbsent(dataSource, new ArrayList()); List tracks = tracksMap.get(dataSource); tracks.add(trackInfo); } /** * Sets metrics for an associated {@link org.robolectric.shadows.util.DataSource}. * * @param metrics the data which will be returned by {@link MediaExtractor#getMetrics()}. */ public static void setMetrics(DataSource dataSource, PersistableBundle metrics) { metricsMap.put(dataSource, metrics); } private void setDataSource(DataSource dataSource) { if (tracksMap.containsKey(dataSource)) { this.tracks = tracksMap.get(dataSource); } else { this.tracks = new ArrayList<>(); } this.trackSampleReadPositions = new int[tracks.size()]; Arrays.fill(trackSampleReadPositions, 0); this.trackLastReadSize = new int[tracks.size()]; Arrays.fill(trackLastReadSize, 0); this.metrics = metricsMap.get(dataSource); } @Implementation(minSdk = N) protected void setDataSource(AssetFileDescriptor assetFileDescriptor) { setDataSource(toDataSource(assetFileDescriptor)); } @Implementation protected void setDataSource(Context context, Uri uri, Map headers) { setDataSource(toDataSource(context, uri, headers)); } @Implementation protected void setDataSource(FileDescriptor fileDescriptor) { setDataSource(toDataSource(fileDescriptor)); } @Implementation(minSdk = M) protected void setDataSource(MediaDataSource mediaDataSource) { setDataSource(toDataSource(mediaDataSource)); } @Implementation protected void setDataSource(FileDescriptor fileDescriptor, long offset, long length) { setDataSource(toDataSource(fileDescriptor, offset, length)); } @Implementation protected void setDataSource(String path) { setDataSource(toDataSource(path)); } @Implementation protected void setDataSource(String path, Map headers) { setDataSource(toDataSource(path)); } @Implementation protected boolean advance() { if (selectedTrackIndex == -1) { throw new IllegalStateException("Called advance() with no selected track"); } int readPosition = trackSampleReadPositions[selectedTrackIndex]; int trackDataLength = tracks.get(selectedTrackIndex).sampleData.length; if (readPosition >= trackDataLength) { return false; } trackSampleReadPositions[selectedTrackIndex] += trackLastReadSize[selectedTrackIndex]; return true; } @Implementation protected int getSampleTrackIndex() { return selectedTrackIndex; } @Implementation protected int getTrackCount() { return tracks.size(); } @Implementation protected MediaFormat getTrackFormat(int index) { if (index >= tracks.size()) { throw new ArrayIndexOutOfBoundsException( "Called getTrackFormat() with index:" + index + ", beyond number of tracks:" + tracks.size()); } return tracks.get(index).format; } @Implementation protected int readSampleData(ByteBuffer byteBuf, int offset) { if (selectedTrackIndex == -1) { return 0; } int currentReadPosition = trackSampleReadPositions[selectedTrackIndex]; TrackInfo trackInfo = tracks.get(selectedTrackIndex); int trackDataLength = trackInfo.sampleData.length; if (currentReadPosition >= trackDataLength) { return -1; } int length = min(byteBuf.capacity(), trackDataLength - currentReadPosition); byteBuf.put(trackInfo.sampleData, currentReadPosition, length); trackLastReadSize[selectedTrackIndex] = length; return length; } @Implementation protected void selectTrack(int index) { if (selectedTrackIndex != -1) { throw new IllegalStateException( "Called selectTrack() when there is already a track selected; call unselectTrack() first." + " ShadowMediaExtractor does not support multiple track selection."); } if (index >= tracks.size()) { throw new ArrayIndexOutOfBoundsException( "Called selectTrack() with index:" + index + ", beyond number of tracks:" + tracks.size()); } selectedTrackIndex = index; } @Implementation protected void unselectTrack(int index) { if (selectedTrackIndex != index) { throw new IllegalStateException( "Called unselectTrack() on a track other than the single selected track." + " ShadowMediaExtractor does not support multiple track selection."); } selectedTrackIndex = -1; } @Implementation(minSdk = O) protected PersistableBundle getMetrics() { return metrics; } @Resetter public static void reset() { tracksMap.clear(); metricsMap.clear(); DataSource.reset(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy