blob: 93204dde9ce72f1e04032757def1dae5f902d8a8 [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()
140 : _decodedImage(),
141 _width(0),
142 _height(0),
143 _inited(false),
144 _decodeCompleteCallback(NULL) {}
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000145
146I420Decoder::~I420Decoder() {
147 Release();
niklase@google.com470e71d2011-07-07 08:21:25 +0000148}
149
philipelcce46fc2015-12-21 03:04:49 -0800150int I420Decoder::InitDecode(const VideoCodec* codecSettings,
151 int /*numberOfCores */) {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000152 if (codecSettings == NULL) {
153 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
154 } else if (codecSettings->width < 1 || codecSettings->height < 1) {
155 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
156 }
157 _width = codecSettings->width;
158 _height = codecSettings->height;
159 _inited = true;
160 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000161}
162
philipelcce46fc2015-12-21 03:04:49 -0800163int I420Decoder::Decode(const EncodedImage& inputImage,
164 bool /*missingFrames*/,
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000165 const RTPFragmentationHeader* /*fragmentation*/,
166 const CodecSpecificInfo* /*codecSpecificInfo*/,
167 int64_t /*renderTimeMs*/) {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000168 if (inputImage._buffer == NULL) {
169 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
170 }
171 if (_decodeCompleteCallback == NULL) {
172 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
173 }
174 if (inputImage._length <= 0) {
175 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
176 }
mikhal@webrtc.org9e9cc722012-11-14 17:56:46 +0000177 if (inputImage._completeFrame == false) {
178 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
179 }
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000180 if (!_inited) {
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000181 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000182 }
pbos@webrtc.org86850902013-03-19 11:39:03 +0000183 if (inputImage._length < kI420HeaderSize) {
184 return WEBRTC_VIDEO_CODEC_ERROR;
185 }
186
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000187 const uint8_t* buffer = inputImage._buffer;
188 uint16_t width, height;
189
190 buffer = ExtractHeader(buffer, &width, &height);
191 _width = width;
192 _height = height;
niklase@google.com470e71d2011-07-07 08:21:25 +0000193
mikhal@webrtc.org9e9cc722012-11-14 17:56:46 +0000194 // Verify that the available length is sufficient:
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000195 size_t req_length = CalcBufferSize(kI420, _width, _height) + kI420HeaderSize;
pbos@webrtc.org86850902013-03-19 11:39:03 +0000196
197 if (req_length > inputImage._length) {
mikhal@webrtc.org9e9cc722012-11-14 17:56:46 +0000198 return WEBRTC_VIDEO_CODEC_ERROR;
199 }
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000200 // Set decoded image parameters.
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000201 int half_width = (_width + 1) / 2;
philipelcce46fc2015-12-21 03:04:49 -0800202 _decodedImage.CreateEmptyFrame(_width, _height, _width, half_width,
203 half_width);
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000204 // Converting from buffer to plane representation.
guoweis@webrtc.org59140d62015-03-09 17:07:31 +0000205 int ret = ConvertToI420(kI420, buffer, 0, 0, _width, _height, 0,
206 kVideoRotation_0, &_decodedImage);
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 }
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000210 _decodedImage.set_timestamp(inputImage._timeStamp);
niklase@google.com470e71d2011-07-07 08:21:25 +0000211
magjed@webrtc.org2056ee32015-03-16 13:46:52 +0000212 _decodeCompleteCallback->Decoded(_decodedImage);
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000213 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000214}
215
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000216const uint8_t* I420Decoder::ExtractHeader(const uint8_t* buffer,
philipelcce46fc2015-12-21 03:04:49 -0800217 uint16_t* width,
218 uint16_t* height) {
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000219 *width = static_cast<uint16_t>(*buffer++) << 8;
220 *width |= *buffer++;
221 *height = static_cast<uint16_t>(*buffer++) << 8;
222 *height |= *buffer++;
223
224 return buffer;
225}
226
227int I420Decoder::RegisterDecodeCompleteCallback(
228 DecodedImageCallback* callback) {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000229 _decodeCompleteCallback = callback;
230 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000231}
232
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000233int I420Decoder::Release() {
mikhal@webrtc.orgb95e9ca2012-07-10 20:58:08 +0000234 _inited = false;
235 return WEBRTC_VIDEO_CODEC_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000236}
pbos@webrtc.org8911ce42013-03-18 16:39:03 +0000237} // namespace webrtc