/* * Copyright (c) 2011 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 "spatial_resampler.h" namespace webrtc { VPMSimpleSpatialResampler::VPMSimpleSpatialResampler() : _resamplingMode(kFastRescaling), _targetWidth(0), _targetHeight(0), _interpolatorPtr(NULL) { } VPMSimpleSpatialResampler::~VPMSimpleSpatialResampler() { Release(); } WebRtc_Word32 VPMSimpleSpatialResampler::Release() { if (_interpolatorPtr != NULL) { delete _interpolatorPtr; _interpolatorPtr = NULL; } return VPM_OK; } WebRtc_Word32 VPMSimpleSpatialResampler::SetTargetFrameSize(WebRtc_UWord32 width, WebRtc_UWord32 height) { if (_resamplingMode == kNoRescaling) { return VPM_OK; } if (width < 1 || height < 1) { return VPM_PARAMETER_ERROR; } _targetWidth = width; _targetHeight = height; return VPM_OK; } void VPMSimpleSpatialResampler::SetInputFrameResampleMode(VideoFrameResampling resamplingMode) { _resamplingMode = resamplingMode; } void VPMSimpleSpatialResampler::Reset() { _resamplingMode = kFastRescaling; _targetWidth = 0; _targetHeight = 0; } WebRtc_Word32 VPMSimpleSpatialResampler::ResampleFrame(const VideoFrame& inFrame, VideoFrame& outFrame) { WebRtc_Word32 ret; if (_resamplingMode == kNoRescaling) { return outFrame.CopyFrame(inFrame); } else if (_targetWidth < 1 || _targetHeight < 1) { return VPM_PARAMETER_ERROR; } // Check if re-sampling is needed if ((inFrame.Width() == _targetWidth) && (inFrame.Height() == _targetHeight)) { return outFrame.CopyFrame(inFrame); } if (_resamplingMode == kBiLinear) { return BiLinearInterpolation(inFrame, outFrame); } outFrame.SetTimeStamp(inFrame.TimeStamp()); WebRtc_UWord32 currentLength = inFrame.Width() * inFrame.Height() * 3 / 2; if (_targetWidth > inFrame.Width() && ( ExactMultiplier(inFrame.Width(), inFrame.Height()))) { // The codec might want to pad this later... adding 8 pixels const WebRtc_UWord32 requiredSize = (_targetWidth + 8) * (_targetHeight + 8) * 3 / 2; outFrame.VerifyAndAllocate(requiredSize); return UpsampleFrame(inFrame, outFrame); } else { // 1 cut/pad // 2 scale factor 2X (in both cases if required) WebRtc_UWord32 croppedWidth = inFrame.Width(); WebRtc_UWord32 croppedHeight = inFrame.Height(); //Calculates cropped dimensions CropSize(inFrame.Width(), inFrame.Height(), croppedWidth, croppedHeight); VideoFrame* targetFrame; outFrame.VerifyAndAllocate(croppedWidth * croppedHeight * 3 / 2); targetFrame = &outFrame; ConvertI420ToI420(inFrame.Buffer(), inFrame.Width(), inFrame.Height(), targetFrame->Buffer(), croppedWidth, croppedHeight); targetFrame->SetWidth(croppedWidth); targetFrame->SetHeight(croppedHeight); //We have correct aspect ratio, sub-sample with a multiple of two to get //close to the target size ret = SubsampleMultipleOf2(*targetFrame); if (ret != VPM_OK) { return ret; } } return VPM_OK; } WebRtc_Word32 VPMSimpleSpatialResampler::UpsampleFrame(const VideoFrame& inFrame, VideoFrame& outFrame) { outFrame.CopyFrame(inFrame); WebRtc_UWord32 currentLength = inFrame.Width() * inFrame.Height() * 3 / 2; float ratioWidth = _targetWidth / (float)inFrame.Width(); float ratioHeight = _targetHeight / (float)inFrame.Height(); WebRtc_UWord32 scaledWidth = 0; WebRtc_UWord32 scaledHeight = 0; bool scaled = true; if(ratioWidth > 1 || ratioHeight > 1) { // scale up if(ratioWidth <= 1.5 && ratioHeight <= 1.5) { // scale up 1.5 currentLength = ScaleI420Up3_2(inFrame.Width(), inFrame.Height(), outFrame.Buffer(), outFrame.Size(), scaledWidth, scaledHeight); } else if(ratioWidth <= 2 && ratioHeight <= 2) { // scale up 2 currentLength = ScaleI420Up2(inFrame.Width(), inFrame.Height(), outFrame.Buffer(), outFrame.Size(), scaledWidth, scaledHeight); } else if(ratioWidth <= 2.25 && ratioHeight <= 2.25) { // scale up 2.25 currentLength = ScaleI420Up3_2(inFrame.Width(), inFrame.Height(), outFrame.Buffer(), outFrame.Size(), scaledWidth, scaledHeight); currentLength = ScaleI420Up3_2(scaledWidth, scaledHeight, outFrame.Buffer(), outFrame.Size(), scaledWidth, scaledHeight); } else if(ratioWidth <= 3 && ratioHeight <= 3) { // scale up 3 currentLength = ScaleI420Up2(inFrame.Width(), inFrame.Height(), outFrame.Buffer(), outFrame.Size(), scaledWidth, scaledHeight); currentLength = ScaleI420Up3_2(scaledWidth, scaledHeight, outFrame.Buffer(), outFrame.Size(), scaledWidth, scaledHeight); } else if(ratioWidth <= 4 && ratioHeight <= 4) { // scale up 4 currentLength = ScaleI420Up2(inFrame.Width(), inFrame.Height(), outFrame.Buffer(), outFrame.Size(), scaledWidth, scaledHeight); currentLength = ScaleI420Up2(scaledWidth, scaledHeight, outFrame.Buffer(), outFrame.Size(), scaledWidth, scaledHeight); } //TODO: what if ratioWidth/Height >= 8 ? if (scaledWidth <= 0 || scaledHeight <= 0) { return VPM_GENERAL_ERROR; } if ((static_cast(scaledWidth) > _targetWidth) || (static_cast(scaledHeight) > _targetHeight)) { currentLength = CutI420Frame(outFrame.Buffer(), scaledWidth, scaledHeight, _targetWidth, _targetHeight); } } else { return VPM_GENERAL_ERROR; } outFrame.SetWidth(_targetWidth); outFrame.SetHeight(_targetHeight); outFrame.SetLength(_targetWidth * _targetHeight * 3 / 2); return VPM_OK; } WebRtc_Word32 VPMSimpleSpatialResampler::CropSize(WebRtc_UWord32 width, WebRtc_UWord32 height, WebRtc_UWord32& croppedWidth, WebRtc_UWord32& croppedHeight) const { // Crop the image to a width and height which is a // multiple of two, so that we can do a simpler scaling. croppedWidth = _targetWidth; croppedHeight = _targetHeight; if (width >= 8 * _targetWidth && height >= 8 * _targetHeight) { croppedWidth = 8 * _targetWidth; croppedHeight = 8 * _targetHeight; } else if (width >= 4 * _targetWidth && height >= 4 * _targetHeight) { croppedWidth = 4 * _targetWidth; croppedHeight = 4 * _targetHeight; } else if (width >= 2 * _targetWidth && height >= 2 * _targetHeight) { croppedWidth = 2 * _targetWidth; croppedHeight = 2 * _targetHeight; } return VPM_OK; } WebRtc_Word32 VPMSimpleSpatialResampler::SubsampleMultipleOf2(VideoFrame& frame) { WebRtc_UWord32 tempWidth = frame.Width(); WebRtc_UWord32 tempHeight = frame.Height(); while (tempWidth / _targetWidth >= 2 && tempHeight / _targetHeight >= 2) { ScaleI420FrameQuarter(tempWidth, tempHeight, frame.Buffer()); tempWidth /= 2; tempHeight /= 2; } frame.SetWidth(tempWidth); frame.SetHeight(tempHeight); frame.SetLength(frame.Width() * frame.Height() * 3 / 2); return VPM_OK; } bool VPMSimpleSpatialResampler::ExactMultiplier(WebRtc_UWord32 width, WebRtc_UWord32 height) const { bool exactMultiplier = false; if (_targetWidth % width == 0 && _targetHeight % height == 0) { // we have a multiple, is it an even multiple? WebRtc_Word32 widthMultiple = _targetWidth / width; WebRtc_Word32 heightMultiple = _targetHeight / height; if (widthMultiple == 2 && heightMultiple == 2 || widthMultiple == 4 && heightMultiple == 4 || widthMultiple == 8 && heightMultiple == 8 || widthMultiple == 1 && heightMultiple == 1) { exactMultiplier = true; } } return exactMultiplier; } WebRtc_Word32 VPMSimpleSpatialResampler::BiLinearInterpolation(const VideoFrame& inFrame, VideoFrame& outFrame) { WebRtc_Word32 retVal; if (_interpolatorPtr == NULL) { _interpolatorPtr = new interpolator(); if (_interpolatorPtr == NULL) { return VPM_MEMORY; } } // set bi-linear interpolator retVal = _interpolatorPtr->Set(inFrame.Width(), inFrame.Height(), _targetWidth, _targetHeight, kI420, kI420, kBilinear ); if (retVal < 0 ) { return retVal; } //Verify size of output buffer outFrame.VerifyAndAllocate(_targetHeight * _targetWidth * 3 / 2); //interpolate frame retVal = _interpolatorPtr->Interpolate(inFrame.Buffer(), outFrame.Buffer()); //returns height if (retVal < 0) return retVal; //Set output frame parameters outFrame.SetHeight(_targetHeight); outFrame.SetWidth(_targetWidth); outFrame.SetLength(_targetHeight * _targetWidth * 3 / 2); outFrame.SetTimeStamp(inFrame.TimeStamp()); return VPM_OK; } WebRtc_UWord32 VPMSimpleSpatialResampler::TargetHeight() { return _targetHeight; } WebRtc_UWord32 VPMSimpleSpatialResampler::TargetWidth() { return _targetWidth; } } //namespace