studio sound play mediaplayer example ejemplo android android-mediaplayer

android - sound - ¿Alguien tiene MediaPlayer trabajando con ParcelFileDescriptor y createPipe()?



play sound android (4)

En relación con mi pregunta reciente en MediaRecorder y createPipe() , y una discusión sobre la técnica createPipe() en esta otra pregunta de SO , ahora estoy tratando de que MediaPlayer funcione con contenido servido por un ContentProvider través de ParcelFileDescriptor y createPipe() .

Este proyecto de muestra tiene mi trabajo hasta la fecha. Se basa en una muestra anterior que reproduce un clip OGG almacenado como un recurso en bruto . Por lo tanto, sé que mi clip está bien.

He cambiado mi configuración de MediaPlayer a:

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

Al iniciar sesión en PipeProvider , veo que mi Uri se está construyendo correctamente.

PipeProvider es el mismo que en este proyecto de muestra , que funciona para entregar archivos PDF a Adobe Reader, lo que limita lo difícil que puede ser mi código. :-)

Específicamente, openFile() crea un conducto desde 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]); }

donde el subproceso de fondo realiza una copia de secuencia a secuencia típica:

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

Sin embargo, MediaPlayer ahoga:

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

¿Alguien ha visto el código de trabajo para usar createPipe() para servir medios a MediaPlayer ?

¡Gracias por adelantado!


En tesis, se puede anular openAssetFile() en su ContentProvider . Un AssetFileDescriptor se puede devolver con un tamaño y un desplazamiento declarados.

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

Estos valores se pasan a setDataSource() nativo en MediaPlayer (consulte el MediaPlayer.java para obtener más información).

Si la verificación de errores en MediaPlayerService.cpp es (offset> = sb.st_size), un offset menor entonces -1 (el tamaño presumido del contenido) o un tamaño declarado positivo no genera el error.

Esto debería ser un buen punto de partida para un hackeo limpio, pero tengo mala suerte en mis pruebas. El tonto MediaPlayer parece leer todo el "archivo" antes de jugar, causando una tubería rota por delante.


He intentado usar tuberías con MediaPlayer a través de un ContentProvider usando PipeDataWriter (que básicamente usa una tubería y un hilo).

El problema es que el descriptor de archivo esperado por el MediaPlayer, al menos para el contenido de video, debe poder buscarse, y no se puede hacer un fseek en una tubería.


No estoy seguro de que esto pueda funcionar. Cuando ejecuto este código veo este rastro:

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

Ese "error de desplazamiento" proviene de las siguientes líneas en MediaPlayerService.cpp en AOSP, donde hace un fstat () en el lado de lectura de la tubería:

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; }

Y sb.st_size se informa como -1 (a través de getStatSize () en ParcelFileDescriptor en el nivel de Java). El controlador de errores cierra el descriptor, por lo tanto, el error de la tubería rota poco después.

En mi experiencia, MediaPlayer tiene muchos bits rotos como este. Nunca lo he visto funcionar para nada, sino directamente en archivos locales, y (muy buggily) para la transmisión HTTP. Terminé portando FFmpeg para solucionar sus numerosos fallos.


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);