4

I am trying to record mpeg2-ts video in order to stream it to a server / socket using the Android MediaRecorder class as described here...

The code is as follows :

public class MediaRecorderDemo extends Activity
{
private final static String TAG = "MediaRecorderDemo";
Camera camera;
CameraPreview cameraPreview;
MediaRecorder mediaRecorder;
File outputFile = new File(Environment.getExternalStorageDirectory().getPath() + "/out1.ts");

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.media_recorder_demo_layout);

    camera = getCameraInstance();
    cameraPreview = new CameraPreview(this);
    FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
    preview.addView(cameraPreview);
}

private Camera getCameraInstance()
{
    final String FUNCTION = "getCameraInstance";

    Camera c = null;
    try
    {
        c = Camera.open();
    }
    catch(Exception e)
    {
        Log.e(TAG, FUNCTION + " : " + e.getMessage());
    }

    return c;
}

void initMediaRecorder()
{
    final String FUNCTION = "initMediaRecorder";
    FileDescriptor outputFileFD = null;
    try
    {
        outputFile.createNewFile();
        outputFileFD = new FileOutputStream(outputFile).getFD();
    }
    catch(Exception e)
    {
        Log.e(TAG, FUNCTION + " : " + e.getMessage());
    }

    mediaRecorder = new MediaRecorder();
    mediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
        final static String TAG = "MediaRecorder.onErrorListener";

        @Override
        public void onError(MediaRecorder mr, int what, int extra) {
            Log.e(TAG, "Error : " + what + " " + extra);
        }
    });

    camera.unlock();
    mediaRecorder.setPreviewDisplay(cameraPreview.getHolder().getSurface());
    mediaRecorder.setCamera(camera);
    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
    //mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));
    mediaRecorder.setOutputFormat(8);
    Log.d(TAG, "File Exists : " + outputFile.exists());
    mediaRecorder.setOutputFile(outputFileFD);
    mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
    //mediaRecorder.setVideoSize(640, 480);
    mediaRecorder.setMaxDuration(-1);
    //mediaRecorder.setVideoFrameRate(16);
    mediaRecorder.setVideoEncodingBitRate(1024 * 1024);

    try
    {
        mediaRecorder.prepare();
        Log.d(TAG, "MediaRecorder Prepared.");
        mediaRecorder.start();

    }
    catch(Exception e)
    {
        Log.e(TAG, FUNCTION + " : " + e.getMessage());
        //releaseMediaRecorder();
    }
}

void releaseMediaRecorder()
{
    final String FUNCTION = "releaseMediaRecorder";

    try
    {
        if(mediaRecorder != null)
        {
            mediaRecorder.stop();
            mediaRecorder.reset();
            mediaRecorder.release();
            mediaRecorder = null;
            camera.lock();
        }
    }

    catch(Exception e)
    {
        Log.e(TAG, FUNCTION + " : " + e.getMessage());
    }
}

void releaseCamera()
{
    final String FUNCTION = "releaseCamera";
    try
    {
        if(camera != null)
        {
            camera.stopPreview();
            camera.release();
        }
        camera = null;
    }
    catch(Exception e)
    {
        Log.e(TAG, FUNCTION + " : " + e.getMessage());
    }
}

@Override
public void onStart()
{
    super.onStart();
}

@Override
public void onPause()
{
    super.onPause();
}

@Override
public void onResume()
{
    super.onResume();
}

@Override
public void onStop()
{
    super.onStop();
}

@Override
public void onDestroy()
{
    super.onDestroy();
}

public class CameraPreview extends SurfaceView
{
    private final static String TAG = "CameraPreview"; 
    SurfaceHolder holder;
    boolean isPreviewDisplaySet;

    public CameraPreview(Context context)
    {
        this(context, (AttributeSet)null);

        this.holder = getHolder();
        this.holder.addCallback(new SurfaceHolderCallback());
    }

    public CameraPreview(Context context, AttributeSet attrSet)
    {
        this(context, attrSet, 0);
    }

    public CameraPreview(Context context, AttributeSet attrSet, int defStyle)
    {
        super(context, attrSet, defStyle);
    }

