/* * Copyright 2017 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #import "RTCEncodedImage+Private.h" #import #include "rtc_base/numerics/safe_conversions.h" namespace { // An implementation of EncodedImageBufferInterface that doesn't perform any copies. class ObjCEncodedImageBuffer : public webrtc::EncodedImageBufferInterface { public: static rtc::scoped_refptr Create(NSData *data) { return rtc::make_ref_counted(data); } const uint8_t *data() const override { return static_cast(data_.bytes); } // TODO(bugs.webrtc.org/9378): delete this non-const data method. uint8_t *data() override { return const_cast(static_cast(data_.bytes)); } size_t size() const override { return data_.length; } protected: explicit ObjCEncodedImageBuffer(NSData *data) : data_(data) {} ~ObjCEncodedImageBuffer() {} NSData *data_; }; } // A simple wrapper around webrtc::EncodedImageBufferInterface to make it usable with associated // objects. @interface RTCWrappedEncodedImageBuffer : NSObject @property(nonatomic) rtc::scoped_refptr buffer; - (instancetype)initWithEncodedImageBuffer: (rtc::scoped_refptr)buffer; @end @implementation RTCWrappedEncodedImageBuffer @synthesize buffer = _buffer; - (instancetype)initWithEncodedImageBuffer: (rtc::scoped_refptr)buffer { self = [super init]; if (self) { _buffer = buffer; } return self; } @end @implementation RTC_OBJC_TYPE (RTCEncodedImage) (Private) - (rtc::scoped_refptr)encodedData { RTCWrappedEncodedImageBuffer *wrappedBuffer = objc_getAssociatedObject(self, @selector(encodedData)); return wrappedBuffer.buffer; } - (void)setEncodedData:(rtc::scoped_refptr)buffer { return objc_setAssociatedObject( self, @selector(encodedData), [[RTCWrappedEncodedImageBuffer alloc] initWithEncodedImageBuffer:buffer], OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (instancetype)initWithNativeEncodedImage:(const webrtc::EncodedImage &)encodedImage { if (self = [super init]) { // A reference to the encodedData must be stored so that it's kept alive as long // self.buffer references its underlying data. self.encodedData = encodedImage.GetEncodedData(); // Wrap the buffer in NSData without copying, do not take ownership. self.buffer = [NSData dataWithBytesNoCopy:self.encodedData->data() length:encodedImage.size() freeWhenDone:NO]; self.encodedWidth = rtc::dchecked_cast(encodedImage._encodedWidth); self.encodedHeight = rtc::dchecked_cast(encodedImage._encodedHeight); self.timeStamp = encodedImage.RtpTimestamp(); self.captureTimeMs = encodedImage.capture_time_ms_; self.ntpTimeMs = encodedImage.ntp_time_ms_; self.flags = encodedImage.timing_.flags; self.encodeStartMs = encodedImage.timing_.encode_start_ms; self.encodeFinishMs = encodedImage.timing_.encode_finish_ms; self.frameType = static_cast(encodedImage._frameType); self.rotation = static_cast(encodedImage.rotation_); self.qp = @(encodedImage.qp_); self.contentType = (encodedImage.content_type_ == webrtc::VideoContentType::SCREENSHARE) ? RTCVideoContentTypeScreenshare : RTCVideoContentTypeUnspecified; } return self; } - (webrtc::EncodedImage)nativeEncodedImage { // Return the pointer without copying. webrtc::EncodedImage encodedImage; if (self.encodedData) { encodedImage.SetEncodedData(self.encodedData); } else if (self.buffer) { encodedImage.SetEncodedData(ObjCEncodedImageBuffer::Create(self.buffer)); } encodedImage.set_size(self.buffer.length); encodedImage._encodedWidth = rtc::dchecked_cast(self.encodedWidth); encodedImage._encodedHeight = rtc::dchecked_cast(self.encodedHeight); encodedImage.SetRtpTimestamp(self.timeStamp); encodedImage.capture_time_ms_ = self.captureTimeMs; encodedImage.ntp_time_ms_ = self.ntpTimeMs; encodedImage.timing_.flags = self.flags; encodedImage.timing_.encode_start_ms = self.encodeStartMs; encodedImage.timing_.encode_finish_ms = self.encodeFinishMs; encodedImage._frameType = webrtc::VideoFrameType(self.frameType); encodedImage.rotation_ = webrtc::VideoRotation(self.rotation); encodedImage.qp_ = self.qp ? self.qp.intValue : -1; encodedImage.content_type_ = (self.contentType == RTCVideoContentTypeScreenshare) ? webrtc::VideoContentType::SCREENSHARE : webrtc::VideoContentType::UNSPECIFIED; return encodedImage; } @end