ApprtDemo Android: Switch between front and back camera.

This adds a UI icon for switching between the front and back camera.
This cl adds the possibility to change between the front and back camera while in a call
or before the other end have connected.

BUG=3786
R=glaznev@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/23219004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7553 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
perkj@webrtc.org 2014-10-29 08:10:03 +00:00
parent 663fdd02fd
commit 7998089789
4 changed files with 121 additions and 16 deletions

View File

@ -18,6 +18,13 @@
<!-- TODO(kjellander): Add audio and video mute buttons. -->
<ImageButton
android:id="@+id/button_switch_camera"
android:background="@android:drawable/ic_menu_camera"
android:contentDescription="@string/switch_camera"
android:layout_width="48dp"
android:layout_height="48dp"/>
<ImageButton
android:id="@+id/button_toggle_debug"
android:background="@android:drawable/ic_menu_info_details"

View File

@ -16,6 +16,7 @@
<string name="connecting_to">Connecting to: %1$s</string>
<string name="missing_url">FATAL ERROR: Missing URL to connect to.</string>
<string name="ok">OK</string>
<string name="switch_camera">Switch front/back camera</string>
<string name="action_settings">Settings</string>
<!-- Settings strings. -->

View File

@ -139,6 +139,14 @@ public class AppRTCDemoActivity extends Activity
}
});
((ImageButton) findViewById(R.id.button_switch_camera)).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
pc.switchCamera();
}
});
((ImageButton) findViewById(R.id.button_toggle_debug)).setOnClickListener(
new View.OnClickListener() {
@Override

View File

@ -60,13 +60,17 @@ public class PeerConnectionClient {
private boolean videoSourceStopped;
private final PCObserver pcObserver = new PCObserver();
private final SDPObserver sdpObserver = new SDPObserver();
private final MediaConstraints videoConstraints;
private final VideoRenderer.Callbacks localRender;
private final VideoRenderer.Callbacks remoteRender;
private LinkedList<IceCandidate> queuedRemoteCandidates =
new LinkedList<IceCandidate>();
private MediaConstraints sdpMediaConstraints;
private PeerConnectionEvents events;
private final MediaConstraints sdpMediaConstraints;
private final PeerConnectionEvents events;
private boolean isInitiator;
private boolean useFrontFacingCamera = true;
private SessionDescription localSdp = null; // either offer or answer SDP
private MediaStream videoMediaStream = null;
public PeerConnectionClient(
Activity activity,
@ -75,6 +79,8 @@ public class PeerConnectionClient {
AppRTCSignalingParameters appRtcParameters,
PeerConnectionEvents events) {
this.activity = activity;
this.videoConstraints = appRtcParameters.videoConstraints;
this.localRender = localRender;
this.remoteRender = remoteRender;
this.events = events;
isInitiator = appRtcParameters.initiator;
@ -101,23 +107,19 @@ public class PeerConnectionClient {
// EnumSet.of(Logging.TraceLevel.TRACE_ALL),
// Logging.Severity.LS_SENSITIVE);
Log.d(TAG, "Creating local video source");
MediaStream lMS = factory.createLocalMediaStream("ARDAMS");
if (appRtcParameters.videoConstraints != null) {
VideoCapturer capturer = getVideoCapturer();
videoSource = factory.createVideoSource(
capturer, appRtcParameters.videoConstraints);
VideoTrack videoTrack =
factory.createVideoTrack("ARDAMSv0", videoSource);
videoTrack.addRenderer(new VideoRenderer(localRender));
lMS.addTrack(videoTrack);
if (videoConstraints != null) {
videoMediaStream = factory.createLocalMediaStream("ARDAMSVideo");
videoMediaStream.addTrack(createVideoTrack(useFrontFacingCamera));
pc.addStream(videoMediaStream, new MediaConstraints());
}
if (appRtcParameters.audioConstraints != null) {
MediaStream lMS = factory.createLocalMediaStream("ARDAMSAudio");
lMS.addTrack(factory.createAudioTrack(
"ARDAMSa0",
factory.createAudioSource(appRtcParameters.audioConstraints)));
pc.addStream(lMS, new MediaConstraints());
}
pc.addStream(lMS, new MediaConstraints());
}
public boolean getStats(StatsObserver observer, MediaStreamTrack track) {
@ -202,11 +204,15 @@ public class PeerConnectionClient {
// Cycle through likely device names for the camera and return the first
// capturer that works, or crash if none do.
private VideoCapturer getVideoCapturer() {
private VideoCapturer getVideoCapturer(boolean useFrontFacing) {
String[] cameraFacing = { "front", "back" };
int[] cameraIndex = { 0, 1 };
int[] cameraOrientation = { 0, 90, 180, 270 };
if (!useFrontFacing) {
cameraFacing[0] = "back";
cameraFacing[1] = "front";
}
for (String facing : cameraFacing) {
int[] cameraIndex = { 0, 1 };
int[] cameraOrientation = { 0, 90, 180, 270 };
for (int index : cameraIndex) {
for (int orientation : cameraOrientation) {
String name = "Camera " + index + ", Facing " + facing +
@ -222,6 +228,22 @@ public class PeerConnectionClient {
throw new RuntimeException("Failed to open capturer");
}
private VideoTrack createVideoTrack(boolean frontFacing) {
VideoCapturer capturer = getVideoCapturer(frontFacing);
if (videoSource != null) {
videoSource.stop();
videoSource.dispose();
}
videoSource = factory.createVideoSource(
capturer, videoConstraints);
String trackExtension = frontFacing ? "frontFacing" : "backFacing";
VideoTrack videoTrack =
factory.createVideoTrack("ARDAMSv0" + trackExtension, videoSource);
videoTrack.addRenderer(new VideoRenderer(localRender));
return videoTrack;
}
// Poor-man's assert(): die with |msg| unless |condition| is true.
private static void abortUnless(boolean condition, String msg) {
if (!condition) {
@ -285,6 +307,49 @@ public class PeerConnectionClient {
queuedRemoteCandidates = null;
}
public void switchCamera() {
if (videoConstraints == null)
return; // No video is sent.
if (pc.signalingState() != PeerConnection.SignalingState.STABLE) {
Log.e(TAG, "Switching camera during negotiation is not handled.");
return;
}
pc.removeStream(videoMediaStream);
VideoTrack currentTrack = videoMediaStream.videoTracks.get(0);
videoMediaStream.removeTrack(currentTrack);
String trackId = currentTrack.id();
// On Android, there can only be one camera open at the time and we
// need to release our implicit references to the videoSource before the
// PeerConnectionFactory is released. Since createVideoTrack creates a new
// videoSource and frees the old one, we need to release the track here.
currentTrack.dispose();
useFrontFacingCamera = !useFrontFacingCamera;
VideoTrack newTrack = createVideoTrack(useFrontFacingCamera);
videoMediaStream.addTrack(newTrack);
pc.addStream(videoMediaStream, new MediaConstraints());
SessionDescription remoteDesc = pc.getRemoteDescription();
if (localSdp == null || remoteDesc == null) {
Log.d(TAG, "Switching camera before the negotiation started.");
return;
}
localSdp = new SessionDescription(localSdp.type,
localSdp.description.replaceAll(trackId, newTrack.id()));
if (isInitiator) {
pc.setLocalDescription(new SwitchCameraSdbObserver(), localSdp);
pc.setRemoteDescription(new SwitchCameraSdbObserver(), remoteDesc);
} else {
pc.setRemoteDescription(new SwitchCameraSdbObserver(), remoteDesc);
pc.setLocalDescription(new SwitchCameraSdbObserver(), localSdp);
}
}
// Implementation detail: observe ICE & stream changes and react accordingly.
private class PCObserver implements PeerConnection.Observer {
@Override
@ -442,4 +507,28 @@ public class PeerConnectionClient {
});
}
}
private class SwitchCameraSdbObserver implements SdpObserver {
@Override
public void onCreateSuccess(SessionDescription sdp) {
}
@Override
public void onSetSuccess() {
Log.d(TAG, "Camera switch SDP set succesfully");
}
@Override
public void onCreateFailure(final String error) {
}
@Override
public void onSetFailure(final String error) {
activity.runOnUiThread(new Runnable() {
public void run() {
throw new RuntimeException("setSDP error while switching camera: " + error);
}
});
}
}
}