blob: d0c8d0c62c97113090ee35aea284a6098b4c4f58 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
pwestin@webrtc.orgfdf21c82012-02-02 12:46:58 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
Henrik Kjellandera74c08d2015-10-22 12:23:11 +020011#include "webrtc/modules/video_coding/codecs/i420/include/i420.h"
mikhal@webrtc.org538f0ab2012-07-11 18:20:39 +000012
pbos@webrtc.org8911ce42013-03-18 16:39:03 +000013#include <limits>
pbos@webrtc.orga4407322013-07-16 12:32:05 +000014#include <string>
niklase@google.com470e71d2011-07-07 08:21:25 +000015
pbos@webrtc.orga4407322013-07-16 12:32:05 +000016#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
mikhal@webrtc.org538f0ab2012-07-11 18:20:39 +000017
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000018namespace {
19const size_t kI420HeaderSize = 4;
20}
21
pbos@webrtc.org8911ce42013-03-18 16:39:03 +000022namespace webrtc {
niklase@google.com470e71d2011-07-07 08:21:25 +000023
philipelcce46fc2015-12-21 03:04:49 -080024I420Encoder::I420Encoder()
25 : _inited(false), _encodedImage(), _encodedCompleteCallback(NULL) {}
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +000026
27I420Encoder::~I420Encoder() {
28 _inited = false;
philipelcce46fc2015-12-21 03:04:49 -080029 delete[] _encodedImage._buffer;
niklase@google.com470e71d2011-07-07 08:21:25 +000030}
31
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +000032int 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) {
philipelcce46fc2015-12-21 03:04:49 -080036 delete[] _encodedImage._buffer;
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +000037 _encodedImage._buffer = NULL;
38 }
39 _inited = false;
40 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +000041}
42
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +000043int I420Encoder::InitEncode(const VideoCodec* codecSettings,
mikhal@webrtc.orga2031d52012-07-31 15:53:44 +000044 int /*numberOfCores*/,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000045 size_t /*maxPayloadSize */) {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +000046 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) {
philipelcce46fc2015-12-21 03:04:49 -080055 delete[] _encodedImage._buffer;
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +000056 _encodedImage._buffer = NULL;
57 _encodedImage._size = 0;
58 }
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000059 const size_t newSize =
60 CalcBufferSize(kI420, codecSettings->width, codecSettings->height) +
61 kI420HeaderSize;
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +000062 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;
niklase@google.com470e71d2011-07-07 08:21:25 +000072}
73
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -070074int I420Encoder::Encode(const VideoFrame& inputImage,
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +000075 const CodecSpecificInfo* /*codecSpecificInfo*/,
pbos22993e12015-10-19 02:39:06 -070076 const std::vector<FrameType>* /*frame_types*/) {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +000077 if (!_inited) {
78 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
79 }
80 if (_encodedCompleteCallback == NULL) {
81 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
82 }
83
Peter Boström49e196a2015-10-23 15:58:18 +020084 _encodedImage._frameType = kVideoFrameKey;
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +000085 _encodedImage._timeStamp = inputImage.timestamp();
86 _encodedImage._encodedHeight = inputImage.height();
87 _encodedImage._encodedWidth = inputImage.width();
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +000088
pbos@webrtc.org8911ce42013-03-18 16:39:03 +000089 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;
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +000096 }
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +000097
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000098 size_t req_length =
99 CalcBufferSize(kI420, inputImage.width(), inputImage.height()) +
100 kI420HeaderSize;
101 if (_encodedImage._size > req_length) {
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000102 // Reallocate buffer.
philipelcce46fc2015-12-21 03:04:49 -0800103 delete[] _encodedImage._buffer;
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000104
105 _encodedImage._buffer = new uint8_t[req_length];
106 _encodedImage._size = req_length;
107 }
108
philipelcce46fc2015-12-21 03:04:49 -0800109 uint8_t* buffer = _encodedImage._buffer;
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000110
111 buffer = InsertHeader(buffer, width, height);
112
philipelcce46fc2015-12-21 03:04:49 -0800113 int ret_length =
114 ExtractBuffer(inputImage, req_length - kI420HeaderSize, buffer);
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000115 if (ret_length < 0)
116 return WEBRTC_VIDEO_CODEC_MEMORY;
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000117 _encodedImage._length = ret_length + kI420HeaderSize;
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000118
changbin.shao@webrtc.orgf31f56d2015-02-09 09:14:03 +0000119 _encodedCompleteCallback->Encoded(_encodedImage, NULL, NULL);
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000120 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000121}
122
philipelcce46fc2015-12-21 03:04:49 -0800123uint8_t* I420Encoder::InsertHeader(uint8_t* buffer,
124 uint16_t width,
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000125 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}
niklase@google.com470e71d2011-07-07 08:21:25 +0000132
philipelcce46fc2015-12-21 03:04:49 -0800133int I420Encoder::RegisterEncodeCompleteCallback(
134 EncodedImageCallback* callback) {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000135 _encodedCompleteCallback = callback;
136 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000137}
138
philipelcce46fc2015-12-21 03:04:49 -0800139I420Decoder::I420Decoder()
nisse64ec8f82016-09-27 00:17:25 -0700140 : _width(0),
philipelcce46fc2015-12-21 03:04:49 -0800141 _height(0),
142 _inited(false),
143 _decodeCompleteCallback(NULL) {}
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000144
145I420Decoder::~I420Decoder() {
146 Release();
niklase@google.com470e71d2011-07-07 08:21:25 +0000147}
148
philipelcce46fc2015-12-21 03:04:49 -0800149int I420Decoder::InitDecode(const VideoCodec* codecSettings,
150 int /*numberOfCores */) {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000151 if (codecSettings == NULL) {
152 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
153 } else if (codecSettings->width < 1 || codecSettings->height < 1) {
154 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
155 }
156 _width = codecSettings->width;
157 _height = codecSettings->height;
158 _inited = true;
159 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000160}
161
philipelcce46fc2015-12-21 03:04:49 -0800162int I420Decoder::Decode(const EncodedImage& inputImage,
163 bool /*missingFrames*/,
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000164 const RTPFragmentationHeader* /*fragmentation*/,
165 const CodecSpecificInfo* /*codecSpecificInfo*/,
166 int64_t /*renderTimeMs*/) {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000167 if (inputImage._buffer == NULL) {
168 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
169 }
170 if (_decodeCompleteCallback == NULL) {
171 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
172 }
173 if (inputImage._length <= 0) {
174 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
175 }
mikhal@webrtc.org9e9cc722012-11-14 17:56:46 +0000176 if (inputImage._completeFrame == false) {
177 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
178 }
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000179 if (!_inited) {
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000180 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000181 }
pbos@webrtc.org86850902013-03-19 11:39:03 +0000182 if (inputImage._length < kI420HeaderSize) {
183 return WEBRTC_VIDEO_CODEC_ERROR;
184 }
185
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000186 const uint8_t* buffer = inputImage._buffer;
187 uint16_t width, height;
188
189 buffer = ExtractHeader(buffer, &width, &height);
190 _width = width;
191 _height = height;
niklase@google.com470e71d2011-07-07 08:21:25 +0000192
mikhal@webrtc.org9e9cc722012-11-14 17:56:46 +0000193 // Verify that the available length is sufficient:
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000194 size_t req_length = CalcBufferSize(kI420, _width, _height) + kI420HeaderSize;
pbos@webrtc.org86850902013-03-19 11:39:03 +0000195
196 if (req_length > inputImage._length) {
mikhal@webrtc.org9e9cc722012-11-14 17:56:46 +0000197 return WEBRTC_VIDEO_CODEC_ERROR;
198 }
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000199 // Set decoded image parameters.
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000200 int half_width = (_width + 1) / 2;
nisse64ec8f82016-09-27 00:17:25 -0700201 rtc::scoped_refptr<webrtc::I420Buffer> frame_buffer =
202 I420Buffer::Create(_width, _height, _width, half_width, half_width);
203
204 // Converting from raw buffer I420Buffer.
guoweis@webrtc.org59140d62015-03-09 17:07:31 +0000205 int ret = ConvertToI420(kI420, buffer, 0, 0, _width, _height, 0,
nisse64ec8f82016-09-27 00:17:25 -0700206 kVideoRotation_0, frame_buffer.get());
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000207 if (ret < 0) {
mikhal@webrtc.orga2031d52012-07-31 15:53:44 +0000208 return WEBRTC_VIDEO_CODEC_MEMORY;
209 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000210
nisse64ec8f82016-09-27 00:17:25 -0700211 VideoFrame decoded_image(frame_buffer, inputImage._timeStamp, 0,
212 webrtc::kVideoRotation_0);
213 _decodeCompleteCallback->Decoded(decoded_image);
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000214 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000215}
216
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000217const uint8_t* I420Decoder::ExtractHeader(const uint8_t* buffer,
philipelcce46fc2015-12-21 03:04:49 -0800218 uint16_t* width,
219 uint16_t* height) {
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000220 *width = static_cast<uint16_t>(*buffer++) << 8;
221 *width |= *buffer++;
222 *height = static_cast<uint16_t>(*buffer++) << 8;
223 *height |= *buffer++;
224
225 return buffer;
226}
227
228int I420Decoder::RegisterDecodeCompleteCallback(
229 DecodedImageCallback* callback) {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000230 _decodeCompleteCallback = callback;
231 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000232}
233
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000234int I420Decoder::Release() {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000235 _inited = false;
236 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000237}
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000238} // namespace webrtc