Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc.
3 : *
4 : * Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com>
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, write to the Free Software
18 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 : */
20 :
21 : #include "libav_deps.h" // MUST BE INCLUDED FIRST
22 : #include "libav_utils.h"
23 : #include "video_scaler.h"
24 : #include "media_buffer.h"
25 : #include "logger.h"
26 :
27 : #include <cassert>
28 :
29 : namespace jami {
30 : namespace video {
31 :
32 628 : VideoScaler::VideoScaler()
33 628 : : ctx_(0)
34 628 : , mode_(SWS_FAST_BILINEAR)
35 3140 : , tmp_data_()
36 628 : {}
37 :
38 628 : VideoScaler::~VideoScaler()
39 : {
40 628 : sws_freeContext(ctx_);
41 628 : }
42 :
43 : void
44 5654 : VideoScaler::scale(const VideoFrame& input, VideoFrame& output){
45 5654 : scale(input.pointer(), output.pointer());
46 5654 : }
47 :
48 : void
49 5654 : VideoScaler::scale(const AVFrame* input_frame, AVFrame* output_frame)
50 : {
51 11308 : ctx_ = sws_getCachedContext(ctx_,
52 5654 : input_frame->width,
53 5654 : input_frame->height,
54 5654 : (AVPixelFormat) input_frame->format,
55 : output_frame->width,
56 : output_frame->height,
57 5654 : (AVPixelFormat) output_frame->format,
58 : mode_,
59 : NULL,
60 : NULL,
61 : NULL);
62 5654 : if (!ctx_) {
63 0 : JAMI_ERR("Unable to create a scaler context");
64 0 : return;
65 : }
66 :
67 5654 : sws_scale(ctx_,
68 5654 : input_frame->data,
69 5654 : input_frame->linesize,
70 : 0,
71 5654 : input_frame->height,
72 5654 : output_frame->data,
73 5654 : output_frame->linesize);
74 : }
75 :
76 : void
77 5653 : VideoScaler::scale_with_aspect(const VideoFrame& input, VideoFrame& output)
78 : {
79 5653 : if (input.width() == output.width() && input.height() == output.height()) {
80 5652 : if (input.format() != output.format()) {
81 5652 : auto outPtr = convertFormat(input, (AVPixelFormat) output.format());
82 5652 : output.copyFrom(*outPtr);
83 5652 : } else {
84 0 : output.copyFrom(input);
85 : }
86 : } else {
87 1 : auto output_frame = output.pointer();
88 1 : scale_and_pad(input, output, 0, 0, output_frame->width, output_frame->height, true);
89 : }
90 5653 : }
91 :
92 : void
93 1 : VideoScaler::scale_and_pad(const VideoFrame& input,
94 : VideoFrame& output,
95 : unsigned xoff,
96 : unsigned yoff,
97 : unsigned dest_width,
98 : unsigned dest_height,
99 : bool keep_aspect)
100 : {
101 1 : const auto input_frame = input.pointer();
102 1 : auto output_frame = output.pointer();
103 :
104 : /* Correct destination width/height and offset if we need to keep input
105 : * frame aspect.
106 : */
107 1 : if (keep_aspect) {
108 1 : const float local_ratio = (float) dest_width / dest_height;
109 1 : const float input_ratio = (float) input_frame->width / input_frame->height;
110 :
111 1 : if (local_ratio > input_ratio) {
112 1 : auto old_dest_width = dest_width;
113 1 : dest_width = dest_height * input_ratio;
114 1 : xoff += (old_dest_width - dest_width) / 2;
115 : } else {
116 0 : auto old_dest_heigth = dest_height;
117 0 : dest_height = dest_width / input_ratio;
118 0 : yoff += (old_dest_heigth - dest_height) / 2;
119 : }
120 : }
121 :
122 : // buffer overflow checks
123 1 : if ((xoff + dest_width > (unsigned) output_frame->width)
124 1 : || (yoff + dest_height > (unsigned) output_frame->height)) {
125 0 : JAMI_ERR("Unable to scale video");
126 0 : return;
127 : }
128 :
129 2 : ctx_ = sws_getCachedContext(ctx_,
130 1 : input_frame->width,
131 1 : input_frame->height,
132 1 : (AVPixelFormat) input_frame->format,
133 : dest_width,
134 : dest_height,
135 1 : (AVPixelFormat) output_frame->format,
136 : mode_,
137 : NULL,
138 : NULL,
139 : NULL);
140 1 : if (!ctx_) {
141 0 : JAMI_ERR("Unable to create a scaler context");
142 0 : return;
143 : }
144 :
145 : // Make an offset'ed copy of output data from xoff and yoff
146 1 : const auto out_desc = av_pix_fmt_desc_get((AVPixelFormat) output_frame->format);
147 1 : memset(tmp_data_, 0, sizeof(tmp_data_));
148 4 : for (int i = 0; i < 4 && output_frame->linesize[i]; i++) {
149 3 : signed x_shift = xoff, y_shift = yoff;
150 3 : if (i == 1 || i == 2) {
151 2 : x_shift = -((-x_shift) >> out_desc->log2_chroma_w);
152 2 : y_shift = -((-y_shift) >> out_desc->log2_chroma_h);
153 : }
154 3 : auto x_step = out_desc->comp[i].step;
155 3 : tmp_data_[i] = output_frame->data[i] + y_shift * output_frame->linesize[i]
156 3 : + x_shift * x_step;
157 : }
158 :
159 1 : sws_scale(ctx_,
160 1 : input_frame->data,
161 1 : input_frame->linesize,
162 : 0,
163 1 : input_frame->height,
164 1 : tmp_data_,
165 1 : output_frame->linesize);
166 : }
167 :
168 : std::unique_ptr<VideoFrame>
169 5653 : VideoScaler::convertFormat(const VideoFrame& input, AVPixelFormat pix)
170 : {
171 5653 : auto output = std::make_unique<VideoFrame>();
172 5653 : output->reserve(pix, input.width(), input.height());
173 5653 : scale(input, *output);
174 5653 : av_frame_copy_props(output->pointer(), input.pointer());
175 5653 : return output;
176 0 : }
177 :
178 : void
179 0 : VideoScaler::reset()
180 : {
181 0 : if (ctx_) {
182 0 : sws_freeContext(ctx_);
183 0 : ctx_ = nullptr;
184 : }
185 0 : }
186 :
187 : } // namespace video
188 : } // namespace jami
|