libstreaming icon indicating copy to clipboard operation
libstreaming copied to clipboard

Dual camera support

Open ghost opened this issue 10 years ago • 7 comments

Hi,

I am trying to display both front camera and back camera previews using libstreaming. I want to know if we can use two separate SurfaceViews (one for back and one for front camera) and then initiate a session for each of them.

Currently, I am facing an issue while doing so as it is not able to access both cameras simultaneously. It connects to only one camera at a time. Is this possible using libstreaming?

Here is my code public class MainActivity extends Activity implements RtspClient.Callback, Session.Callback, SurfaceHolder.Callback {

   // log tag
public final static String TAG = MainActivity.class.getSimpleName();
// surfaceview
private static SurfaceView mSurfaceView;
// Rtsp session
private Session mSession;
private static RtspClient mClient;
private Button btnSwitch;
private static SurfaceView mFrontSurfaceView;
private Session mFrontSession;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    requestWindowFeature(Window.FEATURE_NO_TITLE);

    setContentView(R.layout.activity_main);

    mSurfaceView = (SurfaceView) findViewById(R.id.surface);
    mFrontSurfaceView = (SurfaceView) findViewById(R.id.surface2);
    btnSwitch = (Button)findViewById(R.id.btnSwitch);
    mSurfaceView.getHolder().addCallback(this);
    mFrontSurfaceView.getHolder().addCallback(this);

    // Initialize RTSP client
    initRtspClient(); 

    btnSwitch.setOnClickListener(new View.OnClickListener() {   
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            try{
                //TODO check if front or back camera is on and then switch
                //by default it is back camera
                mSession.switchCamera(); //change from back camera to front camera              
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    });


}

@Override
protected void onResume() {
    super.onResume();
    toggleStreaming();
}

@Override
protected void onPause(){
    super.onPause();
    toggleStreaming();
}

private void initRtspClient() {

    // Configures the SessionBuilder
    mSession = SessionBuilder.getInstance()
            .setContext(getApplicationContext())
            .setAudioEncoder(SessionBuilder.AUDIO_NONE)
            .setAudioQuality(new AudioQuality(8000, 16000))             
            .setVideoEncoder(SessionBuilder.VIDEO_H264)
            .setCamera(CameraInfo.CAMERA_FACING_BACK)
            .setSurfaceView(mSurfaceView).setPreviewOrientation(90)
            .setCallback(this).build();

    //front camera session
    mFrontSession =  SessionBuilder.getInstance()
            .setContext(getApplicationContext())
            .setAudioEncoder(SessionBuilder.AUDIO_NONE)
            .setAudioQuality(new AudioQuality(8000, 16000))             
            .setVideoEncoder(SessionBuilder.VIDEO_H264)
            .setSurfaceView(mFrontSurfaceView).setPreviewOrientation(0)
            .setCamera(CameraInfo.CAMERA_FACING_FRONT)
            .setCallback(this).build();

    // Configures the RTSP client
    mClient = new RtspClient();
    mClient.setSession(mSession);
    mClient.setSession(mFrontSession);
    mClient.setCallback(this);
    mSurfaceView.setAspectRatioMode(SurfaceView.ASPECT_RATIO_PREVIEW);
    mFrontSurfaceView.setAspectRatioMode(SurfaceView.ASPECT_RATIO_PREVIEW);
    String ip, port, path;

    // We parse the URI written in the Editext
    Pattern uri = Pattern.compile("rtsp://(.+):(\\d+)/(.+)");
    Matcher m = uri.matcher(AppConfig.STREAM_URL);
    m.find();
    ip = m.group(1);
    port = m.group(2);
    path = m.group(3);

    mClient.setCredentials(AppConfig.PUBLISHER_USERNAME,
            AppConfig.PUBLISHER_PASSWORD);
    mClient.setServerAddress(ip, Integer.parseInt(port));
    mClient.setStreamPath("/" + path);
}

private void toggleStreaming() {
    if (!mClient.isStreaming()) {
        // Start camera preview
        mSession.startPreview();
        mFrontSession.startPreview();
        // Start video stream
        mClient.startStream();
    } else {
        // already streaming, stop streaming
        // stop camera preview
        mSession.stopPreview();
        mFrontSession.stopPreview();
        // stop streaming
        mClient.stopStream();
    }
}

@Override
public void onDestroy() {
    super.onDestroy();
    mClient.release();
    mSession.release();
    mFrontSession.release();
    mSurfaceView.getHolder().removeCallback(this);
    mFrontSurfaceView.getHolder().removeCallback(this);
}

@Override
public void onSessionError(int reason, int streamType, Exception e) {
    switch (reason) {
    case Session.ERROR_CAMERA_ALREADY_IN_USE:
        break;
    case Session.ERROR_CAMERA_HAS_NO_FLASH:
        break;
    case Session.ERROR_INVALID_SURFACE:
        break;
    case Session.ERROR_STORAGE_NOT_READY:
        break;
    case Session.ERROR_CONFIGURATION_NOT_SUPPORTED:
        break;
    case Session.ERROR_OTHER:
        break;
    }

    if (e != null) {
        alertError(e.getMessage());
        e.printStackTrace();
    }
}

private void alertError(final String msg) {
    final String error = (msg == null) ? "Unknown error: " : msg;
    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
    builder.setMessage(error).setPositiveButton("Ok",
            new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
        }
    });
    AlertDialog dialog = builder.create();
    dialog.show();
}

