webrtc/modules/video_processing/main/source/spatial_resampler.cc

356 lines
11 KiB
C++
Raw Normal View History

/*
* 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<WebRtc_UWord32>(scaledWidth) > _targetWidth) ||
(static_cast<WebRtc_UWord32>(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