AVPKit
VideoResampler.cpp
1 /*******************************************************************************
2  * Copyright (c) 2024, 2026, Olivier Ayache. All rights reserved.
3  *
4  * This file is part of AVPKit.
5  *
6  * AVPKit is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser 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  * AVPKit 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 Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with AVPKit. If not, see <http://www.gnu.org/licenses/>.
18  *******************************************************************************/
19 
20 #include <stdexcept>
21 
22 #include <com/avpkit/ferry/Logger.h>
23 
24 #include <com/avpkit/core/VideoResampler.h>
25 #include <com/avpkit/core/VideoPicture.h>
26 #include <com/avpkit/core/Rational.h>
27 #include <com/avpkit/core/Property.h>
28 
29 // This is the only place we include this to limit
30 // how much it pollutes our code, and make it
31 // easier to yank later.
32 extern "C" {
33 #include <libswscale/swscale.h>
34 
35 #include "PixelFormat.h"
36 }
37 
38 VS_LOG_SETUP(VS_CPP_PACKAGE);
39 
40 using namespace com::avpkit::ferry;
41 
42 namespace com { namespace avpkit { namespace core
43  {
44 
45  VideoResampler :: VideoResampler()
46  {
47  mIHeight = 0;
48  mIWidth = 0;
49  mOHeight = 0;
50  mOWidth = 0;
51  mIPixelFmt = IPixelFormat::NONE;
52  mOPixelFmt = IPixelFormat::NONE;
53  mContext = 0;
54  }
55 
56  VideoResampler :: ~VideoResampler()
57  {
58  if (mContext)
59  sws_freeContext(mContext);
60  mContext = 0;
61  }
62 
63  int32_t
64  VideoResampler :: getInputHeight()
65  {
66  return mIHeight;
67  }
68 
69  int32_t
70  VideoResampler :: getInputWidth()
71  {
72  return mIWidth;
73  }
74 
75  int32_t
76  VideoResampler :: getOutputHeight()
77  {
78  return mOHeight;
79  }
80 
81  int32_t
82  VideoResampler :: getOutputWidth()
83  {
84  return mOWidth;
85  }
86 
88  VideoResampler :: getInputPixelFormat()
89  {
90  return mIPixelFmt;
91  }
92 
94  VideoResampler :: getOutputPixelFormat()
95  {
96  return mOPixelFmt;
97  }
98 
99  int32_t
100  VideoResampler :: resample(IVideoPicture* pOutFrame, IVideoPicture* pInFrame)
101  {
102  int32_t retval = -1;
103  VideoPicture* outFrame = static_cast<VideoPicture*>(pOutFrame);
104  VideoPicture* inFrame = static_cast<VideoPicture*>(pInFrame);
105  try
106  {
107  if (!outFrame)
108  throw std::invalid_argument("invalid output frame");
109  if (outFrame->getHeight() != mOHeight)
110  throw std::runtime_error("output frame height does not match expected value");
111  if (outFrame->getWidth() != mOWidth)
112  throw std::runtime_error("output frame width does not match expected value");
113  if (outFrame->getPixelType() != mOPixelFmt)
114  throw std::runtime_error("output frame pixel format does not match expected value");
115 
116 
117  if (!inFrame)
118  throw std::invalid_argument("invalid input frame");
119 
120  if (inFrame->getHeight() != mIHeight)
121  throw std::runtime_error("input frame height does not match expected value");
122  if (inFrame->getWidth() != mIWidth)
123  throw std::runtime_error("input frame width does not match expected value");
124  if (inFrame->getPixelType() != mIPixelFmt)
125  throw std::runtime_error("input frame pixel format does not match expected value");
126  if (!inFrame->isComplete())
127  throw std::runtime_error("incoming frame doesn't have complete data");
128 
129  // Allocate our output frame.
130  outFrame->setComplete(false, mOPixelFmt, mOWidth, mOHeight,
131  inFrame->getPts());
132  AVFrame *outAVFrame = outFrame->getAVFrame();
133 
134  // Get our input frame; since it's complete() we can pass anything
135  // for the argument
136  AVFrame *inAVFrame = inFrame->getAVFrame();
137 
138  /*
139  VS_LOG_DEBUG("%dx%d(%d:%d)=>%dx%d(%d:%d)",
140  inFrame->getWidth(), inFrame->getHeight(), inFrame->getPixelType(), inAVFrame->linesize[0],
141  outFrame->getWidth(), outFrame->getHeight(), outFrame->getPixelType(), outAVFrame->linesize[0]);
142 
143  memset(outAVFrame->data[0], 64*3-1, outAVFrame->linesize[0]);
144  */
145  // And do the conversion; works around FFmpeg checkin r30306
146  const uint8_t* srcFrame[4] = {
147  inAVFrame->data[0],
148  inAVFrame->data[1],
149  inAVFrame->data[2],
150  inAVFrame->data[3],
151  };
152  retval = sws_scale(mContext, srcFrame, inAVFrame->linesize, 0,
153  mIHeight, outAVFrame->data, outAVFrame->linesize);
154 
155  // and go home happy.
156  {
157  outFrame->setQuality(inFrame->getQuality());
158  outFrame->setComplete(retval >= 0,mOPixelFmt, mOWidth, mOHeight,
159  inFrame->getPts());
160  }
161  }
162  catch (std::bad_alloc& e)
163  {
164  throw e;
165  }
166  catch (std::exception& e)
167  {
168  VS_LOG_DEBUG("error: %s", e.what());
169  retval = -1;
170  }
171  return retval;
172  }
173 
174 /*
175  * The next two methods are to work around a break in backwards compatibility introduced
176  * in FFmpeg in September 2010
177  */
178 static int handle_jpeg(PixelFormat::Type *format)
179 {
180  switch (*format) {
181  case AV_PIX_FMT_YUVJ420P: *format = (PixelFormat::Type)AV_PIX_FMT_YUVJ420P; return 1;
182  case AV_PIX_FMT_YUVJ422P: *format = (PixelFormat::Type)AV_PIX_FMT_YUVJ422P; return 1;
183  case AV_PIX_FMT_YUVJ444P: *format = (PixelFormat::Type)AV_PIX_FMT_YUVJ444P; return 1;
184  case AV_PIX_FMT_YUVJ440P: *format = (PixelFormat::Type)AV_PIX_FMT_YUVJ440P; return 1;
185  default: return 0;
186  }
187 }
188 
189 static SwsContext *avpkitSws_getContext(int srcW, int srcH, PixelFormat::Type srcFormat,
190  int dstW, int dstH, PixelFormat::Type dstFormat, int flags,
191  SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param)
192 {
193  SwsContext *c;
194  const int *coeff;
195  int srcRange=handle_jpeg(&srcFormat);
196  int dstRange=handle_jpeg(&dstFormat);
197 
198  if(!(c=sws_alloc_context()))
199  return NULL;
200 
201  av_opt_set_int(c, "sws_flags", flags, 0);
202  av_opt_set_int(c, "srcw", srcW, 0);
203  av_opt_set_int(c, "srch", srcH, 0);
204  av_opt_set_int(c, "dstw", dstW, 0);
205  av_opt_set_int(c, "dsth", dstH, 0);
206  av_opt_set_int(c, "src_range", srcRange, 0);
207  av_opt_set_int(c, "dst_range", dstRange, 0);
208  av_opt_set_int(c, "src_format", srcFormat, 0);
209  av_opt_set_int(c, "dst_format", dstFormat, 0);
210 
211  if (param) {
212  av_opt_set_double(c, "param0", param[0], 0);
213  av_opt_set_double(c, "param1", param[1], 0);
214  }
215 
216  coeff = sws_getCoefficients(SWS_CS_DEFAULT);
217  sws_setColorspaceDetails(c, coeff, srcRange, coeff /* FIXME*/, dstRange, 0, 1<<16, 1<<16);
218 
219  if(sws_init_context(c, srcFilter, dstFilter) < 0){
220  sws_freeContext(c);
221  return NULL;
222  }
223 
224  return c;
225 }
226  VideoResampler*
227  VideoResampler :: make(
228  int32_t outputWidth, int32_t outputHeight,
229  IPixelFormat::Type outputFmt,
230  int32_t inputWidth, int32_t inputHeight,
231  IPixelFormat::Type inputFmt)
232  {
233  VideoResampler* retval = 0;
234  try
235  {
236  if (outputWidth <= 0)
237  throw std::invalid_argument("invalid output width");
238  if (outputHeight <= 0)
239  throw std::invalid_argument("invalid output height");
240  if (outputFmt == IPixelFormat::NONE)
241  throw std::invalid_argument("cannot set output pixel format to none");
242  if (inputWidth <= 0)
243  throw std::invalid_argument("invalid input width");
244  if (inputHeight <= 0)
245  throw std::invalid_argument("invalid input height");
246  if (inputFmt == IPixelFormat::NONE)
247  throw std::invalid_argument("cannot set input pixel format to none");
248 
249  retval = VideoResampler::make();
250  if (retval)
251  {
252  retval->mOHeight = outputHeight;
253  retval->mOWidth = outputWidth;
254  retval->mOPixelFmt = outputFmt;
255 
256  retval->mIHeight = inputHeight;
257  retval->mIWidth = inputWidth;
258  retval->mIPixelFmt = inputFmt;
259 
260  int32_t flags = 0;
261  if (inputWidth < outputWidth)
262  // We're upscaling
263  flags = SWS_BICUBIC;
264  else
265  // We're downscaling
266  flags = SWS_AREA;
267 
268  retval->mContext = avpkitSws_getContext(
269  retval->mIWidth, // src width
270  retval->mIHeight, // src height
271  retval->mIPixelFmt, // src pixel type
272  retval->mOWidth, // dst width
273  retval->mOHeight, // dst height
274  retval->mOPixelFmt, // dst pixel type
275  flags, // Flags
276  0, // Source Filter
277  0, // Destination Filter
278  0 // An array of parameters for filters
279  );
280  if (!retval->mContext)
281  throw std::runtime_error("could not allocate a image rescaler");
282  }
283  }
284  catch (std::bad_alloc& e)
285  {
286  VS_REF_RELEASE(retval);
287  }
288  catch (std::exception& e)
289  {
290  VS_LOG_DEBUG("error: %s", e.what());
291  VS_REF_RELEASE(retval);
292  }
293 
294  return retval;
295  }
296 
297  int32_t
298  VideoResampler :: getNumProperties()
299  {
300  return Property::getNumProperties(mContext);
301  }
302 
303  IProperty*
304  VideoResampler :: getPropertyMetaData(int32_t propertyNo)
305  {
306  return Property::getPropertyMetaData(mContext, propertyNo);
307  }
308 
309  IProperty*
310  VideoResampler :: getPropertyMetaData(const char *name)
311  {
312  return Property::getPropertyMetaData(mContext, name);
313  }
314 
315  int32_t
316  VideoResampler :: setProperty(IMetaData* valuesToSet, IMetaData* valuesNotFound)
317  {
318  return Property::setProperty(mContext, valuesToSet, valuesNotFound);
319  }
320 
321 
322  int32_t
323  VideoResampler :: setProperty(const char* aName, const char *aValue)
324  {
325  return Property::setProperty(mContext, aName, aValue);
326  }
327 
328  int32_t
329  VideoResampler :: setProperty(const char* aName, double aValue)
330  {
331  return Property::setProperty(mContext, aName, aValue);
332  }
333 
334  int32_t
335  VideoResampler :: setProperty(const char* aName, int64_t aValue)
336  {
337  return Property::setProperty(mContext, aName, aValue);
338  }
339 
340  int32_t
341  VideoResampler :: setProperty(const char* aName, bool aValue)
342  {
343  return Property::setProperty(mContext, aName, aValue);
344  }
345 
346 
347  int32_t
348  VideoResampler :: setProperty(const char* aName, IRational *aValue)
349  {
350  return Property::setProperty(mContext, aName, aValue);
351  }
352 
353 
354  char*
355  VideoResampler :: getPropertyAsString(const char *aName)
356  {
357  return Property::getPropertyAsString(mContext, aName);
358  }
359 
360  double
361  VideoResampler :: getPropertyAsDouble(const char *aName)
362  {
363  return Property::getPropertyAsDouble(mContext, aName);
364  }
365 
366  int64_t
367  VideoResampler :: getPropertyAsLong(const char *aName)
368  {
369  return Property::getPropertyAsLong(mContext, aName);
370  }
371 
372  IRational*
373  VideoResampler :: getPropertyAsRational(const char *aName)
374  {
375  return Property::getPropertyAsRational(mContext, aName);
376  }
377 
378  bool
379  VideoResampler :: getPropertyAsBoolean(const char *aName)
380  {
381  return Property::getPropertyAsBoolean(mContext, aName);
382  }
383 
384  }}}
Get MetaData about a IContainer or IStream.
Definition: IMetaData.h:51
Represents settable properties that effect how AVPKit objects operate.
Definition: IProperty.h:37
This class wraps represents a Rational number for the AVPKit.
Definition: IRational.h:43
Represents one raw (undecoded) picture in a video stream, plus a timestamp for when to display that v...
Definition: IVideoPicture.h:40
virtual void setComplete(bool aIsComplete, IPixelFormat::Type format, int width, int height, int64_t pts)
After modifying the raw data in this buffer, call this function to let the object know it is now comp...
virtual int64_t getPts()
What is the Presentation Time Stamp (in Microseconds) of this picture.
virtual IPixelFormat::Type getPixelType()
Returns the pixel format of the picture.
Definition: VideoPicture.h:50
virtual bool isComplete()
Is this picture completely decoded?
Definition: VideoPicture.h:47
virtual void setQuality(int newQuality)
Set the Quality to a new value.
virtual int getQuality()
This value is the quality setting this VideoPicture had when it was decoded, or is the value to use w...
virtual int getWidth()
What is the width of the picture.
Definition: VideoPicture.h:48
virtual int getHeight()
What is the height of the picture.
Definition: VideoPicture.h:49
VS_API_AVPKIT AVFrame * getAVFrame()
Call to get the raw underlying AVFrame we manage; don't pass this to ffmpeg directly as ffmpeg often ...
This library contains routines used by AVPKit libraries for "ferry"ing Java objects to and from nativ...
WARNING: Do not use logging in this class, and do not set any static file variables to values other t...