| /* | |
| * 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 <objc/runtime.h> | |
| #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 webrtc::scoped_refptr<ObjCEncodedImageBuffer> Create(NSData *data) { | |
| return webrtc::make_ref_counted<ObjCEncodedImageBuffer>(data); | |
| } | |
| const uint8_t *data() const override { | |
| return static_cast<const uint8_t *>(data_.bytes); | |
| } | |
| size_t size() const override { return data_.length; } | |
| protected: | |
| explicit ObjCEncodedImageBuffer(NSData *data) : data_(data) {} | |
| ~ObjCEncodedImageBuffer() {} | |
| NSData *data_; | |
| }; | |
| } // namespace | |
| // A simple wrapper around webrtc::EncodedImageBufferInterface to make it usable | |
| // with associated objects. | |
| @interface RTCWrappedEncodedImageBuffer : NSObject | |
| @property(nonatomic) webrtc::scoped_refptr<webrtc::EncodedImageBufferInterface> | |
| buffer; | |
| - (instancetype)initWithEncodedImageBuffer: | |
| (webrtc::scoped_refptr<webrtc::EncodedImageBufferInterface>)buffer; | |
| @end | |
| @implementation RTCWrappedEncodedImageBuffer | |
| @synthesize buffer = _buffer; | |
| - (instancetype)initWithEncodedImageBuffer: | |
| (webrtc::scoped_refptr<webrtc::EncodedImageBufferInterface>)buffer { | |
| self = [super init]; | |
| if (self) { | |
| _buffer = buffer; | |
| } | |
| return self; | |
| } | |
| @end | |
| @implementation RTC_OBJC_TYPE (RTCEncodedImage) | |
| (Private) | |
| - (webrtc::scoped_refptr<webrtc::EncodedImageBufferInterface>)encodedData { | |
| RTCWrappedEncodedImageBuffer *wrappedBuffer = | |
| objc_getAssociatedObject(self, @selector(encodedData)); | |
| return wrappedBuffer.buffer; | |
| } | |
| - (void)setEncodedData: | |
| (webrtc::scoped_refptr<webrtc::EncodedImageBufferInterface>)buffer { | |
| return objc_setAssociatedObject( | |
| self, | |
| @selector(encodedData), | |
| [[RTCWrappedEncodedImageBuffer alloc] initWithEncodedImageBuffer:buffer], | |
| OBJC_ASSOCIATION_RETAIN_NONATOMIC); | |
| } | |
| - (instancetype)initWithNativeEncodedImage: | |
| (const webrtc::EncodedImage &)encodedImage { | |
| self = [super init]; | |
| if (self) { | |
| // 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. | |
| // `NSData` provides interface that allows to write into the buffer, | |
| // `EncodedImageBufferInterface` gives read-only access to the buffer. | |
| // As of now write part of ths NSData interface are not used when accessing | |
| // `buffer`, however it might be safer to refactor RTCEncodedImage not | |
| // to expose buffer as NSData, and then remove this unsafe const_cast. | |
| self.buffer = [NSData | |
| dataWithBytesNoCopy:const_cast<uint8_t *>(self.encodedData->data()) | |
| length:encodedImage.size() | |
| freeWhenDone:NO]; | |
| self.encodedWidth = | |
| webrtc::dchecked_cast<int32_t>(encodedImage._encodedWidth); | |
| self.encodedHeight = | |
| webrtc::dchecked_cast<int32_t>(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<RTCFrameType>(encodedImage.frame_type()); | |
| self.rotation = static_cast<RTCVideoRotation>(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 = | |
| webrtc::dchecked_cast<uint32_t>(self.encodedWidth); | |
| encodedImage._encodedHeight = | |
| webrtc::dchecked_cast<uint32_t>(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.set_frame_type(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 |