1/*
2 *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "sdk/objc/native/src/objc_video_track_source.h"
12
13#import "base/RTCVideoFrame.h"
14#import "base/RTCVideoFrameBuffer.h"
15#import "components/video_frame_buffer/RTCCVPixelBuffer.h"
16
17#include "api/video/i420_buffer.h"
18#include "sdk/objc/native/src/objc_frame_buffer.h"
19
20@interface RTCObjCVideoSourceAdapter ()
21@property(nonatomic) webrtc::ObjCVideoTrackSource *objCVideoTrackSource;
22@end
23
24@implementation RTCObjCVideoSourceAdapter
25
26@synthesize objCVideoTrackSource = _objCVideoTrackSource;
27
28- (void)capturer:(RTC_OBJC_TYPE(RTCVideoCapturer) *)capturer
29    didCaptureVideoFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame {
30  _objCVideoTrackSource->OnCapturedFrame(frame);
31}
32
33@end
34
35namespace webrtc {
36
37ObjCVideoTrackSource::ObjCVideoTrackSource()
38    : AdaptedVideoTrackSource(/* required resolution alignment */ 2) {}
39
40ObjCVideoTrackSource::ObjCVideoTrackSource(RTCObjCVideoSourceAdapter *adapter) : adapter_(adapter) {
41  adapter_.objCVideoTrackSource = this;
42}
43
44bool ObjCVideoTrackSource::is_screencast() const {
45  return false;
46}
47
48absl::optional<bool> ObjCVideoTrackSource::needs_denoising() const {
49  return false;
50}
51
52MediaSourceInterface::SourceState ObjCVideoTrackSource::state() const {
53  return SourceState::kLive;
54}
55
56bool ObjCVideoTrackSource::remote() const {
57  return false;
58}
59
60void ObjCVideoTrackSource::OnOutputFormatRequest(int width, int height, int fps) {
61  cricket::VideoFormat format(width, height, cricket::VideoFormat::FpsToInterval(fps), 0);
62  video_adapter()->OnOutputFormatRequest(format);
63}
64
65void ObjCVideoTrackSource::OnCapturedFrame(RTC_OBJC_TYPE(RTCVideoFrame) * frame) {
66  const int64_t timestamp_us = frame.timeStampNs / rtc::kNumNanosecsPerMicrosec;
67  const int64_t translated_timestamp_us =
68      timestamp_aligner_.TranslateTimestamp(timestamp_us, rtc::TimeMicros());
69
70  int adapted_width;
71  int adapted_height;
72  int crop_width;
73  int crop_height;
74  int crop_x;
75  int crop_y;
76  if (!AdaptFrame(frame.width,
77                  frame.height,
78                  timestamp_us,
79                  &adapted_width,
80                  &adapted_height,
81                  &crop_width,
82                  &crop_height,
83                  &crop_x,
84                  &crop_y)) {
85    return;
86  }
87
88  rtc::scoped_refptr<VideoFrameBuffer> buffer;
89  if (adapted_width == frame.width && adapted_height == frame.height) {
90    // No adaption - optimized path.
91    buffer = new rtc::RefCountedObject<ObjCFrameBuffer>(frame.buffer);
92  } else if ([frame.buffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]) {
93    // Adapted CVPixelBuffer frame.
94    RTC_OBJC_TYPE(RTCCVPixelBuffer) *rtcPixelBuffer =
95        (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)frame.buffer;
96    buffer = new rtc::RefCountedObject<ObjCFrameBuffer>([[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc]
97        initWithPixelBuffer:rtcPixelBuffer.pixelBuffer
98               adaptedWidth:adapted_width
99              adaptedHeight:adapted_height
100                  cropWidth:crop_width
101                 cropHeight:crop_height
102                      cropX:crop_x + rtcPixelBuffer.cropX
103                      cropY:crop_y + rtcPixelBuffer.cropY]);
104  } else {
105    // Adapted I420 frame.
106    // TODO(magjed): Optimize this I420 path.
107    rtc::scoped_refptr<I420Buffer> i420_buffer = I420Buffer::Create(adapted_width, adapted_height);
108    buffer = new rtc::RefCountedObject<ObjCFrameBuffer>(frame.buffer);
109    i420_buffer->CropAndScaleFrom(*buffer->ToI420(), crop_x, crop_y, crop_width, crop_height);
110    buffer = i420_buffer;
111  }
112
113  // Applying rotation is only supported for legacy reasons and performance is
114  // not critical here.
115  VideoRotation rotation = static_cast<VideoRotation>(frame.rotation);
116  if (apply_rotation() && rotation != kVideoRotation_0) {
117    buffer = I420Buffer::Rotate(*buffer->ToI420(), rotation);
118    rotation = kVideoRotation_0;
119  }
120
121  OnFrame(VideoFrame::Builder()
122              .set_video_frame_buffer(buffer)
123              .set_rotation(rotation)
124              .set_timestamp_us(translated_timestamp_us)
125              .build());
126}
127
128}  // namespace webrtc
129