/* * Copyright (C) 2013-2019 Savoir-faire Linux Inc. * * Author: Guillaume Roguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "libav_deps.h" // MUST BE INCLUDED FIRST #include "libav_utils.h" #include "video_scaler.h" #include "media_buffer.h" #include "logger.h" #include namespace jami { namespace video { VideoScaler::VideoScaler() : ctx_(0), mode_(SWS_FAST_BILINEAR), tmp_data_() {} VideoScaler::~VideoScaler() { sws_freeContext(ctx_); } void VideoScaler::scale(const VideoFrame& input, VideoFrame& output) { const auto input_frame = input.pointer(); auto output_frame = output.pointer(); ctx_ = sws_getCachedContext(ctx_, input_frame->width, input_frame->height, (AVPixelFormat) input_frame->format, output_frame->width, output_frame->height, (AVPixelFormat) output_frame->format, mode_, NULL, NULL, NULL); if (!ctx_) { JAMI_ERR("Unable to create a scaler context"); return; } sws_scale(ctx_, input_frame->data, input_frame->linesize, 0, input_frame->height, output_frame->data, output_frame->linesize); } void VideoScaler::scale_with_aspect(const VideoFrame& input, VideoFrame& output) { if (input.width() == output.width() && input.height() == output.height()) { if (input.format() != output.format()) { auto outPtr = convertFormat(input, (AVPixelFormat)output.format()); output.copyFrom(*outPtr); } else { output.copyFrom(input); } } else { auto output_frame = output.pointer(); scale_and_pad(input, output, 0, 0, output_frame->width, output_frame->height, true); } } void VideoScaler::scale_and_pad(const VideoFrame& input, VideoFrame& output, unsigned xoff, unsigned yoff, unsigned dest_width, unsigned dest_height, bool keep_aspect) { const auto input_frame = input.pointer(); auto output_frame = output.pointer(); /* Correct destination width/height and offset if we need to keep input * frame aspect. */ if (keep_aspect) { const float local_ratio = (float)dest_width / dest_height; const float input_ratio = (float)input_frame->width / input_frame->height; if (local_ratio > input_ratio) { auto old_dest_width = dest_width; dest_width = dest_height * input_ratio; xoff += (old_dest_width - dest_width) / 2; } else { auto old_dest_heigth = dest_height; dest_height = dest_width / input_ratio; yoff += (old_dest_heigth - dest_height) / 2; } } // buffer overflow checks assert(xoff + dest_width <= (unsigned)output_frame->width); assert(yoff + dest_height <= (unsigned)output_frame->height); ctx_ = sws_getCachedContext(ctx_, input_frame->width, input_frame->height, (AVPixelFormat) input_frame->format, dest_width, dest_height, (AVPixelFormat) output_frame->format, mode_, NULL, NULL, NULL); if (!ctx_) { JAMI_ERR("Unable to create a scaler context"); return; } // Make an offset'ed copy of output data from xoff and yoff const auto out_desc = av_pix_fmt_desc_get((AVPixelFormat)output_frame->format); memset(tmp_data_, 0, sizeof(tmp_data_)); for (int i = 0; i < 4 && output_frame->linesize[i]; i++) { signed x_shift=xoff, y_shift=yoff; if (i == 1 || i == 2) { x_shift = -((-x_shift) >> out_desc->log2_chroma_w); y_shift = -((-y_shift) >> out_desc->log2_chroma_h); } auto x_step = out_desc->comp[i].step; tmp_data_[i] = output_frame->data[i] + y_shift * output_frame->linesize[i] + x_shift * x_step; } sws_scale(ctx_, input_frame->data, input_frame->linesize, 0, input_frame->height, tmp_data_, output_frame->linesize); } std::unique_ptr VideoScaler::convertFormat(const VideoFrame& input, AVPixelFormat pix) { auto output = std::make_unique(); output->reserve(pix, input.width(), input.height()); scale(input, *output); return output; } void VideoScaler::reset() { if (ctx_) { sws_freeContext(ctx_); ctx_ = nullptr; } } }} // namespace jami::video