首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >有谁让MediaPlayer使用ParcelFileDescriptor和createPipe()?

有谁让MediaPlayer使用ParcelFileDescriptor和createPipe()?
EN

Stack Overflow用户
提问于 2012-10-17 01:41:32
回答 4查看 6.9K关注 0票数 19

my recent question on MediaRecorder and createPipe()相关,并讨论了this other SO question中的createPipe()技术,我现在正在尝试让MediaPlayer通过ParcelFileDescriptorcreatePipe()处理由ContentProvider提供的内容。

到目前为止,This sample project有我的作品。它是基于an earlier sample that plays an OGG clip stored as a raw resource的。因此,我知道我的剪辑是好的。

我已将我的MediaPlayer设置更改为:

代码语言:javascript
复制
  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);
    }
  }

通过登录PipeProvider,我看到我的Uri正在被正确构造。

PipeProviderthis sample project是一样的,它用于向Adobe Reader提供PDF,这限制了我的代码可以搞砸的程度。:-)

具体地说,openFile()ParcelFileDescriptor创建管道

代码语言:javascript
复制
  @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]);
  }

其中后台线程执行典型的流到流复制:

代码语言:javascript
复制
  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阻塞了:

代码语言:javascript
复制
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提供媒体服务的工作代码

提前感谢!

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-10-17 22:47:01

我不确定这是否能行得通。当我运行这段代码时,我看到了这样的跟踪:

代码语言:javascript
复制
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中的以下几行,其中它在管道的读取端执行一个fstat():

代码语言:javascript
复制
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 (通过Java级别的ParcelFileDescriptor上的getStatSize() )。错误处理程序关闭描述符,因此不久之后就会出现管道损坏错误。

在我的经验中,MediaPlayer有很多像这样的碎片。除了直接在本地文件上工作之外,我从来没有见过它对任何事情都有效,并且(非常错误地)用于HTTP流。我最终移植了FFmpeg,以绕过它的许多缺点。

票数 12
EN

Stack Overflow用户

发布于 2012-10-17 21:29:32

我尝试过通过使用PipeDataWriter的ContentProvider在MediaPlayer中使用管道(基本上是使用管道和线程)。

问题是,MediaPlayer期望的文件描述符必须是可查找的,并且您不能在管道上执行fseek。

票数 7
EN

Stack Overflow用户

发布于 2013-12-13 02:22:40

从理论上讲,ContentProvider上的openAssetFile()可以被覆盖。可以使用声明的大小和偏移量返回AssetFileDescriptor

代码语言:javascript
复制
@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),则偏移量minor then -1 (内容的假定大小)或正声明大小不会触发错误。

这应该是一个很好的起点,但我在测试中运气不好。愚蠢的MediaPlayer似乎在播放之前读取了整个“文件”,导致前面的管道破裂。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12920429

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档