diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index 13793fb06b..43ed6ae3be 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -1063,6 +1063,7 @@ if (is_ios || is_mac) { "objc/unittests/ObjCVideoTrackSource_xctest.mm", "objc/unittests/RTCCVPixelBuffer_xctest.mm", "objc/unittests/RTCCallbackLogger_xctest.m", + "objc/unittests/RTCEncodedImage_xctest.mm", "objc/unittests/RTCFileVideoCapturer_xctest.mm", "objc/unittests/RTCH264ProfileLevelId_xctest.m", "objc/unittests/RTCNV12TextureCache_xctest.m", @@ -1084,6 +1085,7 @@ if (is_ios || is_mac) { deps = [ ":audio_device", ":audio_session_objc", + ":base_native_additions_objc", ":base_objc", ":callback_logger_objc", ":framework_objc", diff --git a/sdk/objc/api/peerconnection/RTCEncodedImage+Private.mm b/sdk/objc/api/peerconnection/RTCEncodedImage+Private.mm index f1df13e554..0b2b64dce9 100644 --- a/sdk/objc/api/peerconnection/RTCEncodedImage+Private.mm +++ b/sdk/objc/api/peerconnection/RTCEncodedImage+Private.mm @@ -13,6 +13,29 @@ #import #include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/ref_counted_object.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 new rtc::RefCountedObject(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. @@ -51,9 +74,12 @@ - (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:encodedImage.mutable_data() - length:encodedImage.size() + self.buffer = [NSData dataWithBytesNoCopy:self.encodedData->data() + length:self.encodedData->size() freeWhenDone:NO]; self.encodedWidth = rtc::dchecked_cast(encodedImage._encodedWidth); self.encodedHeight = rtc::dchecked_cast(encodedImage._encodedHeight); @@ -77,8 +103,13 @@ - (webrtc::EncodedImage)nativeEncodedImage { // Return the pointer without copying. - webrtc::EncodedImage encodedImage( - (uint8_t *)self.buffer.bytes, (size_t)self.buffer.length, (size_t)self.buffer.length); + 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.SetTimestamp(self.timeStamp); diff --git a/sdk/objc/unittests/RTCEncodedImage_xctest.mm b/sdk/objc/unittests/RTCEncodedImage_xctest.mm new file mode 100644 index 0000000000..577ecda2ff --- /dev/null +++ b/sdk/objc/unittests/RTCEncodedImage_xctest.mm @@ -0,0 +1,54 @@ +/* + * Copyright 2020 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 "api/peerconnection/RTCEncodedImage+Private.h" + +#import + +@interface RTCEncodedImageTests : XCTestCase +@end + +@implementation RTCEncodedImageTests + +- (void)testInitializedWithNativeEncodedImage { + const auto encoded_data = webrtc::EncodedImageBuffer::Create(); + webrtc::EncodedImage encoded_image; + encoded_image.SetEncodedData(encoded_data); + + RTCEncodedImage *encodedImage = + [[RTCEncodedImage alloc] initWithNativeEncodedImage:encoded_image]; + + XCTAssertEqual([encodedImage nativeEncodedImage].GetEncodedData(), encoded_data); +} + +- (void)testInitWithNSData { + NSData *bufferData = [NSData data]; + RTCEncodedImage *encodedImage = [[RTCEncodedImage alloc] init]; + encodedImage.buffer = bufferData; + + webrtc::EncodedImage result_encoded_image = [encodedImage nativeEncodedImage]; + XCTAssertTrue(result_encoded_image.GetEncodedData() != nullptr); + XCTAssertEqual(result_encoded_image.GetEncodedData()->data(), bufferData.bytes); +} + +- (void)testRetainsNativeEncodedImage { + RTCEncodedImage *encodedImage; + { + const auto encoded_data = webrtc::EncodedImageBuffer::Create(); + webrtc::EncodedImage encoded_image; + encoded_image.SetEncodedData(encoded_data); + encodedImage = [[RTCEncodedImage alloc] initWithNativeEncodedImage:encoded_image]; + } + webrtc::EncodedImage result_encoded_image = [encodedImage nativeEncodedImage]; + XCTAssertTrue(result_encoded_image.GetEncodedData() != nullptr); + XCTAssertTrue(result_encoded_image.GetEncodedData()->data() != nullptr); +} + +@end diff --git a/sdk/objc/unittests/objc_video_decoder_factory_tests.mm b/sdk/objc/unittests/objc_video_decoder_factory_tests.mm index 2246eaaf5c..bd31a6eb0d 100644 --- a/sdk/objc/unittests/objc_video_decoder_factory_tests.mm +++ b/sdk/objc/unittests/objc_video_decoder_factory_tests.mm @@ -70,6 +70,7 @@ TEST(ObjCVideoDecoderFactoryTest, DecodeReturnsOKOnSuccess) { std::unique_ptr decoder = GetObjCDecoder(CreateOKDecoderFactory()); webrtc::EncodedImage encoded_image; + encoded_image.SetEncodedData(webrtc::EncodedImageBuffer::Create()); EXPECT_EQ(decoder->Decode(encoded_image, false, 0), WEBRTC_VIDEO_CODEC_OK); } @@ -78,6 +79,7 @@ TEST(ObjCVideoDecoderFactoryTest, DecodeReturnsErrorOnFail) { std::unique_ptr decoder = GetObjCDecoder(CreateErrorDecoderFactory()); webrtc::EncodedImage encoded_image; + encoded_image.SetEncodedData(webrtc::EncodedImageBuffer::Create()); EXPECT_EQ(decoder->Decode(encoded_image, false, 0), WEBRTC_VIDEO_CODEC_ERROR); }