与my recent question on MediaRecorder
and createPipe()
相关,以及this other SO question中对createPipe()
技术的讨论,我现在正试图让MediaPlayer
使用{{1}提供的内容通过ContentProvider
和ParcelFileDescriptor
。
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
已正确构建。
Uri
与this sample project中的相同,它适用于向Adobe Reader提供PDF,这限制了我的代码被搞砸了。 : - )
具体而言,PipeProvider
从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()
提供媒体的工作代码?
提前致谢!
答案 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);