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