1 /*
2  *  Copyright (c) 2012 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 "webrtc/modules/video_coding/codecs/i420/include/i420.h"
12 
13 #include <limits>
14 #include <string>
15 
16 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
17 
18 namespace {
19 const size_t kI420HeaderSize = 4;
20 }
21 
22 namespace webrtc {
23 
I420Encoder()24 I420Encoder::I420Encoder()
25     : _inited(false), _encodedImage(), _encodedCompleteCallback(NULL) {}
26 
~I420Encoder()27 I420Encoder::~I420Encoder() {
28   _inited = false;
29   delete[] _encodedImage._buffer;
30 }
31 
Release()32 int I420Encoder::Release() {
33   // Should allocate an encoded frame and then release it here, for that we
34   // actually need an init flag.
35   if (_encodedImage._buffer != NULL) {
36     delete[] _encodedImage._buffer;
37     _encodedImage._buffer = NULL;
38   }
39   _inited = false;
40   return WEBRTC_VIDEO_CODEC_OK;
41 }
42 
InitEncode(const VideoCodec * codecSettings,int,size_t)43 int I420Encoder::InitEncode(const VideoCodec* codecSettings,
44                             int /*numberOfCores*/,
45                             size_t /*maxPayloadSize */) {
46   if (codecSettings == NULL) {
47     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
48   }
49   if (codecSettings->width < 1 || codecSettings->height < 1) {
50     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
51   }
52 
53   // Allocating encoded memory.
54   if (_encodedImage._buffer != NULL) {
55     delete[] _encodedImage._buffer;
56     _encodedImage._buffer = NULL;
57     _encodedImage._size = 0;
58   }
59   const size_t newSize =
60       CalcBufferSize(kI420, codecSettings->width, codecSettings->height) +
61       kI420HeaderSize;
62   uint8_t* newBuffer = new uint8_t[newSize];
63   if (newBuffer == NULL) {
64     return WEBRTC_VIDEO_CODEC_MEMORY;
65   }
66   _encodedImage._size = newSize;
67   _encodedImage._buffer = newBuffer;
68 
69   // If no memory allocation, no point to init.
70   _inited = true;
71   return WEBRTC_VIDEO_CODEC_OK;
72 }
73 
Encode(const VideoFrame & inputImage,const CodecSpecificInfo *,const std::vector<FrameType> *)74 int I420Encoder::Encode(const VideoFrame& inputImage,
75                         const CodecSpecificInfo* /*codecSpecificInfo*/,
76                         const std::vector<FrameType>* /*frame_types*/) {
77   if (!_inited) {
78     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
79   }
80   if (_encodedCompleteCallback == NULL) {
81     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
82   }
83 
84   _encodedImage._frameType = kVideoFrameKey;
85   _encodedImage._timeStamp = inputImage.timestamp();
86   _encodedImage._encodedHeight = inputImage.height();
87   _encodedImage._encodedWidth = inputImage.width();
88 
89   int width = inputImage.width();
90   if (width > std::numeric_limits<uint16_t>::max()) {
91     return WEBRTC_VIDEO_CODEC_ERR_SIZE;
92   }
93   int height = inputImage.height();
94   if (height > std::numeric_limits<uint16_t>::max()) {
95     return WEBRTC_VIDEO_CODEC_ERR_SIZE;
96   }
97 
98   size_t req_length =
99       CalcBufferSize(kI420, inputImage.width(), inputImage.height()) +
100       kI420HeaderSize;
101   if (_encodedImage._size > req_length) {
102     // Reallocate buffer.
103     delete[] _encodedImage._buffer;
104 
105     _encodedImage._buffer = new uint8_t[req_length];
106     _encodedImage._size = req_length;
107   }
108 
109   uint8_t* buffer = _encodedImage._buffer;
110 
111   buffer = InsertHeader(buffer, width, height);
112 
113   int ret_length =
114       ExtractBuffer(inputImage, req_length - kI420HeaderSize, buffer);
115   if (ret_length < 0)
116     return WEBRTC_VIDEO_CODEC_MEMORY;
117   _encodedImage._length = ret_length + kI420HeaderSize;
118 
119   _encodedCompleteCallback->Encoded(_encodedImage, NULL, NULL);
120   return WEBRTC_VIDEO_CODEC_OK;
121 }
122 
InsertHeader(uint8_t * buffer,uint16_t width,uint16_t height)123 uint8_t* I420Encoder::InsertHeader(uint8_t* buffer,
124                                    uint16_t width,
125                                    uint16_t height) {
126   *buffer++ = static_cast<uint8_t>(width >> 8);
127   *buffer++ = static_cast<uint8_t>(width & 0xFF);
128   *buffer++ = static_cast<uint8_t>(height >> 8);
129   *buffer++ = static_cast<uint8_t>(height & 0xFF);
130   return buffer;
131 }
132 
RegisterEncodeCompleteCallback(EncodedImageCallback * callback)133 int I420Encoder::RegisterEncodeCompleteCallback(
134     EncodedImageCallback* callback) {
135   _encodedCompleteCallback = callback;
136   return WEBRTC_VIDEO_CODEC_OK;
137 }
138 
I420Decoder()139 I420Decoder::I420Decoder()
140     : _decodedImage(),
141       _width(0),
142       _height(0),
143       _inited(false),
144       _decodeCompleteCallback(NULL) {}
145 
~I420Decoder()146 I420Decoder::~I420Decoder() {
147   Release();
148 }
149 
Reset()150 int I420Decoder::Reset() {
151   return WEBRTC_VIDEO_CODEC_OK;
152 }
153 
InitDecode(const VideoCodec * codecSettings,int)154 int I420Decoder::InitDecode(const VideoCodec* codecSettings,
155                             int /*numberOfCores */) {
156   if (codecSettings == NULL) {
157     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
158   } else if (codecSettings->width < 1 || codecSettings->height < 1) {
159     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
160   }
161   _width = codecSettings->width;
162   _height = codecSettings->height;
163   _inited = true;
164   return WEBRTC_VIDEO_CODEC_OK;
165 }
166 
Decode(const EncodedImage & inputImage,bool,const RTPFragmentationHeader *,const CodecSpecificInfo *,int64_t)167 int I420Decoder::Decode(const EncodedImage& inputImage,
168                         bool /*missingFrames*/,
169                         const RTPFragmentationHeader* /*fragmentation*/,
170                         const CodecSpecificInfo* /*codecSpecificInfo*/,
171                         int64_t /*renderTimeMs*/) {
172   if (inputImage._buffer == NULL) {
173     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
174   }
175   if (_decodeCompleteCallback == NULL) {
176     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
177   }
178   if (inputImage._length <= 0) {
179     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
180   }
181   if (inputImage._completeFrame == false) {
182     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
183   }
184   if (!_inited) {
185     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
186   }
187   if (inputImage._length < kI420HeaderSize) {
188     return WEBRTC_VIDEO_CODEC_ERROR;
189   }
190 
191   const uint8_t* buffer = inputImage._buffer;
192   uint16_t width, height;
193 
194   buffer = ExtractHeader(buffer, &width, &height);
195   _width = width;
196   _height = height;
197 
198   // Verify that the available length is sufficient:
199   size_t req_length = CalcBufferSize(kI420, _width, _height) + kI420HeaderSize;
200 
201   if (req_length > inputImage._length) {
202     return WEBRTC_VIDEO_CODEC_ERROR;
203   }
204   // Set decoded image parameters.
205   int half_width = (_width + 1) / 2;
206   _decodedImage.CreateEmptyFrame(_width, _height, _width, half_width,
207                                  half_width);
208   // Converting from buffer to plane representation.
209   int ret = ConvertToI420(kI420, buffer, 0, 0, _width, _height, 0,
210                           kVideoRotation_0, &_decodedImage);
211   if (ret < 0) {
212     return WEBRTC_VIDEO_CODEC_MEMORY;
213   }
214   _decodedImage.set_timestamp(inputImage._timeStamp);
215 
216   _decodeCompleteCallback->Decoded(_decodedImage);
217   return WEBRTC_VIDEO_CODEC_OK;
218 }
219 
ExtractHeader(const uint8_t * buffer,uint16_t * width,uint16_t * height)220 const uint8_t* I420Decoder::ExtractHeader(const uint8_t* buffer,
221                                           uint16_t* width,
222                                           uint16_t* height) {
223   *width = static_cast<uint16_t>(*buffer++) << 8;
224   *width |= *buffer++;
225   *height = static_cast<uint16_t>(*buffer++) << 8;
226   *height |= *buffer++;
227 
228   return buffer;
229 }
230 
RegisterDecodeCompleteCallback(DecodedImageCallback * callback)231 int I420Decoder::RegisterDecodeCompleteCallback(
232     DecodedImageCallback* callback) {
233   _decodeCompleteCallback = callback;
234   return WEBRTC_VIDEO_CODEC_OK;
235 }
236 
Release()237 int I420Decoder::Release() {
238   _inited = false;
239   return WEBRTC_VIDEO_CODEC_OK;
240 }
241 }  // namespace webrtc
242