Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anyone Have MediaRecorder Working with ParcelFileDescriptor and createPipe()?

I am trying to work out an example of recording audio, with the data storage being handled by the app, not MediaRecorder. Use cases include storing the recording on internal storage or encrypting the recording.

In principle, this should work using a pipe created by createPipe() on ParcelFileDescriptor, but I am getting malformed output.

First, here is a sample project that records "naturally" using MediaRecorder, with MediaRecorder writing directly to an output file on external storage. This app works just fine, and the output can be played either by the Android device the recorded it or VLC on my Linux box.

Here is my createPipe() variation of this project. From the standpoint of general MediaRecorder configuration (e.g., setOutputFormat()), it is the same as the first, so that code is presumably correct.

However, I am supplying the output via:

  recorder.setOutputFile(getStreamFd());

Where getStreamFd() uses createPipe(), spawns a background thread to read from the pipe, and returns the writing end for use by MediaRecorder:

  private FileDescriptor getStreamFd() {
    ParcelFileDescriptor[] pipe=null;

    try {
      pipe=ParcelFileDescriptor.createPipe();

      new TransferThread(new AutoCloseInputStream(pipe[0]),
                       new FileOutputStream(getOutputFile())).start();
    }
    catch (IOException e) {
      Log.e(getClass().getSimpleName(), "Exception opening pipe", e);
    }

    return(pipe[1].getFileDescriptor());
  }

TransferThread is a classic java.io stream-to-stream copy routine, augmented with smarts to flush and sync the output file:

  static class TransferThread extends Thread {
    InputStream in;
    FileOutputStream out;

    TransferThread(InputStream in, FileOutputStream out) {
      this.in=in;
      this.out=out;
    }

    @Override
    public void run() {
      byte[] buf=new byte[8192];
      int len;

      try {
        while ((len=in.read(buf)) > 0) {
          out.write(buf, 0, len);
        }

        in.close();

        out.flush();
        out.getFD().sync();
        out.close();
      }
      catch (IOException e) {
        Log.e(getClass().getSimpleName(),
              "Exception transferring file", e);
      }
    }
  }

When I run the second app, I get an output file that, by crude inspection via a hex editor, seems basically OK. IOW, it's not like it's a zero-byte file, or is filled with unrecognizable gibberish. It is filled with a similar sort of gibberish as is the output from the first app. However, neither Android nor VLC can play it.

If I had to guess, I would presume that I am screwing up something in reading from the pipe, but I am not sure where specifically I am going wrong.

Any suggestions?

Thanks in advance!

like image 803
CommonsWare Avatar asked Oct 15 '12 11:10

CommonsWare


1 Answers

I would guess this is related to my answer to your other question. Anyone Have MediaPlayer Working with ParcelFileDescriptor and createPipe()?

Probably when the MediaRecorder will seek to write the Header information, the pipe is closed.

If you use:

recorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

The record works fine, because it will not have a header information, only raw audio.

like image 97
luciofm Avatar answered Nov 05 '22 05:11

luciofm