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
18namespace {
19const size_t kI420HeaderSize = 4;
20}
21
22namespace webrtc {
23
24I420Encoder::I420Encoder()
25    : _inited(false), _encodedImage(), _encodedCompleteCallback(NULL) {}
26
27I420Encoder::~I420Encoder() {
28  _inited = false;
29  delete[] _encodedImage._buffer;
30}
31
32int 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
43int 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
74int 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
123uint8_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
133int I420Encoder::RegisterEncodeCompleteCallback(
134    EncodedImageCallback* callback) {
135  _encodedCompleteCallback = callback;
136  return WEBRTC_VIDEO_CODEC_OK;
137}
138
139I420Decoder::I420Decoder()
140    : _decodedImage(),
141      _width(0),
142      _height(0),
143      _inited(false),
144      _decodeCompleteCallback(NULL) {}
145
146I420Decoder::~I420Decoder() {
147  Release();
148}
149
150int I420Decoder::Reset() {
151  return WEBRTC_VIDEO_CODEC_OK;
152}
153
154int 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
167int 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
220const 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
231int I420Decoder::RegisterDecodeCompleteCallback(
232    DecodedImageCallback* callback) {
233  _decodeCompleteCallback = callback;
234  return WEBRTC_VIDEO_CODEC_OK;
235}
236
237int I420Decoder::Release() {
238  _inited = false;
239  return WEBRTC_VIDEO_CODEC_OK;
240}
241}  // namespace webrtc
242