任何人有MediaPlayer使用ParcelFileDescriptor和createPipe()?

关于我最近在MediaRecordercreatePipe() ,以及在其他SO问题中对createPipe()技术的讨论 ,我现在试图让MediaPlayer通过ParcelFileDescriptorcreatePipe()来处理由ContentProvider提供的内容。

这个示例项目迄今为止我的工作。 它基于一个早期的样本,播放存储为原始资源的OGG剪辑 。 因此,我知道我的剪辑是好的。

我已将MediaPlayer设置更改为:

  private void loadClip() { try { mp=new MediaPlayer(); mp.setDataSource(this, PipeProvider.CONTENT_URI.buildUpon() .appendPath("clip.ogg") .build()); mp.setOnCompletionListener(this); mp.prepare(); } catch (Exception e) { goBlooey(e); } } 

通过loginPipeProvider ,我看到我的Uri正在构build。

PipeProvider和这个示例项目是一样的, 这个示例项目适用于将PDF提供给Adobe Reader,这限制了我的代码可能会如何搞砸。 🙂

具体来说, openFile()ParcelFileDescriptor创build一个pipe道:

  @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { ParcelFileDescriptor[] pipe=null; try { pipe=ParcelFileDescriptor.createPipe(); AssetManager assets=getContext().getResources().getAssets(); new TransferTask(assets.open(uri.getLastPathSegment()), new AutoCloseOutputStream(pipe[1])).start(); } catch (IOException e) { Log.e(getClass().getSimpleName(), "Exception opening pipe", e); throw new FileNotFoundException("Could not open pipe for: " + uri.toString()); } return(pipe[0]); } 

后台线程执行典型的stream到stream复制:

  static class TransferTask extends Thread { InputStream in; OutputStream out; TransferTask(InputStream in, OutputStream out) { this.in=in; this.out=out; } @Override public void run() { byte[] buf=new byte[1024]; int len; try { while ((len=in.read(buf)) > 0) { out.write(buf, 0, len); } in.close(); out.close(); } catch (IOException e) { Log.e(getClass().getSimpleName(), "Exception transferring file", e); } } } 

但是, MediaPlayer扼stream圈:

 10-16 13:33:13.203: E/MediaPlayer(3060): Unable to to create media player 10-16 13:33:13.203: D/MediaPlayer(3060): Couldn't open file on client side, trying server side 10-16 13:33:13.207: E/TransferTask(3060): Exception transferring file 10-16 13:33:13.207: E/TransferTask(3060): java.io.IOException: write failed: EPIPE (Broken pipe) 10-16 13:33:13.207: E/TransferTask(3060): at libcore.io.IoBridge.write(IoBridge.java:462) 10-16 13:33:13.207: E/TransferTask(3060): at java.io.FileOutputStream.write(FileOutputStream.java:187) 10-16 13:33:13.207: E/TransferTask(3060): at com.commonsware.android.audiolstream.PipeProvider$TransferTask.run(PipeProvider.java:120) 10-16 13:33:13.207: E/TransferTask(3060): Caused by: libcore.io.ErrnoException: write failed: EPIPE (Broken pipe) 10-16 13:33:13.207: E/TransferTask(3060): at libcore.io.Posix.writeBytes(Native Method) 10-16 13:33:13.207: E/TransferTask(3060): at libcore.io.Posix.write(Posix.java:178) 10-16 13:33:13.207: E/TransferTask(3060): at libcore.io.BlockGuardOs.write(BlockGuardOs.java:191) 10-16 13:33:13.207: E/TransferTask(3060): at libcore.io.IoBridge.write(IoBridge.java:457) 10-16 13:33:13.207: E/TransferTask(3060): ... 2 more 10-16 13:33:13.211: E/MediaPlayer(3060): Unable to to create media player 10-16 13:33:13.218: E/TransferTask(3060): Exception transferring file 10-16 13:33:13.218: E/TransferTask(3060): java.io.IOException: write failed: EPIPE (Broken pipe) 10-16 13:33:13.218: E/TransferTask(3060): at libcore.io.IoBridge.write(IoBridge.java:462) 10-16 13:33:13.218: E/TransferTask(3060): at java.io.FileOutputStream.write(FileOutputStream.java:187) 10-16 13:33:13.218: E/TransferTask(3060): at com.commonsware.android.audiolstream.PipeProvider$TransferTask.run(PipeProvider.java:120) 10-16 13:33:13.218: E/TransferTask(3060): Caused by: libcore.io.ErrnoException: write failed: EPIPE (Broken pipe) 10-16 13:33:13.218: E/TransferTask(3060): at libcore.io.Posix.writeBytes(Native Method) 10-16 13:33:13.218: E/TransferTask(3060): at libcore.io.Posix.write(Posix.java:178) 10-16 13:33:13.218: E/TransferTask(3060): at libcore.io.BlockGuardOs.write(BlockGuardOs.java:191) 10-16 13:33:13.218: E/TransferTask(3060): at libcore.io.IoBridge.write(IoBridge.java:457) 10-16 13:33:13.218: E/TransferTask(3060): ... 2 more 

