20

我最近关于MediaRecorderand的问题有关,以及在createPipe()另一个 SO question中对createPipe()技术的讨论,我现在正在尝试使用通过and提供的内容。MediaPlayerContentProviderParcelFileDescriptorcreatePipe()

这个示例项目有我迄今为止的工作。它基于播放存储为原始资源的 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);
    }
  }

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

PipeProvider与此示例项目中的相同,它适用于将 PDF 提供给 Adob​​e Reader,这限制了我的代码可能会搞砸的程度。:-)

具体来说,openFile()创建一个管道ParcelFileDescriptor

  @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]);
  }

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

  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窒息:

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

提前致谢!

4

4 回答 4

12

我不确定这是否可行。当我运行此代码时,我看到此跟踪:

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():

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 来解决它的众多缺陷。

于 2012-10-17T14:47:01.093 回答
8

我尝试通过使用 PipeDataWriter 的 ContentProvider 将管道与 MediaPlayer 一起使用(基本上使用管道和线程)。

问题是 MediaPlayer 期望的文件描述符,至少对于视频内容,必须是可搜索的,并且您不能在管道上执行 fseek。

于 2012-10-17T13:29:32.067 回答
0

在论文中,openAssetFile()ContentProvider可以被覆盖。可以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;

}

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

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

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

于 2013-12-12T18:22:39.773 回答
0
From Api level 23 onwards, you can use MediaDataSource class.


import java.io.*;
import android.media.MediaDataSource;

public class MyAudioSource extends MediaDataSource {
    private final byte[] buf;
    private final ByteArrayInputStream is;

    public MyAudioSource(byte[] buf){
        super();
        this.buf=buf;
        is=new ByteArrayInputStream(buf);
    }

    public long getSize() {
        return buf.length;
    }

    public int readAt(long position, byte[] buffer, int offset, int size){
        is.reset();
        is.skip(position);
        return is.read(buffer,offset,size);
    }
}



Now use above class for MediaPlayer like:

    // some how get your audio buffer in buf
    MyAudioSource mas = new MyAudioSource(buf);
    mediaPlayer.setDataSource(mas);
于 2018-10-09T11:49:33.073 回答