Index: webrtc/sdk/objc/Framework/Classes/avfoundationvideocapturer.mm |
diff --git a/webrtc/sdk/objc/Framework/Classes/avfoundationvideocapturer.mm b/webrtc/sdk/objc/Framework/Classes/avfoundationvideocapturer.mm |
index ddeedb5ab6ffe19aedcc48627b571bd59880cc7d..fd608fe59072b813f7a0d8579ac79989dc0c2120 100644 |
--- a/webrtc/sdk/objc/Framework/Classes/avfoundationvideocapturer.mm |
+++ b/webrtc/sdk/objc/Framework/Classes/avfoundationvideocapturer.mm |
@@ -22,10 +22,13 @@ |
#import "WebRTC/UIDevice+RTCDevice.h" |
#endif |
+#include "libyuv/rotate.h" |
+ |
#include "webrtc/base/bind.h" |
#include "webrtc/base/checks.h" |
#include "webrtc/base/thread.h" |
#include "webrtc/common_video/include/corevideo_frame_buffer.h" |
+#include "webrtc/common_video/rotation.h" |
struct AVCaptureSessionPresetResolution { |
NSString *sessionPreset; |
@@ -98,7 +101,7 @@ static NSString *GetSessionPresetForVideoFormat( |
AVCaptureVideoDataOutput *_videoDataOutput; |
// The cricket::VideoCapturer that owns this class. Should never be NULL. |
webrtc::AVFoundationVideoCapturer *_capturer; |
- BOOL _orientationHasChanged; |
+ webrtc::VideoRotation _rotation; |
BOOL _hasRetriedOnFatalError; |
BOOL _isRunning; |
BOOL _hasStarted; |
@@ -228,7 +231,14 @@ static NSString *GetSessionPresetForVideoFormat( |
self.hasStarted = YES; |
[RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession |
block:^{ |
- _orientationHasChanged = NO; |
+#if TARGET_OS_IPHONE |
+ // Default to portrait orientation on iPhone. This will be reset in |
+ // updateOrientation unless orientation is unknown/faceup/facedown. |
+ _rotation = webrtc::kVideoRotation_90; |
+#else |
+ // No rotation on Mac. |
+ _rotation = webrtc::kVideoRotation_0; |
+#endif |
[self updateOrientation]; |
#if TARGET_OS_IPHONE |
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; |
@@ -263,7 +273,6 @@ static NSString *GetSessionPresetForVideoFormat( |
- (void)deviceOrientationDidChange:(NSNotification *)notification { |
[RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession |
block:^{ |
- _orientationHasChanged = YES; |
[self updateOrientation]; |
}]; |
} |
@@ -278,7 +287,7 @@ static NSString *GetSessionPresetForVideoFormat( |
if (!self.hasStarted) { |
return; |
} |
- _capturer->CaptureSampleBuffer(sampleBuffer); |
+ _capturer->CaptureSampleBuffer(sampleBuffer, _rotation); |
} |
- (void)captureOutput:(AVCaptureOutput *)captureOutput |
@@ -510,36 +519,26 @@ static NSString *GetSessionPresetForVideoFormat( |
// Called from capture session queue. |
- (void)updateOrientation { |
- AVCaptureConnection *connection = |
- [_videoDataOutput connectionWithMediaType:AVMediaTypeVideo]; |
- if (!connection.supportsVideoOrientation) { |
- // TODO(tkchin): set rotation bit on frames. |
- return; |
- } |
#if TARGET_OS_IPHONE |
- AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationPortrait; |
switch ([UIDevice currentDevice].orientation) { |
case UIDeviceOrientationPortrait: |
- orientation = AVCaptureVideoOrientationPortrait; |
+ _rotation = webrtc::kVideoRotation_90; |
break; |
case UIDeviceOrientationPortraitUpsideDown: |
- orientation = AVCaptureVideoOrientationPortraitUpsideDown; |
+ _rotation = webrtc::kVideoRotation_270; |
break; |
case UIDeviceOrientationLandscapeLeft: |
- orientation = AVCaptureVideoOrientationLandscapeRight; |
+ _rotation = webrtc::kVideoRotation_180; |
break; |
case UIDeviceOrientationLandscapeRight: |
- orientation = AVCaptureVideoOrientationLandscapeLeft; |
+ _rotation = webrtc::kVideoRotation_0; |
break; |
case UIDeviceOrientationFaceUp: |
case UIDeviceOrientationFaceDown: |
case UIDeviceOrientationUnknown: |
- if (!_orientationHasChanged) { |
- connection.videoOrientation = orientation; |
- } |
- return; |
+ // Ignore. |
+ break; |
} |
- connection.videoOrientation = orientation; |
#endif |
} |
@@ -578,9 +577,12 @@ enum AVFoundationVideoCapturerMessageType : uint32_t { |
}; |
struct AVFoundationFrame { |
- AVFoundationFrame(CVImageBufferRef buffer, int64_t time) |
- : image_buffer(buffer), capture_time(time) {} |
+ AVFoundationFrame(CVImageBufferRef buffer, |
+ webrtc::VideoRotation rotation, |
+ int64_t time) |
+ : image_buffer(buffer), rotation(rotation), capture_time(time) {} |
CVImageBufferRef image_buffer; |
+ webrtc::VideoRotation rotation; |
int64_t capture_time; |
}; |
@@ -688,14 +690,14 @@ bool AVFoundationVideoCapturer::GetUseBackCamera() const { |
} |
void AVFoundationVideoCapturer::CaptureSampleBuffer( |
- CMSampleBufferRef sampleBuffer) { |
- if (CMSampleBufferGetNumSamples(sampleBuffer) != 1 || |
- !CMSampleBufferIsValid(sampleBuffer) || |
- !CMSampleBufferDataIsReady(sampleBuffer)) { |
+ CMSampleBufferRef sample_buffer, webrtc::VideoRotation rotation) { |
+ if (CMSampleBufferGetNumSamples(sample_buffer) != 1 || |
+ !CMSampleBufferIsValid(sample_buffer) || |
+ !CMSampleBufferDataIsReady(sample_buffer)) { |
return; |
} |
- CVImageBufferRef image_buffer = CMSampleBufferGetImageBuffer(sampleBuffer); |
+ CVImageBufferRef image_buffer = CMSampleBufferGetImageBuffer(sample_buffer); |
if (image_buffer == NULL) { |
return; |
} |
@@ -703,7 +705,7 @@ void AVFoundationVideoCapturer::CaptureSampleBuffer( |
// Retain the buffer and post it to the webrtc thread. It will be released |
// after it has successfully been signaled. |
CVBufferRetain(image_buffer); |
- AVFoundationFrame frame(image_buffer, rtc::TimeNanos()); |
+ AVFoundationFrame frame(image_buffer, rotation, rtc::TimeNanos()); |
_startThread->Post(RTC_FROM_HERE, this, kMessageTypeFrame, |
new rtc::TypedMessageData<AVFoundationFrame>(frame)); |
} |
@@ -714,7 +716,7 @@ void AVFoundationVideoCapturer::OnMessage(rtc::Message *msg) { |
rtc::TypedMessageData<AVFoundationFrame>* data = |
static_cast<rtc::TypedMessageData<AVFoundationFrame>*>(msg->pdata); |
const AVFoundationFrame& frame = data->data(); |
- OnFrameMessage(frame.image_buffer, frame.capture_time); |
+ OnFrameMessage(frame.image_buffer, frame.rotation, frame.capture_time); |
delete data; |
break; |
} |
@@ -722,6 +724,7 @@ void AVFoundationVideoCapturer::OnMessage(rtc::Message *msg) { |
} |
void AVFoundationVideoCapturer::OnFrameMessage(CVImageBufferRef image_buffer, |
+ webrtc::VideoRotation rotation, |
int64_t capture_time_ns) { |
RTC_DCHECK(_startThread->IsCurrent()); |
@@ -749,16 +752,33 @@ void AVFoundationVideoCapturer::OnFrameMessage(CVImageBufferRef image_buffer, |
} |
if (adapted_width != captured_width || crop_width != captured_width || |
- adapted_height != captured_height || crop_height != captured_height) { |
+ adapted_height != captured_height || crop_height != captured_height || |
+ (apply_rotation() && rotation != webrtc::kVideoRotation_0)) { |
// TODO(magjed): Avoid converting to I420. |
rtc::scoped_refptr<webrtc::I420Buffer> scaled_buffer( |
_buffer_pool.CreateBuffer(adapted_width, adapted_height)); |
scaled_buffer->CropAndScaleFrom(buffer->NativeToI420Buffer(), crop_x, |
crop_y, crop_width, crop_height); |
- buffer = scaled_buffer; |
+ if (!apply_rotation() || rotation == webrtc::kVideoRotation_0) { |
+ buffer = scaled_buffer; |
+ } else { |
+ // Applying rotation is only supported for legacy reasons and performance |
+ // is not critical here. |
+ buffer = (rotation == webrtc::kVideoRotation_180) |
+ ? I420Buffer::Create(adapted_width, adapted_height) |
+ : I420Buffer::Create(adapted_height, adapted_width); |
+ libyuv::I420Rotate(scaled_buffer->DataY(), scaled_buffer->StrideY(), |
+ scaled_buffer->DataU(), scaled_buffer->StrideU(), |
+ scaled_buffer->DataV(), scaled_buffer->StrideV(), |
+ buffer->MutableDataY(), buffer->StrideY(), |
+ buffer->MutableDataU(), buffer->StrideU(), |
+ buffer->MutableDataV(), buffer->StrideV(), |
+ crop_width, crop_height, |
+ static_cast<libyuv::RotationMode>(rotation)); |
+ } |
} |
- OnFrame(cricket::WebRtcVideoFrame(buffer, webrtc::kVideoRotation_0, |
+ OnFrame(cricket::WebRtcVideoFrame(buffer, rotation, |
translated_camera_time_us, 0), |
captured_width, captured_height); |