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

时间:2012-10-16 17:41:32

标签: android android-mediaplayer

my recent question on MediaRecorder and createPipe()相关,以及this other SO question中对createPipe()技术的讨论,我现在正试图让MediaPlayer使用{{1}提供的内容通过ContentProviderParcelFileDescriptor

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

我已将createPipe()设置更改为:

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已正确构建。

Urithis sample project中的相同,它适用于向Adobe Reader提供PDF,这限制了我的代码被搞砸了。 : - )

具体而言,PipeProvideropenFile()创建管道:

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()提供媒体的工作代码?

提前致谢!

4 个答案:

答案 0 :(得分:10)

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

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来解决它的无数失败。

答案 1 :(得分:6)

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

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

答案 2 :(得分: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&gt; = sb.st_size),则偏移次要值-1(内容的假定大小)或正声明的大小不会触发错误。

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

答案 3 :(得分: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);
相关问题