    private void releaseCamera()
    {
        if(camera != null)
        {
            camera.release();
            camera = null;
        }
    }

    private class SurfaceHolderCallback implements SurfaceHolder.Callback
    {
        @Override
        public void surfaceCreated(SurfaceHolder holder)
        {
            final String FUNCTION = "surfaceCreated";
            Log.d(TAG, "Surface Created.");

            try
            {
                camera.setPreviewDisplay(holder);
                camera.startPreview();

                initMediaRecorder();
                //mediaRecorder.start();
            }
            catch(Exception e)
            {
                Log.e(TAG, FUNCTION + " : " + e.getMessage());
            }
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder)
        {
            final String FUNCTION = "surfaceDestroyed";
            Log.d(TAG, "Surface Destroyed.");

            try
            {
                releaseMediaRecorder();
                releaseCamera();
            }
            catch(Exception e)
            {
                Log.e(TAG, FUNCTION + " : " + e.getMessage());
            } 
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
        {
            final String FUNCTION = "surfaceChanged";
            Log.d(TAG, "Surface Changed.");

            if(holder.getSurface() == null)
                return;

            try
            {
                camera.stopPreview();
            }
            catch(Exception e)
            {
                Log.e(TAG, FUNCTION + " : " + e.getMessage());
            }

            try
            {
                camera.setPreviewDisplay(holder);
                camera.startPreview();
            }
            catch(Exception e)
            {
                Log.e(TAG, FUNCTION + " : " + e.getMessage());
            }
        }
    }
}
}

The mediaRecorder.prepare() and mediaRecorder.start() are invoked without any error and the camera preview is displayed... But after a while the preview is cut off and then the screen freezes and an empty file is created at the output path... This issue is also being reported on the Android Issue List but is not yet being rectified...

I have tried to run the same app on a Galaxy Note N7000 with Stock Android ICS ROM and also on a Samsung Galaxy Tab 2 P3100 but with a Custom Android 4.2 ROM...So it does not seem to be something related to a ROM or a specific hardware config...

If there is something that am missing on or doing wrong, would be glad and certainly relieved to know that...?

Thanks...

Stryker33
  • 473
  • 1
  • 5
  • 16
  • Do you get any errors on your `logcat`? Can you share if you have any logs? Also, do you observe this behavior with only `MPEG-2 TS` or do you observe with say `MPEG-4` recording also? – Ganesh Mar 23 '13 at 02:39
  • Currently the hide API OUTPUT_FORMAT_MPEG2TS works for video only, if encode both video and audio, the ts output is malformed. The solution is start two MediaRecorder deal with video and audio separately, then mux video and audio frame by yourself. – yorkw Jun 10 '15 at 10:39

2 Answers2

2

The solution is applying a patch in M2ts Writer. Build libstagefright.so and push to device. Also set below in application

recorder.setAudioSamplingRate(48000); 
recorder.setAudioEncodingBitRate(128000);

or else it will not record the clip completely. I did not dig into the reason for setting the above parameters though.

Patch for M2tsWriter in libstagefright:

diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp

index c9ed5bb..a42371f 100644

--- a/media/libstagefright/MPEG2TSWriter.cpp

+++ b/media/libstagefright/MPEG2TSWriter.cpp

@@ -411,6 +411,7 @@ void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp<AMessage> &msg) { 
                            (const uint8_t *)buffer->data() 
                             + buffer->range_offset(), 
                            buffer->range_length()); 
+                    readMore(); 
                 } else if (buffer->range_length() > 0) {
                     if (mStreamType == 0x0f) { 
                         if (!appendAACFrames(buffer)) { 
sdh
  • 53
  • 1
  • 8
  • I do not see any code missing. 1 line code change in media/libstagefright/MPEG2TSWriter.cpp and calling the 2 above set apis for aac encoder setting. – sdh Sep 28 '14 at 02:21
-1

I got similar problem to yours, though not on the same device. According to my initial investigation, recording heap(buffer) of camera in HAL was not released correctly when mpeg ts was being recorded. But I'm still not very sure whether yuv data had reached OMX. The advanced cause should be checked by each hardware vendor. Hope it helps. :)

popahqiu
  • 1
  • 2