@Override
public void onRtspUpdate(int message, Exception exception) {
    switch (message) {
    case RtspClient.ERROR_CONNECTION_FAILED:
    case RtspClient.ERROR_WRONG_CREDENTIALS:
        alertError(exception.getMessage());
        exception.printStackTrace();
        break;
    }
}

@Override
public void onPreviewStarted() {
}

@Override
public void onSessionConfigured() {
}

@Override
public void onSessionStarted() {
}

@Override
public void onSessionStopped() {
}

@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}

@Override
public void onBitrateUpdate(long bitrate) {
    // TODO Auto-generated method stub
}
}

When I run this code, I get a RunTimeException: Failed to connect to camera service.

ghost avatar Sep 02 '15 02:09 ghost

Regarding your question "I want to know if we can use two separate SurfaceViews (one for back and one for front camera)" Yes, it is possible to use two separate SurfaceViews in the application and have data from two sources feed these surfaces.

Having said that, I have couple of inputs regarding the other parts of your question

  • Please note that SessionBuilder is a singleton class(unless you have modified it). So when you do

mSession = SessionBuilder.getInstance() ... mFrontSession = SessionBuilder.getInstance() ...

you are essentially connecting to the same SessionBuilder instance. I dont think this is what you intended.

  • Also, have you verified that dual camera works using any other app? In other words, do you know for a fact that the version of android you are using supports dual camera? (Until some versions of android, the framework did not support dual camera.. hence the question). If you are sure that the version of android you have supports dual camera, changing the SessionBuilder class should be the first step(or maybe have a copy of SessionBuilder like SessionBuilder_FrontCam. I say this because some classes of libstreaming relies on SessionBuilder being singleton. For eg: RTSPServer, where while parsing the URI, it gets the SessionBuilder instance to build a session)

Locnath avatar Sep 02 '15 04:09 Locnath

Hi Locnath,

Thanks for the quick reply.

Yes, it is possible to use two separate SurfaceViews in the application and have data from two sources feed these surfaces.

Ok, that's great since I have two defined two SurfaceViews in my layout currently.

Please note that SessionBuilder is a singleton class(unless you have modified it)

OK I will make note of that. I have not modified the SessionBuilder class till now. Yes , I do not want to connect to the same SessionBuilder instance as that would not help in achieving my functionality. So should I modify SessionBuilder class or is there any workaround for the same?

As per my knowledge, the dual camera feature is currently supported on high end devices like LG G3, Samsung S5 etc. I am testing my app on one of those devices only. The Android version is 4.4 Kitkat. I feel its not the Android version but the device manufacturer's support for dual camera that I need to be concerned about. I will check on the same and reply back. (Let me know what you think!)

If you are sure that the version of android you have SessionBuilder class should be the first step(or maybe have a copy of SessionBuilder like SessionBuilder_FrontCam

Ok if I do so, then apart from creating a copy, do I need to change any of the methods in the class? Can you provide your inputs regarding this?

Regards, Karan

ghost avatar Sep 02 '15 05:09 ghost

Official Android camera API (also camera2 on Lollipop and Marshmallow) does not support dual camera. Some devices (e.g. S4) support "picture in picture" in their stock camera app, but they use undocumented extensions for camera API, and I am not sure they even allow 3rd party developers to enable this (may require extra private permissions).

On the other hand, Qualcomm Snapdragon 801-based devices support two simultaneous cameras out-of-the-box.

See also http://stackoverflow.com/questions/11419940/using-both-front-and-back-cameras-simultaneously-android

alexcohn avatar Sep 02 '15 09:09 alexcohn

@alexcohn : Thanks for the info. Yes a very few phones support that feature as of now. I too stumbled upon similar links but couldn't get any concrete answer.

ghost avatar Sep 02 '15 10:09 ghost

@karanbalkar @alexcohn @karanbalkar @Locnath Hi all!

For htc one add to manifest:

<permission android:name="com.htc.camera.permission.NOTIFY_BI_REPORT"/>
<permission android:name="com.htc.camera.permission.CONTROL"/>
<permission android:name="com.htc.camera.permission.RECEIVE_STATE_CHANGE"/>
<uses-permission android:name="com.htc.camera.permission.NOTIFY_BI_REPORT"/>

psalexander avatar Dec 07 '15 13:12 psalexander

I need to run this whole thing in a background service. I do not want to star the whole thing in the Main Activity. Can any one please help on how to achieve this. I am facing an issue on how to create the Surface View in the service.

Shanmugamsundarrajan avatar Sep 01 '17 12:09 Shanmugamsundarrajan

In which case I have to use RTSPClient and RTSPServer? My app is to Stream Live from both cameras. so what should I use RTSPClient or RTSPServer?

mohd-nabil avatar Jun 20 '19 06:06 mohd-nabil