| /* | 
 |  *  Copyright (c) 2012 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. | 
 |  */ | 
 |  | 
 | #include "modules/video_coding/codecs/i420/include/i420.h" | 
 |  | 
 | #include <limits> | 
 | #include <string> | 
 |  | 
 | #include "api/video/i420_buffer.h" | 
 | #include "common_video/libyuv/include/webrtc_libyuv.h" | 
 | #include "third_party/libyuv/include/libyuv.h" | 
 |  | 
 | namespace { | 
 | const size_t kI420HeaderSize = 4; | 
 | } | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | I420Encoder::I420Encoder() | 
 |     : _inited(false), _encodedImage(), _encodedCompleteCallback(NULL) {} | 
 |  | 
 | I420Encoder::~I420Encoder() { | 
 |   _inited = false; | 
 |   delete[] _encodedImage._buffer; | 
 | } | 
 |  | 
 | int I420Encoder::Release() { | 
 |   // Should allocate an encoded frame and then release it here, for that we | 
 |   // actually need an init flag. | 
 |   if (_encodedImage._buffer != NULL) { | 
 |     delete[] _encodedImage._buffer; | 
 |     _encodedImage._buffer = NULL; | 
 |   } | 
 |   _inited = false; | 
 |   return WEBRTC_VIDEO_CODEC_OK; | 
 | } | 
 |  | 
 | int I420Encoder::InitEncode(const VideoCodec* codecSettings, | 
 |                             int /*numberOfCores*/, | 
 |                             size_t /*maxPayloadSize */) { | 
 |   if (codecSettings == NULL) { | 
 |     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 
 |   } | 
 |   if (codecSettings->width < 1 || codecSettings->height < 1) { | 
 |     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 
 |   } | 
 |  | 
 |   // Allocating encoded memory. | 
 |   if (_encodedImage._buffer != NULL) { | 
 |     delete[] _encodedImage._buffer; | 
 |     _encodedImage._buffer = NULL; | 
 |     _encodedImage._size = 0; | 
 |   } | 
 |   const size_t newSize = CalcBufferSize(VideoType::kI420, codecSettings->width, | 
 |                                         codecSettings->height) + | 
 |                          kI420HeaderSize; | 
 |   uint8_t* newBuffer = new uint8_t[newSize]; | 
 |   if (newBuffer == NULL) { | 
 |     return WEBRTC_VIDEO_CODEC_MEMORY; | 
 |   } | 
 |   _encodedImage._size = newSize; | 
 |   _encodedImage._buffer = newBuffer; | 
 |  | 
 |   // If no memory allocation, no point to init. | 
 |   _inited = true; | 
 |   return WEBRTC_VIDEO_CODEC_OK; | 
 | } | 
 |  | 
 | int I420Encoder::Encode(const VideoFrame& inputImage, | 
 |                         const CodecSpecificInfo* /*codecSpecificInfo*/, | 
 |                         const std::vector<FrameType>* /*frame_types*/) { | 
 |   if (!_inited) { | 
 |     return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 
 |   } | 
 |   if (_encodedCompleteCallback == NULL) { | 
 |     return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 
 |   } | 
 |  | 
 |   _encodedImage._frameType = kVideoFrameKey; | 
 |   _encodedImage.SetTimestamp(inputImage.timestamp()); | 
 |   _encodedImage._encodedHeight = inputImage.height(); | 
 |   _encodedImage._encodedWidth = inputImage.width(); | 
 |  | 
 |   int width = inputImage.width(); | 
 |   if (width > std::numeric_limits<uint16_t>::max()) { | 
 |     return WEBRTC_VIDEO_CODEC_ERR_SIZE; | 
 |   } | 
 |   int height = inputImage.height(); | 
 |   if (height > std::numeric_limits<uint16_t>::max()) { | 
 |     return WEBRTC_VIDEO_CODEC_ERR_SIZE; | 
 |   } | 
 |  | 
 |   size_t req_length = CalcBufferSize(VideoType::kI420, inputImage.width(), | 
 |                                      inputImage.height()) + | 
 |                       kI420HeaderSize; | 
 |   if (_encodedImage._size > req_length) { | 
 |     // Reallocate buffer. | 
 |     delete[] _encodedImage._buffer; | 
 |  | 
 |     _encodedImage._buffer = new uint8_t[req_length]; | 
 |     _encodedImage._size = req_length; | 
 |   } | 
 |  | 
 |   uint8_t* buffer = _encodedImage._buffer; | 
 |  | 
 |   buffer = InsertHeader(buffer, width, height); | 
 |  | 
 |   int ret_length = | 
 |       ExtractBuffer(inputImage, req_length - kI420HeaderSize, buffer); | 
 |   if (ret_length < 0) | 
 |     return WEBRTC_VIDEO_CODEC_MEMORY; | 
 |   _encodedImage._length = ret_length + kI420HeaderSize; | 
 |  | 
 |   _encodedCompleteCallback->OnEncodedImage(_encodedImage, nullptr, nullptr); | 
 |  | 
 |   return WEBRTC_VIDEO_CODEC_OK; | 
 | } | 
 |  | 
 | uint8_t* I420Encoder::InsertHeader(uint8_t* buffer, | 
 |                                    uint16_t width, | 
 |                                    uint16_t height) { | 
 |   *buffer++ = static_cast<uint8_t>(width >> 8); | 
 |   *buffer++ = static_cast<uint8_t>(width & 0xFF); | 
 |   *buffer++ = static_cast<uint8_t>(height >> 8); | 
 |   *buffer++ = static_cast<uint8_t>(height & 0xFF); | 
 |   return buffer; | 
 | } | 
 |  | 
 | int I420Encoder::RegisterEncodeCompleteCallback( | 
 |     EncodedImageCallback* callback) { | 
 |   _encodedCompleteCallback = callback; | 
 |   return WEBRTC_VIDEO_CODEC_OK; | 
 | } | 
 |  | 
 | I420Decoder::I420Decoder() : _decodeCompleteCallback(NULL) {} | 
 |  | 
 | I420Decoder::~I420Decoder() { | 
 |   Release(); | 
 | } | 
 |  | 
 | int I420Decoder::InitDecode(const VideoCodec* codecSettings, | 
 |                             int /*numberOfCores */) { | 
 |   return WEBRTC_VIDEO_CODEC_OK; | 
 | } | 
 |  | 
 | int I420Decoder::Decode(const EncodedImage& inputImage, | 
 |                         bool /*missingFrames*/, | 
 |                         const CodecSpecificInfo* /*codecSpecificInfo*/, | 
 |                         int64_t /*renderTimeMs*/) { | 
 |   if (inputImage._buffer == NULL) { | 
 |     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 
 |   } | 
 |   if (_decodeCompleteCallback == NULL) { | 
 |     return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 
 |   } | 
 |   if (inputImage._length <= 0) { | 
 |     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 
 |   } | 
 |   if (inputImage._completeFrame == false) { | 
 |     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 
 |   } | 
 |   if (inputImage._length < kI420HeaderSize) { | 
 |     return WEBRTC_VIDEO_CODEC_ERROR; | 
 |   } | 
 |  | 
 |   const uint8_t* buffer = inputImage._buffer; | 
 |   uint16_t width, height; | 
 |   buffer = ExtractHeader(buffer, &width, &height); | 
 |  | 
 |   // Verify that the available length is sufficient: | 
 |   size_t req_length = | 
 |       CalcBufferSize(VideoType::kI420, width, height) + kI420HeaderSize; | 
 |  | 
 |   if (req_length > inputImage._length) { | 
 |     return WEBRTC_VIDEO_CODEC_ERROR; | 
 |   } | 
 |   // Set decoded image parameters. | 
 |   rtc::scoped_refptr<webrtc::I420Buffer> frame_buffer = | 
 |       I420Buffer::Create(width, height); | 
 |  | 
 |   // Converting from raw buffer I420Buffer. | 
 |   int y_stride = 16 * ((width + 15) / 16); | 
 |   int uv_stride = 16 * ((width + 31) / 32); | 
 |   int y_size = y_stride * height; | 
 |   int u_size = uv_stride * frame_buffer->ChromaHeight(); | 
 |   int ret = libyuv::I420Copy( | 
 |       buffer, y_stride, buffer + y_size, uv_stride, buffer + y_size + u_size, | 
 |       uv_stride, frame_buffer.get()->MutableDataY(), | 
 |       frame_buffer.get()->StrideY(), frame_buffer.get()->MutableDataU(), | 
 |       frame_buffer.get()->StrideU(), frame_buffer.get()->MutableDataV(), | 
 |       frame_buffer.get()->StrideV(), width, height); | 
 |   if (ret < 0) { | 
 |     return WEBRTC_VIDEO_CODEC_MEMORY; | 
 |   } | 
 |  | 
 |   VideoFrame decoded_image(frame_buffer, inputImage.Timestamp(), 0, | 
 |                            webrtc::kVideoRotation_0); | 
 |   _decodeCompleteCallback->Decoded(decoded_image); | 
 |   return WEBRTC_VIDEO_CODEC_OK; | 
 | } | 
 |  | 
 | const uint8_t* I420Decoder::ExtractHeader(const uint8_t* buffer, | 
 |                                           uint16_t* width, | 
 |                                           uint16_t* height) { | 
 |   *width = static_cast<uint16_t>(*buffer++) << 8; | 
 |   *width |= *buffer++; | 
 |   *height = static_cast<uint16_t>(*buffer++) << 8; | 
 |   *height |= *buffer++; | 
 |  | 
 |   return buffer; | 
 | } | 
 |  | 
 | int I420Decoder::RegisterDecodeCompleteCallback( | 
 |     DecodedImageCallback* callback) { | 
 |   _decodeCompleteCallback = callback; | 
 |   return WEBRTC_VIDEO_CODEC_OK; | 
 | } | 
 |  | 
 | int I420Decoder::Release() { | 
 |   return WEBRTC_VIDEO_CODEC_OK; | 
 | } | 
 | }  // namespace webrtc |