6

I'm trying to send h264/AAC video from Android's MediaRecorder through a local Socket. The goal is to to send video to a WOWZA server throught RTMP or RTSP, but it's giving me a lot of trouble and for now I'm just trying to write the data to a file from the LocalServerSocket.

Here is some code. Sorry it's not really clean, but I spent hours testing many things and my project is a mess right now.

In the Camera activity, the output file setup:

LocalSocket outSocket = new LocalSocket();

try {
    outSocket.connect(new LocalSocketAddress(LOCAL_SOCKET));
} catch (Exception e) {
    Log.i(LOG_TAG, "Error connecting socket: "+e);
}
mMediaRecorder.setOutputFile(outSocket.getFileDescriptor());

The LocalServerSocket implementation:

try {
    mLocalServerSocket = new LocalServerSocket(mName);
} catch (Exception e) {
    Log.e(LOG_TAG, "Error creating server socket: "+e);
    return;
}

while (true) {

    File out = null;
    FileOutputStream fop = null;
    try {
        mLocalClientSocket = mLocalServerSocket.accept();

        InputStream in = mLocalClientSocket.getInputStream();

        out = new File(mContext.getExternalFilesDir(null), "testfile.mp4");
        fop = new FileOutputStream(out);

        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer)) >= 0) {

            Log.i(LOG_TAG, "Writing "+len+" bytes");
            fop.write(buffer, 0, len);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    finally{
        try {
            fop.close();
            mLocalClientSocket.close();
        } catch (Exception e2) {}
    }
}

The problem is that the file resulting from this is not readable by any media player. Do you think this is because of an encoding issue? This code should generate a binary file if I understand well?!

Thanks in advance, cheers.

Simon
  • 3,580
  • 2
  • 23
  • 24
  • if you don't get errors it is probably caused by encoding / filename issues. What [OutputFormat](http://developer.android.com/reference/android/media/MediaRecorder.OutputFormat.html) did you set? Maybe renaming it to `.3gp` or so helps - see [here](http://stackoverflow.com/questions/9110711/android-mediarecorder-getting-3gp-instead-of-mpeg4) – zapl May 23 '12 at 14:21
  • The output format is MPEG_4, so I guess it should by ok with a .mp4 extension. Besides, if I write directly to a .mp4 file in mMediaRecorder.setOutputFile() it's working perfectly. – Simon May 23 '12 at 14:34
  • did you check if something like [mediainfo](http://mediainfo.sourceforge.net/en) can read the properties of the created video? You could also try a binary diff (at least of the header) of a directly generated file and a file send through your local socket to check if the generated file looks right. – zapl May 23 '12 at 14:42
  • I didn't try with mediainfo, but no one on my media player can read the file or find any codec information (tried with VLC, MplayerX and Quicktime). I tried to compare the files too. The working one starts with: ftyp3gp43gp43gp6wideBtmdat The non-working one by: ftyp3gp43gp43gp6widemdat For the rest, I'm working on it but for now I couldn't say if there is a problem. – Simon May 23 '12 at 16:41
  • According to http://www.mattakis.com/blog/kisg/20090708/broadcasting-video-with-android-without-writing-to-the-file-system my issue should be related a header incorrectly set because sockets are not seekable. Trying to find a solution right now. – Simon May 24 '12 at 09:59

3 Answers3

2

Ok, I've found why the files couldn't play. In MP4 and 3GPP files, there is a header containing the bytes:

ftyp3gp4 3gp43gp6 wide mdat

in HEX

0000001866747970336770340000030033677034336770360000000877696465000392D86D6461740000

The 4 bytes before the 'mdat' tag represent the position of another 'moov' tag situated at the end of the file. The position is usually set when the recording is over, but as MediaRecorder can't seek sockets, it can't set these bytes to the correct value in our case.

My problem now is to find a way to make such a file streamable, as it involves for it to be played before the recording is over.

Simon
  • 3,580
  • 2
  • 23
  • 24
1

You could try using mp4box to restructure your file. The moov box gives the indexes for each audio and video sample. If that is at the end of the file, it makes it difficult to stream.

This might help: http://boliston.wordpress.com/tag/moov-box/

Or this: mp4box -inter 0.5 some_file.mp4

(I don't have the chance to try currently)

If you need this to work with your app, I am not aware of any activities to port mp4box to Android.

  • BTW mp4box is part of the GPAC project nowadays. There is a package for that in Ubuntu, at least. http://gpac.wp.mines-telecom.fr/ – AOSP_junkie Sep 16 '13 at 11:09
0

I tried today to do the same, but mp4 is not very easy to stream (as said before some parts are written at the end). I don't say it's impossible but it seems at least quite hard.

So a workaround for newer Android APIs (4.3) could be this one:

  • Set the camera preview to a SurfaceTexture: camera.setPreviewTexture
  • Record this texture using OpenGL and MediaCodex + Muxer

The drawback of this solution is that the preview size of a camera might be smaller than the video size. This means depending on you device you can't record at the highest resolution. Hint: some cameras say they don't support higher preview sizes but they do and you can try to configure the camera to set the preview size to the video size. If you do so catch the RuntimeException of camera.setParameters and if it fails only use the supported preview sizes.

Some links how to record from a SurfaceTexture:

Bigflage: great examples for MediaCodec stuff.

The VideoRecorder class from Lablet.

May also be useful: spydroid-ipcamera streams the data from the MediaRecorder socket as RTP streams but I have found no way to feed that to the MediaCodec. (I already got stuck reading the correct NAL unit sizes as they do...)

Clemens
  • 86
  • 8