MediaExtractor.setDataSource works or doesn't based on Android OS version
-
It's been a while since I've participated on CP, and I'm back with a doozy I'm having a problem with MediaExtractor and using a MemoryFile as the data source. In the real world, this media viewer is part of a larger app that supplies a chunk of bytes that contains a complete MP4 video. To eliminate all the other stuff, I have created a little demo app that only contains the viewer and a test MemoryFile. Please note that not using a MemoryFile, i.e. saving the video data to storage and playing from there, is not an option. First, the code. I create the MemoryFile in the test app's MainActivity.onCreate
// these lines are only in the test app
InputStream inS = getResources ().getAssets ().open ("testmp4.mp4");int length = inS.available ();
byte[] data = new byte[length];
inS.read (data);
inS.close ();// in the real world, the other stuff gives me a byte[] array and my code starts here
m_memFile = new MemoryFile (null, length);
m_memFile.writeBytes (data, 0, 0, length);Method getFD = MemoryFile.class.getMethod ("getFileDescriptor");
m_memFileFD = (FileDescriptor) getFD.invoke (m_memFile);This is in the MainActivity's Play button click listener:
if (m_player == null)
{
m_player = new MyPlayer (MainActivity.this.m_events, MainActivity.this.m_memFileFD, MainActivity.this.m_memFile.length (), (SurfaceView) findViewById (R.id.viewerMediaVideo));
}
m_player.play ();The video player is in the class MyPlayer that extends Runnable:
protected MediaExtractor m_extractor = null;
protected PlayerStates m_state = PlayerStates.STOPPED;
private SurfaceHolder m_surface = null;
protected FileDescriptor m_memFileFD = null;
protected long m_memFileLen = 0;public MyPlayer (PlayerEvents ev, FileDescriptor fd, long len, SurfaceView s)
{
m_events = ev;
m_memFileFD = fd;
m_memFileLen = len;
m_surface = s.getHolder ();
}public void play ()
{
if (m_state == PlayerStates.STOPPED)
{
m_stop = false;
new Thread (this).start ();
}
...
}public void run ()
{
android.os.Process.setThreadPriority (Process.THREAD_PRIORITY_URGENT_AUDIO);m\_extractor = new MediaExtractor (); try { m\_extractor.setDataSource (m\_memFileFD, 0, m\_memFileLen); } catch (final Exception excp) { m\_extractor.release (); m\_extractor = null; m\_handler.post (new Runnable (){public void run () {m\_events.onError ("
-
It's been a while since I've participated on CP, and I'm back with a doozy I'm having a problem with MediaExtractor and using a MemoryFile as the data source. In the real world, this media viewer is part of a larger app that supplies a chunk of bytes that contains a complete MP4 video. To eliminate all the other stuff, I have created a little demo app that only contains the viewer and a test MemoryFile. Please note that not using a MemoryFile, i.e. saving the video data to storage and playing from there, is not an option. First, the code. I create the MemoryFile in the test app's MainActivity.onCreate
// these lines are only in the test app
InputStream inS = getResources ().getAssets ().open ("testmp4.mp4");int length = inS.available ();
byte[] data = new byte[length];
inS.read (data);
inS.close ();// in the real world, the other stuff gives me a byte[] array and my code starts here
m_memFile = new MemoryFile (null, length);
m_memFile.writeBytes (data, 0, 0, length);Method getFD = MemoryFile.class.getMethod ("getFileDescriptor");
m_memFileFD = (FileDescriptor) getFD.invoke (m_memFile);This is in the MainActivity's Play button click listener:
if (m_player == null)
{
m_player = new MyPlayer (MainActivity.this.m_events, MainActivity.this.m_memFileFD, MainActivity.this.m_memFile.length (), (SurfaceView) findViewById (R.id.viewerMediaVideo));
}
m_player.play ();The video player is in the class MyPlayer that extends Runnable:
protected MediaExtractor m_extractor = null;
protected PlayerStates m_state = PlayerStates.STOPPED;
private SurfaceHolder m_surface = null;
protected FileDescriptor m_memFileFD = null;
protected long m_memFileLen = 0;public MyPlayer (PlayerEvents ev, FileDescriptor fd, long len, SurfaceView s)
{
m_events = ev;
m_memFileFD = fd;
m_memFileLen = len;
m_surface = s.getHolder ();
}public void play ()
{
if (m_state == PlayerStates.STOPPED)
{
m_stop = false;
new Thread (this).start ();
}
...
}public void run ()
{
android.os.Process.setThreadPriority (Process.THREAD_PRIORITY_URGENT_AUDIO);m\_extractor = new MediaExtractor (); try { m\_extractor.setDataSource (m\_memFileFD, 0, m\_memFileLen); } catch (final Exception excp) { m\_extractor.release (); m\_extractor = null; m\_handler.post (new Runnable (){public void run () {m\_events.onError ("
Just a followup ... I never did get around to harassing Google about this. I simply worked around the problem by checking the build version and only using the "old" way with a MemoryFile for KitKat and Lollipop and using the "new with API 23" MediaDataSource with the data behind the MemoryFile for Marshmallow and up.
Be wary of strong drink. It can make you shoot at tax collectors - and miss. Lazarus Long, "Time Enough For Love" by Robert A. Heinlein