1

Context:

  • I have two devices running the same android app.
  • The app starts a recorder onCreate.
  • The recorded bytes are sent to a node socket server.
  • The socket server sends the audio to the other device, where the socket event listener sets up the media player to play the audio.
  • Media player fails at the mediaPlayer.prepare() method.

I also have used a MediaDataSource class to store the bytes.

// MainActivity.java
public class MainActivity extends AppCompatActivity {
    private static final String LOG_TAG = "socket_main_act";
    private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
    private static final int SAMPLING_RATE = 22050;
    private MediaPlayer mediaPlayer = new MediaPlayer();
    private MediaRecorder recorder = new MediaRecorder();
    private boolean permissionToRecord = false;
    private String [] permissions = {Manifest.permission.RECORD_AUDIO};

    private void startRecording(Socket socket) {
        Log.i(LOG_TAG, "start recording.");
        try {
            ParcelFileDescriptor[] descriptors = ParcelFileDescriptor.createPipe();
            ParcelFileDescriptor recorderRead = new ParcelFileDescriptor(descriptors[0]);
            ParcelFileDescriptor recorderWrite = new ParcelFileDescriptor(descriptors[1]);
            InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(recorderRead);
            Log.i(LOG_TAG, "Setup IO.");

            recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            recorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
            recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC_ELD);
            recorder.setOutputFile(recorderWrite.getFileDescriptor());
            recorder.setAudioSamplingRate(SAMPLING_RATE);
            Log.i(LOG_TAG, "Setup recorder.");
            recorder.prepare();
            recorder.start();
            Log.i(LOG_TAG, "Start recording.");

            int read = 1;
            byte[] data = new byte[SAMPLING_RATE];

            while (read != -1) {
                read = inputStream.read(data, 0, data.length);
                socket.emit("audio", (Object) data);
            }
            Log.i(LOG_TAG, "Data from recorder exhausted.");
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(LOG_TAG, "Recording faults");
        }
    }

    public Emitter.Listener onAudioBroadcast = new Emitter.Listener() {
        @Override
        public void call(final Object... args) {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream os;
            byte[] data;
            try {
                os = new ObjectOutputStream(out);
                for (Object arg : args) {
                    os.writeObject(arg);
                }
                data = out.toByteArray();
                Log.i(LOG_TAG, "Read data as byte array.");
                AudioDataSource src = new AudioDataSource(data);
                Log.i(LOG_TAG, "Setup player and src");
                AudioAttributes attr = new AudioAttributes.Builder()
                    .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                    .build();
                Log.i(LOG_TAG, "Setup attributes");
                mediaPlayer.setAudioAttributes(attr);
                Log.i(LOG_TAG, "Player set with attributes.");
                mediaPlayer.setDataSource(src);
                Log.i(LOG_TAG, "Player read from source");
                mediaPlayer.prepare();
                Log.i(LOG_TAG, "prepare player.");
                mediaPlayer.start();
                Log.i(LOG_TAG, "Start playing.");
            } catch (IOException e) {
                Log.e(LOG_TAG, "Player failed to stream audio: " + e.toString());
                e.printStackTrace();
            }

        }
    };

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_RECORD_AUDIO_PERMISSION:
                permissionToRecord = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                break;
        }
        if (!permissionToRecord) finish();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION);
        setContentView(R.layout.activity_main);
        try {
            Socket mSocket = IO.socket("http://192.168.2.49:3000");
            mSocket.on("audio-broadcast", onAudioBroadcast);
            mSocket.connect();
            startRecording(mSocket);
        } catch (URISyntaxException e) {
            Log.d(LOG_TAG, "uri is invalid.");
        }
    }
}

and here is the MediaDataSource implementation.

public class AudioDataSource extends MediaDataSource {

    private byte[] data;
    private static final String LOG_TAG = "audio_src";

    public AudioDataSource(byte[] data) {
        this.data = data;
    }

    @Override
    public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
        if (position >= data.length) return -1;
        Log.i(LOG_TAG, "Data non-empty.");
        int endPosition = (int)(position + size);
        Log.i(LOG_TAG, String.valueOf(endPosition));
        int adjustedSize = size;
        if (endPosition > data.length) {
            adjustedSize -= endPosition - data.length;
            Log.i(LOG_TAG, String.valueOf(adjustedSize));
        }
        System.arraycopy(data, (int)position, buffer, offset, adjustedSize);
        Log.i(LOG_TAG, "Copy array");
        return adjustedSize;
    }

    @Override
    public long getSize() throws IOException {
        return (long)data.length;
    }

    @Override
    public void close() throws IOException { }
}

The logs here show that there is some data moving around, yet the error:

I/audio_src: Data non-empty.
2019-12-30 22:24:59.154 17285-17919/com.example.socket_client_demo I/audio_src: 22528
2019-12-30 22:24:59.154 17285-17919/com.example.socket_client_demo I/audio_src: 1597
2019-12-30 22:24:59.154 17285-17919/com.example.socket_client_demo I/audio_src: Copy array
2019-12-30 22:24:59.154 17285-17919/com.example.socket_client_demo I/audio_src: Data non-empty.
2019-12-30 22:24:59.155 17285-17919/com.example.socket_client_demo I/audio_src: 2048
2019-12-30 22:24:59.155 17285-17919/com.example.socket_client_demo I/audio_src: Copy array
2019-12-30 22:24:59.156 17285-17919/com.example.socket_client_demo E/MediaPlayerNative: error (1, -2147483648)
2019-12-30 22:24:59.156 17285-17349/com.example.socket_client_demo E/socket_main_act: Player failed to stream audio: java.io.IOException: Prepare failed.: status=0x1

I am assuming that mediaPlayer is not able to process the data from the src? Please help me find what is incorrect with my approach.

If it would help to run the app on your system, here is the repo for the same. You would need a socket.io server too, make sure the phone is connected to the same network as your socket server.

Amresh Venugopal
  • 9,299
  • 5
  • 38
  • 52
  • Have you configured `networkSecurityConfig` in your Manifest? – Christilyn Arjona Dec 30 '19 at 19:01
  • Pardon me for I don't know why I'd need that, could you tell me what is the use for it? – Amresh Venugopal Dec 31 '19 at 07:49
  • No worries. It's because that same IO exception occurs (for media player) when you don't have network security configured in your manifest. See this post for more details https://stackoverflow.com/questions/55479574/android-mediaplayer-java-io-ioexception-prepare-failed-status-0x1 – Christilyn Arjona Dec 31 '19 at 07:55

0 Answers0