有没有人看到使用createPipe()MediaPlayer提供媒体的工作代码?

提前致谢!

Solutions Collecting From Web of "任何人有MediaPlayer使用ParcelFileDescriptor和createPipe()?"

我不确定这可以工作。 当我运行这个代码时,我看到这个痕迹:

 I/AudioSystem(30916): getting audio flinger I/AudioSystem(30916): returning new audio session id D/IAudioFlinger(30916): newAudioSessionId In D/AudioFlinger(28138): nextUniqueId, current 178 D/IAudioFlinger(30916): newAudioSessionId Out, id = 178 D/MediaPlayer(30916): setDataSource(Context context, content://com.commonsware.android.audiolstream/clip.ogg, Map<String, String> headers) in D/MediaPlayer(30916): setDataSource(FileDescriptor fd) in E/MediaPlayerService(28138): offset error 

这个“偏移错误”来自AOSP中的MediaPlayerService.cpp中的以下行,它在pipe道的读取侧执行fstat():

 status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length) { struct stat sb; int ret = fstat(fd, &sb); .... if (offset >= sb.st_size) { LOGE("offset error"); ::close(fd); return UNKNOWN_ERROR; } 

并且sb.st_size被报告为-1(通过ParcelFileDescriptor上的Java级别的getStatSize())。 error handling程序将closures描述符,因此之后不久就会出现断pipe错误。

根据我的经验,MediaPlayer有许多像这样的坏点。 我从来没有见过它的工作,但直接在本地文件,并(非常buggily)的HTTPstream。 我结束了移植FFmpeg来解决其众多的失败。

我试图通过使用PipeDataWriter(基本上使用pipe道和线程)的ContentProvider使用MediaPlayer的pipe道。

问题在于,MediaPlayer所需的文件描述符(至less对于video内容而言)必须是可search的,并且您不能在pipe道上执行fseek。

在论文中,您的ContentProvider上的openAssetFile()可以被重写。 AssetFileDescriptor可以返回声明的大小和偏移量。

 @Override public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { ParcelFileDescriptor fd = openFile(uri, mode); return fd != null ? new AssetFileDescriptor(fd, offset, size) : null; } 

此值传递给MediaPlayer上的本机setDataSource() (请查看MediaPlayer.java以了解更多信息)。

如果MediaPlayerService.cpp中的错误检查是(offset> = sb.st_size),则偏移量小于-1(内容的假定大小)或正确声明的大小不会触发错误。

这应该是一个干净的黑客很好的起点,但我有我的testing运气不好。 哑巴的MediaPlayer似乎在播放之前读取了整个“文件”,导致前方的pipe道破损。