|  |  |  | @ -8,6 +8,7 @@ import com.otaliastudios.cameraview.VideoResult; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.cameraview.controls.Audio; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.cameraview.controls.VideoCodec; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.cameraview.internal.DeviceEncoders; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.cameraview.internal.utils.CamcorderProfiles; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.cameraview.size.Size; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | import androidx.annotation.NonNull; | 
			
		
	
	
		
			
				
					|  |  |  | @ -17,10 +18,9 @@ import androidx.annotation.Nullable; | 
			
		
	
		
			
				
					|  |  |  |  |  * A {@link VideoRecorder} that uses {@link android.media.MediaRecorder} APIs. | 
			
		
	
		
			
				
					|  |  |  |  |  * | 
			
		
	
		
			
				
					|  |  |  |  |  * When started, the media recorder will be prepared in | 
			
		
	
		
			
				
					|  |  |  |  |  * {@link #onPrepareMediaRecorder(VideoResult.Stub, MediaRecorder)}. | 
			
		
	
		
			
				
					|  |  |  |  |  * Subclasses should override this method and, before calling super(), do two things: | 
			
		
	
		
			
				
					|  |  |  |  |  * - set the media recorder VideoSource | 
			
		
	
		
			
				
					|  |  |  |  |  * - define {@link #mProfile} | 
			
		
	
		
			
				
					|  |  |  |  |  * {@link #prepareMediaRecorder(VideoResult.Stub)}. This will call two abstract methods: | 
			
		
	
		
			
				
					|  |  |  |  |  * - {@link #getCamcorderProfile(VideoResult.Stub)} | 
			
		
	
		
			
				
					|  |  |  |  |  * - {@link #applyVideoSource(VideoResult.Stub, MediaRecorder)} | 
			
		
	
		
			
				
					|  |  |  |  |  * | 
			
		
	
		
			
				
					|  |  |  |  |  * Subclasses can also call {@link #prepareMediaRecorder(VideoResult.Stub)} before start happens, | 
			
		
	
		
			
				
					|  |  |  |  |  * in which case it will not be prepared twice. This can be used for example to test some | 
			
		
	
	
		
			
				
					|  |  |  | @ -32,7 +32,7 @@ public abstract class FullVideoRecorder extends VideoRecorder { | 
			
		
	
		
			
				
					|  |  |  |  |     private static final CameraLogger LOG = CameraLogger.create(TAG); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @SuppressWarnings("WeakerAccess") protected MediaRecorder mMediaRecorder; | 
			
		
	
		
			
				
					|  |  |  |  |     @SuppressWarnings("WeakerAccess") protected CamcorderProfile mProfile; | 
			
		
	
		
			
				
					|  |  |  |  |     private CamcorderProfile mProfile; | 
			
		
	
		
			
				
					|  |  |  |  |     private boolean mMediaRecorderPrepared; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -40,99 +40,140 @@ public abstract class FullVideoRecorder extends VideoRecorder { | 
			
		
	
		
			
				
					|  |  |  |  |         super(listener); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) | 
			
		
	
		
			
				
					|  |  |  |  |     protected boolean prepareMediaRecorder(@NonNull VideoResult.Stub stub) { | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Subclasses should return an appropriate CamcorderProfile. | 
			
		
	
		
			
				
					|  |  |  |  |      * This could be taken from the {@link CamcorderProfiles} utility class based on the | 
			
		
	
		
			
				
					|  |  |  |  |      * stub declared size, for instance. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param stub the stub | 
			
		
	
		
			
				
					|  |  |  |  |      * @return the profile | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     @NonNull | 
			
		
	
		
			
				
					|  |  |  |  |     protected abstract CamcorderProfile getCamcorderProfile(@NonNull VideoResult.Stub stub); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Subclasses should apply a video source to the given recorder. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param stub the stub | 
			
		
	
		
			
				
					|  |  |  |  |      * @param mediaRecorder the recorder | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     protected abstract void applyVideoSource(@NonNull VideoResult.Stub stub, | 
			
		
	
		
			
				
					|  |  |  |  |                                              @NonNull MediaRecorder mediaRecorder); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @SuppressWarnings("WeakerAccess") | 
			
		
	
		
			
				
					|  |  |  |  |     protected final boolean prepareMediaRecorder(@NonNull VideoResult.Stub stub) { | 
			
		
	
		
			
				
					|  |  |  |  |         if (mMediaRecorderPrepared) return true; | 
			
		
	
		
			
				
					|  |  |  |  |         return onPrepareMediaRecorder(stub, new MediaRecorder()); | 
			
		
	
		
			
				
					|  |  |  |  |         // We kind of trust the stub size at this point. It's coming from CameraOptions sizes
 | 
			
		
	
		
			
				
					|  |  |  |  |         // and it's clipped to be less than CamcorderProfile's highest available profile.
 | 
			
		
	
		
			
				
					|  |  |  |  |         // However, we still can't trust the developer parameters (e.g. bit rates), and even
 | 
			
		
	
		
			
				
					|  |  |  |  |         // without them, the camera declared sizes can cause crashes in MediaRecorder (#467, #602).
 | 
			
		
	
		
			
				
					|  |  |  |  |         // A possible solution was to prepare without checking DeviceEncoders first, and should it
 | 
			
		
	
		
			
				
					|  |  |  |  |         // fail, prepare again checking them. However, when parameters are wrong, MediaRecorder
 | 
			
		
	
		
			
				
					|  |  |  |  |         // fails on start() instead of prepare() (start failed -19), so this wouldn't be effective.
 | 
			
		
	
		
			
				
					|  |  |  |  |         return prepareMediaRecorder(stub, true); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     protected boolean onPrepareMediaRecorder(@NonNull VideoResult.Stub stub, | 
			
		
	
		
			
				
					|  |  |  |  |                                              @NonNull MediaRecorder mediaRecorder) { | 
			
		
	
		
			
				
					|  |  |  |  |         mMediaRecorder = mediaRecorder; | 
			
		
	
		
			
				
					|  |  |  |  |     @SuppressWarnings("SameParameterValue") | 
			
		
	
		
			
				
					|  |  |  |  |     private boolean prepareMediaRecorder(@NonNull VideoResult.Stub stub, | 
			
		
	
		
			
				
					|  |  |  |  |                                          boolean applyEncodersConstraints) { | 
			
		
	
		
			
				
					|  |  |  |  |         // 1. Create reference and ask for the CamcorderProfile
 | 
			
		
	
		
			
				
					|  |  |  |  |         mMediaRecorder = new MediaRecorder(); | 
			
		
	
		
			
				
					|  |  |  |  |         mProfile = getCamcorderProfile(stub); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 2. Set the video and audio sources.
 | 
			
		
	
		
			
				
					|  |  |  |  |         applyVideoSource(stub, mMediaRecorder); | 
			
		
	
		
			
				
					|  |  |  |  |         boolean hasAudio = stub.audio == Audio.ON | 
			
		
	
		
			
				
					|  |  |  |  |                 || stub.audio == Audio.MONO | 
			
		
	
		
			
				
					|  |  |  |  |                 || stub.audio == Audio.STEREO; | 
			
		
	
		
			
				
					|  |  |  |  |         if (hasAudio) { | 
			
		
	
		
			
				
					|  |  |  |  |             mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         mMediaRecorder.setOutputFormat(mProfile.fileFormat); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // Get the audio mime type
 | 
			
		
	
		
			
				
					|  |  |  |  |         // https://android.googlesource.com/platform/frameworks/av/+/master/media/libmediaplayerservice/StagefrightRecorder.cpp#1096
 | 
			
		
	
		
			
				
					|  |  |  |  |         // https://github.com/MrAlex94/Waterfox-Old/blob/master/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp
 | 
			
		
	
		
			
				
					|  |  |  |  |         String audioType; | 
			
		
	
		
			
				
					|  |  |  |  |         switch (mProfile.audioCodec) { | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.AudioEncoder.AMR_NB: audioType = "audio/3gpp"; break; | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.AudioEncoder.AMR_WB: audioType = "audio/amr-wb"; break; | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.AudioEncoder.AAC: | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.AudioEncoder.HE_AAC: | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.AudioEncoder.AAC_ELD: audioType = "audio/mp4a-latm"; break; | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.AudioEncoder.VORBIS: audioType = "audio/vorbis"; break; | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.AudioEncoder.DEFAULT: | 
			
		
	
		
			
				
					|  |  |  |  |             default: audioType = "audio/3gpp"; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // Get the video mime type
 | 
			
		
	
		
			
				
					|  |  |  |  |         // https://android.googlesource.com/platform/frameworks/av/+/master/media/libmediaplayerservice/StagefrightRecorder.cpp#1650
 | 
			
		
	
		
			
				
					|  |  |  |  |         // https://github.com/MrAlex94/Waterfox-Old/blob/master/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp
 | 
			
		
	
		
			
				
					|  |  |  |  |         String videoType; | 
			
		
	
		
			
				
					|  |  |  |  |         // 3. Set the output format. Before, change the profile data if the user
 | 
			
		
	
		
			
				
					|  |  |  |  |         // has specified a specific codec.
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (stub.videoCodec == VideoCodec.H_264) { | 
			
		
	
		
			
				
					|  |  |  |  |             mProfile.videoCodec = MediaRecorder.VideoEncoder.H264; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         if (stub.videoCodec == VideoCodec.H_263) { | 
			
		
	
		
			
				
					|  |  |  |  |             mProfile.fileFormat = MediaRecorder.OutputFormat.MPEG_4; | 
			
		
	
		
			
				
					|  |  |  |  |         } else if (stub.videoCodec == VideoCodec.H_263) { | 
			
		
	
		
			
				
					|  |  |  |  |             mProfile.videoCodec = MediaRecorder.VideoEncoder.H263; | 
			
		
	
		
			
				
					|  |  |  |  |             mProfile.fileFormat = MediaRecorder.OutputFormat.MPEG_4; // should work
 | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         switch (mProfile.videoCodec) { | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.VideoEncoder.H263: videoType = "video/3gpp"; break; | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.VideoEncoder.H264: videoType = "video/avc"; break; | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.VideoEncoder.MPEG_4_SP: videoType = "video/mp4v-es"; break; | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.VideoEncoder.VP8: videoType = "video/x-vnd.on2.vp8"; break; | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.VideoEncoder.HEVC: videoType = "video/hevc"; break; | 
			
		
	
		
			
				
					|  |  |  |  |             case MediaRecorder.VideoEncoder.DEFAULT: | 
			
		
	
		
			
				
					|  |  |  |  |             default: videoType = "video/avc"; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         mMediaRecorder.setOutputFormat(mProfile.fileFormat); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // Merge stub and profile
 | 
			
		
	
		
			
				
					|  |  |  |  |         stub.videoFrameRate = stub.videoFrameRate > 0 ? stub.videoFrameRate | 
			
		
	
		
			
				
					|  |  |  |  |                 : mProfile.videoFrameRate; | 
			
		
	
		
			
				
					|  |  |  |  |         stub.videoBitRate = stub.videoBitRate > 0 ? stub.videoBitRate : mProfile.videoBitRate; | 
			
		
	
		
			
				
					|  |  |  |  |         if (hasAudio) { | 
			
		
	
		
			
				
					|  |  |  |  |             stub.audioBitRate = stub.audioBitRate > 0 ? stub.audioBitRate : mProfile.audioBitRate; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         // 4. Update the VideoResult stub with information from the profile, if the
 | 
			
		
	
		
			
				
					|  |  |  |  |         // stub values are absent or incomplete
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (stub.videoFrameRate <= 0) stub.videoFrameRate = mProfile.videoFrameRate; | 
			
		
	
		
			
				
					|  |  |  |  |         if (stub.videoBitRate <= 0) stub.videoBitRate = mProfile.videoBitRate; | 
			
		
	
		
			
				
					|  |  |  |  |         if (stub.audioBitRate <= 0 && hasAudio) stub.audioBitRate = mProfile.audioBitRate; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // Check DeviceEncoders support
 | 
			
		
	
		
			
				
					|  |  |  |  |         boolean flip = stub.rotation % 180 != 0; | 
			
		
	
		
			
				
					|  |  |  |  |         if (flip) stub.size = stub.size.flip(); | 
			
		
	
		
			
				
					|  |  |  |  |         Size newVideoSize = null; | 
			
		
	
		
			
				
					|  |  |  |  |         int newVideoBitRate = 0; | 
			
		
	
		
			
				
					|  |  |  |  |         int newAudioBitRate = 0; | 
			
		
	
		
			
				
					|  |  |  |  |         int newVideoFrameRate = 0; | 
			
		
	
		
			
				
					|  |  |  |  |         int videoEncoderOffset = 0; | 
			
		
	
		
			
				
					|  |  |  |  |         int audioEncoderOffset = 0; | 
			
		
	
		
			
				
					|  |  |  |  |         boolean encodersFound = false; | 
			
		
	
		
			
				
					|  |  |  |  |         while (!encodersFound) { | 
			
		
	
		
			
				
					|  |  |  |  |             DeviceEncoders encoders = new DeviceEncoders(DeviceEncoders.MODE_RESPECT_ORDER, | 
			
		
	
		
			
				
					|  |  |  |  |                     videoType, audioType, videoEncoderOffset, audioEncoderOffset); | 
			
		
	
		
			
				
					|  |  |  |  |             try { | 
			
		
	
		
			
				
					|  |  |  |  |                 newVideoSize = encoders.getSupportedVideoSize(stub.size); | 
			
		
	
		
			
				
					|  |  |  |  |                 newVideoBitRate = encoders.getSupportedVideoBitRate(stub.videoBitRate); | 
			
		
	
		
			
				
					|  |  |  |  |                 newAudioBitRate = encoders.getSupportedAudioBitRate(stub.audioBitRate); | 
			
		
	
		
			
				
					|  |  |  |  |                 newVideoFrameRate = encoders.getSupportedVideoFrameRate(newVideoSize, | 
			
		
	
		
			
				
					|  |  |  |  |                         stub.videoFrameRate); | 
			
		
	
		
			
				
					|  |  |  |  |                 encodersFound = true; | 
			
		
	
		
			
				
					|  |  |  |  |             } catch (DeviceEncoders.VideoException videoException) { | 
			
		
	
		
			
				
					|  |  |  |  |                 videoEncoderOffset++; | 
			
		
	
		
			
				
					|  |  |  |  |             } catch (DeviceEncoders.AudioException audioException) { | 
			
		
	
		
			
				
					|  |  |  |  |                 audioEncoderOffset++; | 
			
		
	
		
			
				
					|  |  |  |  |         // 5. Update the VideoResult stub with DeviceEncoders constraints
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (applyEncodersConstraints) { | 
			
		
	
		
			
				
					|  |  |  |  |             // A. Get the audio mime type
 | 
			
		
	
		
			
				
					|  |  |  |  |             // https://android.googlesource.com/platform/frameworks/av/+/master/media/libmediaplayerservice/StagefrightRecorder.cpp#1096
 | 
			
		
	
		
			
				
					|  |  |  |  |             // https://github.com/MrAlex94/Waterfox-Old/blob/master/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp
 | 
			
		
	
		
			
				
					|  |  |  |  |             String audioType; | 
			
		
	
		
			
				
					|  |  |  |  |             switch (mProfile.audioCodec) { | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.AudioEncoder.AMR_NB: audioType = "audio/3gpp"; break; | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.AudioEncoder.AMR_WB: audioType = "audio/amr-wb"; break; | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.AudioEncoder.AAC: | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.AudioEncoder.HE_AAC: | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.AudioEncoder.AAC_ELD: audioType = "audio/mp4a-latm"; break; | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.AudioEncoder.VORBIS: audioType = "audio/vorbis"; break; | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.AudioEncoder.DEFAULT: | 
			
		
	
		
			
				
					|  |  |  |  |                 default: audioType = "audio/3gpp"; | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             // B. Get the video mime type
 | 
			
		
	
		
			
				
					|  |  |  |  |             // https://android.googlesource.com/platform/frameworks/av/+/master/media/libmediaplayerservice/StagefrightRecorder.cpp#1650
 | 
			
		
	
		
			
				
					|  |  |  |  |             // https://github.com/MrAlex94/Waterfox-Old/blob/master/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp
 | 
			
		
	
		
			
				
					|  |  |  |  |             String videoType; | 
			
		
	
		
			
				
					|  |  |  |  |             switch (mProfile.videoCodec) { | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.VideoEncoder.H263: videoType = "video/3gpp"; break; | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.VideoEncoder.H264: videoType = "video/avc"; break; | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.VideoEncoder.MPEG_4_SP: videoType = "video/mp4v-es"; break; | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.VideoEncoder.VP8: videoType = "video/x-vnd.on2.vp8"; break; | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.VideoEncoder.HEVC: videoType = "video/hevc"; break; | 
			
		
	
		
			
				
					|  |  |  |  |                 case MediaRecorder.VideoEncoder.DEFAULT: | 
			
		
	
		
			
				
					|  |  |  |  |                 default: videoType = "video/avc"; | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             // C. Check DeviceEncoders support
 | 
			
		
	
		
			
				
					|  |  |  |  |             boolean flip = stub.rotation % 180 != 0; | 
			
		
	
		
			
				
					|  |  |  |  |             if (flip) stub.size = stub.size.flip(); | 
			
		
	
		
			
				
					|  |  |  |  |             Size newVideoSize = null; | 
			
		
	
		
			
				
					|  |  |  |  |             int newVideoBitRate = 0; | 
			
		
	
		
			
				
					|  |  |  |  |             int newAudioBitRate = 0; | 
			
		
	
		
			
				
					|  |  |  |  |             int newVideoFrameRate = 0; | 
			
		
	
		
			
				
					|  |  |  |  |             int videoEncoderOffset = 0; | 
			
		
	
		
			
				
					|  |  |  |  |             int audioEncoderOffset = 0; | 
			
		
	
		
			
				
					|  |  |  |  |             boolean encodersFound = false; | 
			
		
	
		
			
				
					|  |  |  |  |             while (!encodersFound) { | 
			
		
	
		
			
				
					|  |  |  |  |                 LOG.i("prepareMediaRecorder:", "Checking DeviceEncoders...", | 
			
		
	
		
			
				
					|  |  |  |  |                         "videoOffset:", videoEncoderOffset, | 
			
		
	
		
			
				
					|  |  |  |  |                         "audioOffset:", audioEncoderOffset); | 
			
		
	
		
			
				
					|  |  |  |  |                 DeviceEncoders encoders = new DeviceEncoders(DeviceEncoders.MODE_RESPECT_ORDER, | 
			
		
	
		
			
				
					|  |  |  |  |                         videoType, audioType, videoEncoderOffset, audioEncoderOffset); | 
			
		
	
		
			
				
					|  |  |  |  |                 try { | 
			
		
	
		
			
				
					|  |  |  |  |                     newVideoSize = encoders.getSupportedVideoSize(stub.size); | 
			
		
	
		
			
				
					|  |  |  |  |                     newVideoBitRate = encoders.getSupportedVideoBitRate(stub.videoBitRate); | 
			
		
	
		
			
				
					|  |  |  |  |                     newAudioBitRate = encoders.getSupportedAudioBitRate(stub.audioBitRate); | 
			
		
	
		
			
				
					|  |  |  |  |                     newVideoFrameRate = encoders.getSupportedVideoFrameRate(newVideoSize, | 
			
		
	
		
			
				
					|  |  |  |  |                             stub.videoFrameRate); | 
			
		
	
		
			
				
					|  |  |  |  |                     encodersFound = true; | 
			
		
	
		
			
				
					|  |  |  |  |                 } catch (DeviceEncoders.VideoException videoException) { | 
			
		
	
		
			
				
					|  |  |  |  |                     videoEncoderOffset++; | 
			
		
	
		
			
				
					|  |  |  |  |                 } catch (DeviceEncoders.AudioException audioException) { | 
			
		
	
		
			
				
					|  |  |  |  |                     audioEncoderOffset++; | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             // D. Apply results
 | 
			
		
	
		
			
				
					|  |  |  |  |             stub.size = newVideoSize; | 
			
		
	
		
			
				
					|  |  |  |  |             stub.videoBitRate = newVideoBitRate; | 
			
		
	
		
			
				
					|  |  |  |  |             stub.audioBitRate = newAudioBitRate; | 
			
		
	
		
			
				
					|  |  |  |  |             stub.videoFrameRate = newVideoFrameRate; | 
			
		
	
		
			
				
					|  |  |  |  |             if (flip) stub.size = stub.size.flip(); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         stub.size = newVideoSize; | 
			
		
	
		
			
				
					|  |  |  |  |         stub.videoBitRate = newVideoBitRate; | 
			
		
	
		
			
				
					|  |  |  |  |         stub.audioBitRate = newAudioBitRate; | 
			
		
	
		
			
				
					|  |  |  |  |         stub.videoFrameRate = newVideoFrameRate; | 
			
		
	
		
			
				
					|  |  |  |  |         if (flip) stub.size = stub.size.flip(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // Set video params
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 6A. Configure MediaRecorder from stub and from profile (video)
 | 
			
		
	
		
			
				
					|  |  |  |  |         boolean flip = stub.rotation % 180 != 0; | 
			
		
	
		
			
				
					|  |  |  |  |         mMediaRecorder.setVideoSize( | 
			
		
	
		
			
				
					|  |  |  |  |                 flip ? stub.size.getHeight() : stub.size.getWidth(), | 
			
		
	
		
			
				
					|  |  |  |  |                 flip ? stub.size.getWidth() : stub.size.getHeight()); | 
			
		
	
	
		
			
				
					|  |  |  | @ -140,7 +181,7 @@ public abstract class FullVideoRecorder extends VideoRecorder { | 
			
		
	
		
			
				
					|  |  |  |  |         mMediaRecorder.setVideoEncoder(mProfile.videoCodec); | 
			
		
	
		
			
				
					|  |  |  |  |         mMediaRecorder.setVideoEncodingBitRate(stub.videoBitRate); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // Set audio params
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 6B. Configure MediaRecorder from stub and from profile (audio)
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (hasAudio) { | 
			
		
	
		
			
				
					|  |  |  |  |             if (stub.audio == Audio.ON) { | 
			
		
	
		
			
				
					|  |  |  |  |                 mMediaRecorder.setAudioChannels(mProfile.audioChannels); | 
			
		
	
	
		
			
				
					|  |  |  | @ -154,7 +195,7 @@ public abstract class FullVideoRecorder extends VideoRecorder { | 
			
		
	
		
			
				
					|  |  |  |  |             mMediaRecorder.setAudioEncodingBitRate(stub.audioBitRate); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // Set other params
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 7. Set other params
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (stub.location != null) { | 
			
		
	
		
			
				
					|  |  |  |  |             mMediaRecorder.setLocation( | 
			
		
	
		
			
				
					|  |  |  |  |                     (float) stub.location.getLatitude(), | 
			
		
	
	
		
			
				
					|  |  |  | @ -180,7 +221,7 @@ public abstract class FullVideoRecorder extends VideoRecorder { | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |         }); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // Prepare the Recorder
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 8. Prepare the Recorder
 | 
			
		
	
		
			
				
					|  |  |  |  |         try { | 
			
		
	
		
			
				
					|  |  |  |  |             mMediaRecorder.prepare(); | 
			
		
	
		
			
				
					|  |  |  |  |             mMediaRecorderPrepared = true; | 
			
		
	
	
		
			
				
					|  |  |  | @ -220,13 +261,15 @@ public abstract class FullVideoRecorder extends VideoRecorder { | 
			
		
	
		
			
				
					|  |  |  |  |             try { | 
			
		
	
		
			
				
					|  |  |  |  |                 mMediaRecorder.stop(); | 
			
		
	
		
			
				
					|  |  |  |  |             } catch (Exception e) { | 
			
		
	
		
			
				
					|  |  |  |  |                 LOG.w("stop:", "Error while closing media recorder.", e); | 
			
		
	
		
			
				
					|  |  |  |  |                 // This can happen if stopVideo() is called right after takeVideo()
 | 
			
		
	
		
			
				
					|  |  |  |  |                 // (in which case we don't care). Or when prepare()/start() have failed for
 | 
			
		
	
		
			
				
					|  |  |  |  |                 // some reason and we are not allowed to call stop.
 | 
			
		
	
		
			
				
					|  |  |  |  |                 // Make sure we don't override the error if one exists already.
 | 
			
		
	
		
			
				
					|  |  |  |  |                 mResult = null; | 
			
		
	
		
			
				
					|  |  |  |  |                 if (mError == null) mError = e; | 
			
		
	
		
			
				
					|  |  |  |  |                 if (mError == null) { | 
			
		
	
		
			
				
					|  |  |  |  |                     LOG.w("stop:", "Error while closing media recorder.", e); | 
			
		
	
		
			
				
					|  |  |  |  |                     mError = e; | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             mMediaRecorder.release(); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
	
		
			
				
					|  |  |  | 
 |