25

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!

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • FWIW, I have a related question about using `createPipe()` with `MediaPlayer`: http://stackoverflow.com/questions/12920429/anyone-have-mediaplayer-working-with-parcelfiledescriptor-and-createpipe – CommonsWare Oct 17 '12 at 12:10
  • Did you solve this problem? – Teocci May 19 '17 at 12:24
  • @Teocci: No. Android O's `ProxyFileDescriptorCallback` *might* help here, but only on Android O and higher. Basically, pipes do not yield seekable streams. – CommonsWare May 19 '17 at 12:28
  • Omg well i have this problem becuase im using `MediaRecorder` to stream the video using RTP protocol. I tried with MediaCodec but is it so hard in Android. – Teocci May 19 '17 at 12:35

1 Answers1

14

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.

Community
  • 1
  • 1
luciofm
  • 737
  • 5
  • 14
  • 1
    Aha! That is indeed working, and your explanation makes sense. Many thanks! – CommonsWare Oct 17 '12 at 14:22
  • Im trying to use this to create an inputstream for a videorecording app but im not sure where your input stream is coming from, what were you passing in for your inputstream exactly? any help would go a long way thatnks – Edmund Rojas Jun 08 '13 at 20:12
  • It's not working right now. The RAW_AMR was deprecated. @CommonsWare Do you have any alternative solutions? – mr.icetea Oct 16 '18 at 16:46
  • @mr.icetea: I have not looked at this problem in a few years, so I am not up to date on what media codes do not require headers. The underlying problem of not being able to seek a pipe remains unchanged AFAIK, so if there are no header-less codecs, I don't know what you could do. – CommonsWare Oct 16 '18 at 21:39