AVPKit
Property.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 <com/avpkit/core/Property.h>
21 #include <com/avpkit/core/MetaData.h>
22 #include <com/avpkit/ferry/Logger.h>
23 #include <com/avpkit/core/Property.h>
24 extern "C" {
25 #include "FfmpegIncludes.h"
26 #include <libavutil/log.h>
27 }
28 
29 #include <stdexcept>
30 #include <cstring>
31 
32 VS_LOG_SETUP(VS_CPP_PACKAGE);
33 
34 namespace com { namespace avpkit { namespace core {
35 
36  Property :: Property()
37  {
38  mOption=0;
39  mOptionStart=0;
40  }
41 
42  Property :: ~Property()
43  {
44  }
45 
46  Property*
47  Property :: make(const AVOption *start, const AVOption *option)
48  {
49  Property *retval = 0;
50  try
51  {
52  if (!start)
53  throw std::bad_alloc();
54  if (!option)
55  throw std::bad_alloc();
56  retval = Property::make();
57  if (retval)
58  {
59  retval ->mOptionStart = start;
60  retval->mOption = option;
61  }
62  }
63  catch (std::bad_alloc & e)
64  {
65  VS_REF_RELEASE(retval);
66  throw e;
67  }
68  return retval;
69  }
70 
71  const char *
73  {
74  return mOption ? mOption->name : 0;
75  }
76 
77  const char *
79  {
80  return mOption ? mOption->help : 0;
81  }
82 
83  const char *
85  {
86  return mOption ? mOption->unit : 0;
87  }
88 
91  {
92  return mOption ? (IProperty::Type) mOption->type : IProperty::PROPERTY_UNKNOWN;
93  }
94 
95  int64_t
97  {
98  return (int64_t)getDefaultAsDouble();
99  }
100 
101  double
103  {
104  return mOption ? mOption->default_val.dbl: 0.0;
105  }
106 
107  int32_t
109  {
110  return mOption ? mOption->flags : 0;
111  }
112 
113  int32_t
115  {
116  int32_t retval=0;
117  const AVOption* last = 0;
118  if (aContext)
119  {
120  do {
121  last = av_opt_next(aContext, last);
122  if (last)
123  {
124  if (last->type != AV_OPT_TYPE_CONST)
125  ++retval;
126  }
127  } while (last);
128  }
129  return retval;
130  }
131 
132  IProperty*
133  Property :: getPropertyMetaData(void *aContext, int32_t aPropertyNo)
134  {
135  IProperty *retval = 0;
136  const AVOption* last = 0;
137 
138  try
139  {
140  if (!aContext)
141  throw std::runtime_error("no context passed in");
142 
143  int32_t optionNo=-1;
144  do {
145  last = av_opt_next(aContext, last);
146  if (last)
147  {
148  if (last->type != AV_OPT_TYPE_CONST)
149  {
150  ++optionNo;
151  // now see if this option position matches the property asked for
152  if (optionNo == aPropertyNo)
153  {
154  retval = Property::make(av_opt_next(aContext, 0), last);
155  break;
156  }
157  }
158  }
159  } while (last);
160  }
161  catch (std::exception & e)
162  {
163  VS_LOG_DEBUG("Error: %s", e.what());
164  VS_REF_RELEASE(retval);
165  }
166  return retval;
167  }
168 
169  IProperty*
170  Property :: getPropertyMetaData(void *aContext, const char *aName)
171  {
172  IProperty *retval = 0;
173  const AVOption* last = 0;
174 
175  try
176  {
177  if (!aContext)
178  throw std::runtime_error("no context passed in");
179 
180  if (!aName || !*aName)
181  throw std::runtime_error("no property name passed in");
182 
183  last = av_opt_find(aContext, aName, 0, 0, 0);
184  if (last)
185  {
186  if (last->type != AV_OPT_TYPE_CONST)
187  {
188  retval = Property::make(av_opt_next(aContext, 0), last);
189  }
190  }
191  }
192  catch (std::exception & e)
193  {
194  VS_LOG_DEBUG("Error: %s", e.what());
195  VS_REF_RELEASE(retval);
196  }
197  return retval;
198  }
199 
200  int32_t
201  Property :: setProperty(void *aContext, const char* aName, const char *aValue)
202  {
203  int32_t retval = -1;
204 
205  try
206  {
207  if (!aContext)
208  throw std::runtime_error("no context passed in");
209 
210  if (!aName || !*aName)
211  throw std::runtime_error("empty property name passed to setProperty");
212 
213  void * target=0;
214  const AVOption *o = av_opt_find2(aContext, aName, 0, PROPERTY_SEARCH_CHILDREN, 1, &target);
215  if (o) {
216  AVClass *c = *(AVClass**)target;
217  (void) c;
218  VS_LOG_TRACE("Found option \"%s\" with help: %s; in unit: %s; object type: %s; instance name: %s",
219  o->name,
220  o->help,
221  o->unit,
222  c->class_name,
223  c->item_name(aContext));
224  }
225  VS_LOG_TRACE("Setting %s to %s", aName, aValue);
226  retval = av_opt_set(aContext, aName, aValue, PROPERTY_SEARCH_CHILDREN);
227  }
228  catch (std::exception & e)
229  {
230  VS_LOG_DEBUG("Error: %s", e.what());
231  retval = -1;
232  }
233 
234  return retval;
235  }
236 
237  char*
238  Property :: getPropertyAsString(void *aContext, const char *aName)
239  {
240  char* retval = 0;
241  char *value = 0;
242 
243  try
244  {
245  if (!aContext)
246  throw std::runtime_error("no context passed in");
247 
248  if (!aName || !*aName)
249  throw std::runtime_error("empty property name passed to setProperty");
250 
251  // we don't allow a string value longer than this. This is
252  // actually safe because this buffer is only used for non-string options
253  if (av_opt_get(aContext, aName, 0, (uint8_t**)&value) < 0)
254  throw std::runtime_error("could not get property");
255 
256  if (value)
257  {
258  // let's make a copy of the data
259  int32_t valLen = strlen(value);
260  if (valLen > 0)
261  {
262  retval = (char*)malloc((valLen+1)*sizeof(char));
263  if (!retval)
264  throw std::bad_alloc();
265 
266  // copy the data
267  strncpy(retval, value, valLen+1);
268  }
269  }
270  }
271  catch (std::exception & e)
272  {
273  VS_LOG_DEBUG("Error: %s", e.what());
274  if (retval)
275  free(retval);
276  retval = 0;
277  }
278  if (value)
279  av_free(value);
280 
281  // NOTE: Caller must call free() on returned value; we mean it!
282  return retval;
283 
284  }
285 
286  static const char * fakeContextToName(void*)
287  {
288  return "avpkitFake";
289  }
290 
291  int32_t
293  {
294  int32_t retval = 0;
295  try {
296  if (getType() != IProperty::PROPERTY_FLAGS)
297  throw std::runtime_error("flag is not of type PROPERTY_FLAGS");
298 
299  // now, iterate through all options, counting all CONSTS that belong
300  // to the same unit as this option
301  const char* unit = getUnit();
302  if (!unit || !*unit)
303  throw std::runtime_error("flag doesn't have a unit setting, so can't tell what constants");
304 
305  // Create a fake class
306  AVClass fakeClass;
307  fakeClass.class_name="AVPKitFakeClass";
308  fakeClass.item_name = fakeContextToName;
309  fakeClass.option = mOptionStart;
310  AVClass *fakeClassPtr = &fakeClass;
311 
312  const AVOption* last = 0;
313  do
314  {
315  last = av_opt_next(&fakeClassPtr, last);
316  if (last &&
317  last->unit &&
318  last->type == AV_OPT_TYPE_CONST &&
319  strcmp(unit, last->unit)==0)
320  ++retval;
321  } while(last);
322  }
323  catch (std::exception & e)
324  {
325  VS_LOG_DEBUG("Error: %s", e.what());
326  retval = -1;
327  }
328  return retval;
329  }
330 
331  IProperty *
332  Property :: getFlagConstant(int32_t position)
333  {
334  IProperty *retval = 0;
335  try
336  {
337  if (getType() != IProperty::PROPERTY_FLAGS)
338  throw std::runtime_error("flag is not of type PROPERTY_FLAGS");
339 
340  // now, iterate through all options, counting all CONSTS that belong
341  // to the same unit as this option
342  const char* unit = getUnit();
343  if (!unit || !*unit)
344  throw std::runtime_error("flag doesn't have a unit setting, so can't tell what constants");
345 
346  // Create a fake class
347  AVClass fakeClass;
348  fakeClass.class_name="AVPKitFakeClass";
349  fakeClass.item_name = fakeContextToName;
350  fakeClass.option = mOptionStart;
351  AVClass *fakeClassPtr = &fakeClass;
352 
353  const AVOption* last = 0;
354  int32_t constNo = -1;
355  do
356  {
357  last = av_opt_next(&fakeClassPtr, last);
358  if (last &&
359  last->unit &&
360  last->type == AV_OPT_TYPE_CONST &&
361  strcmp(unit, last->unit)==0)
362  {
363  // count in the same was as getNumFlagSettings,
364  // and then return if the position is equal
365  ++constNo;
366  if (constNo == position)
367  {
368  retval = Property::make(av_opt_next(&fakeClassPtr, 0), last);
369  }
370  }
371  } while(last);
372 
373  }
374  catch (std::exception & e)
375  {
376  VS_LOG_DEBUG("Error: %s", e.what());
377  VS_REF_RELEASE(retval);
378  }
379  return retval;
380  }
381 
382  IProperty *
383  Property :: getFlagConstant(const char* aName)
384  {
385  IProperty *retval = 0;
386  try
387  {
388  if (getType() != IProperty::PROPERTY_FLAGS)
389  throw std::runtime_error("flag is not of type PROPERTY_FLAGS");
390 
391  // now, iterate through all options, counting all CONSTS that belong
392  // to the same unit as this option
393  const char* unit = getUnit();
394  if (!unit || !*unit)
395  throw std::runtime_error("flag doesn't have a unit setting, so can't tell what constants");
396 
397  AVClass fakeClass;
398  fakeClass.class_name="AVPKitFakeClass";
399  fakeClass.item_name = fakeContextToName;
400  fakeClass.option = mOptionStart;
401 
402  const AVOption* last = 0;
403  last = av_opt_find(&fakeClass, aName, unit, 0, 0);
404  if (last)
405  {
406  if (last->type == AV_OPT_TYPE_CONST)
407  {
408  retval = Property::make(av_opt_next(&fakeClass, 0), last);
409  }
410  }
411 
412  }
413  catch (std::exception & e)
414  {
415  VS_LOG_DEBUG("Error: %s", e.what());
416  VS_REF_RELEASE(retval);
417  }
418  return retval;
419  }
420 
421  double
422  Property :: getPropertyAsDouble(void *aContext, const char* aName)
423  {
424  double retval = 0;
425  try
426  {
427  if (!aContext)
428  throw std::runtime_error("no context passed in");
429 
430  if (!aName || !*aName)
431  throw std::runtime_error("empty property name passed to setProperty");
432 
433  if (av_opt_get_double(aContext, aName, 0, &retval) < 0)
434  throw std::runtime_error("error getting property as double");
435 
436  }
437  catch (std::exception &e)
438  {
439  VS_LOG_DEBUG("Error: %s", e.what());
440  retval = 0;
441  }
442  return retval;
443  }
444 
445  int64_t
446  Property :: getPropertyAsLong(void *aContext, const char* aName)
447  {
448  int64_t retval = 0;
449  try
450  {
451  if (!aContext)
452  throw std::runtime_error("no context passed in");
453 
454  if (!aName || !*aName)
455  throw std::runtime_error("empty property name passed to setProperty");
456  av_opt_get_int(aContext, aName, 0, &retval);
457  }
458  catch (std::exception &e)
459  {
460  VS_LOG_DEBUG("Error: %s", e.what());
461  retval = 0;
462  }
463  return retval;
464  }
465 
466  IRational*
467  Property :: getPropertyAsRational(void *aContext, const char* aName)
468  {
469  IRational *retval = 0;
470  try
471  {
472  if (!aContext)
473  throw std::runtime_error("no context passed in");
474 
475  if (!aName || !*aName)
476  throw std::runtime_error("empty property name passed to setProperty");
477  AVRational value;
478  av_opt_get_q(aContext, aName, 0, &value);
479  retval = IRational::make(value.num, value.den);
480  }
481  catch (std::exception &e)
482  {
483  VS_LOG_DEBUG("Error: %s", e.what());
484  VS_REF_RELEASE(retval);
485  }
486  return retval;
487  }
488 
489 
490  bool
491  Property :: getPropertyAsBoolean(void *aContext, const char* aName)
492  {
493  return (bool) getPropertyAsLong(aContext, aName);
494  }
495 
496  int32_t
497  Property :: setProperty(void *aContext, const char* aName, double value)
498  {
499  int32_t retval = 0;
500  try
501  {
502  if (!aContext)
503  throw std::runtime_error("no context passed in");
504 
505  if (!aName || !*aName)
506  throw std::runtime_error("empty property name passed to setProperty");
507  retval = av_opt_set_double(aContext, aName, value, PROPERTY_SEARCH_CHILDREN);
508  }
509  catch (std::exception &e)
510  {
511  VS_LOG_DEBUG("Error: %s", e.what());
512  retval = -1;
513  }
514  return retval;
515  }
516 
517  int32_t
518  Property :: setProperty(void *aContext, const char* aName, int64_t value)
519  {
520  int32_t retval = 0;
521  try
522  {
523  if (!aContext)
524  throw std::runtime_error("no context passed in");
525 
526  if (!aName || !*aName)
527  throw std::runtime_error("empty property name passed to setProperty");
528 
529  retval = av_opt_set_int(aContext, aName, value, PROPERTY_SEARCH_CHILDREN);
530  }
531  catch (std::exception &e)
532  {
533  VS_LOG_DEBUG("Error: %s", e.what());
534  retval = -1;
535  }
536  return retval;
537  }
538 
539  int32_t
540  Property :: setProperty(void *aContext, const char* aName, bool value)
541  {
542  return setProperty(aContext, aName, (int64_t)value);
543  }
544 
545  int32_t
546  Property :: setProperty(void *aContext, const char* aName, IRational* value)
547  {
548  int32_t retval = 0;
549  try
550  {
551  if (!aContext)
552  throw std::runtime_error("no context passed in");
553 
554  if (!aName || !*aName)
555  throw std::runtime_error("empty property name passed to setProperty");
556 
557  if (!value)
558  throw std::runtime_error("no rational value passed in");
559 
560  AVRational rational;
561  rational.num = value->getNumerator();
562  rational.den = value->getDenominator();
563  retval = av_opt_set_q(aContext, aName, rational, PROPERTY_SEARCH_CHILDREN);
564  }
565  catch (std::exception &e)
566  {
567  VS_LOG_DEBUG("Error: %s", e.what());
568  retval = -1;
569  }
570  return retval;
571  }
572 
573  int32_t
574  Property :: setProperty(void *aContext, IMetaData* aValuesToSet, IMetaData* aValuesNotFound)
575  {
576  int32_t retval =-1;
577  AVDictionary *tmp = 0;
578  MetaData* valuesToSet = dynamic_cast<MetaData*>(aValuesToSet);
579  MetaData* valuesNotFound = dynamic_cast<MetaData*>(aValuesNotFound);
580  AVDictionary *orig = valuesToSet ? valuesToSet->getDictionary() : 0;
581 
582  try {
583  if (!aContext)
584  throw std::runtime_error("no context passed in");
585 
586  if (orig)
587  av_dict_copy(&tmp, orig, 0);
588 
589  // try setting the values.
590  retval = av_opt_set_dict(aContext, &tmp);
591  if (retval < 0)
592  throw std::runtime_error("failed to set options on context");
593 
594  if (valuesNotFound)
595  retval = valuesNotFound->copy(tmp);
596  av_dict_free(&tmp);
597  retval = 0;
598  } catch (std::exception &e) {
599  av_dict_free(&tmp);
600  retval = -1;
601  }
602  return retval;
603  }
604 
605 }}}
Get MetaData about a IContainer or IStream.
Definition: IMetaData.h:51
Represents settable properties that effect how AVPKit objects operate.
Definition: IProperty.h:37
Type
The different type of options that are supported by AVPKit.
Definition: IProperty.h:44
@ PROPERTY_SEARCH_CHILDREN
Search children first.
Definition: IProperty.h:86
This class wraps represents a Rational number for the AVPKit.
Definition: IRational.h:43
static IRational * make()
Get a new rational that will be set to 0/0.
Definition: IRational.cpp:79
virtual int32_t getDenominator()=0
Get the denominator for this rational.
virtual int32_t getNumerator()=0
Get the numerator for this rational.
int32_t copy(IMetaData *copy)
Destroys the current data, and copies all data from copy.
Definition: MetaData.cpp:164
AVDictionary * getDictionary()
Returns the AVDictionary for passing to FFmpeg calls.
Definition: MetaData.h:76
virtual const char * getHelp()
Get the (English) help string for this property.
Definition: Property.cpp:78
static int32_t setProperty(void *context, const char *name, const char *value)
Looks up the property 'name' in 'context' and sets the value of the property to 'value'.
Definition: Property.cpp:201
virtual int32_t getFlags()
Get any set flags (a bitmask) for this option.
Definition: Property.cpp:108
virtual int64_t getDefault()
Get the default setting this flag would have it not set.
Definition: Property.cpp:96
static int32_t getNumProperties(void *context)
For internal use.
Definition: Property.cpp:114
virtual IProperty * getFlagConstant(int32_t position)
If this IProperty is of the type Type#PROPERTY_FLAGS, this method will give you another IProperty rep...
Definition: Property.cpp:332
static int64_t getPropertyAsLong(void *context, const char *name)
Gets the value of this property, and returns as an long;.
Definition: Property.cpp:446
virtual Type getType()
Get the underlying native type of this property.
Definition: Property.cpp:90
static IRational * getPropertyAsRational(void *context, const char *name)
Gets the value of this property, and returns as an IRational;.
Definition: Property.cpp:467
static IProperty * getPropertyMetaData(void *context, int32_t propertyNo)
Returns the metadata for the numbered property.
Definition: Property.cpp:133
virtual int32_t getNumFlagSettings()
If this IProperty is of the type Type#PROPERTY_FLAGS, this method will tell you how many different fl...
Definition: Property.cpp:292
static double getPropertyAsDouble(void *context, const char *name)
Gets the value of this property, and returns as a double;.
Definition: Property.cpp:422
static bool getPropertyAsBoolean(void *context, const char *name)
Gets the value of this property, and returns as a boolean.
Definition: Property.cpp:491
static char * getPropertyAsString(void *context, const char *name)
Gets the value of this property, and returns as a new[]ed string.
Definition: Property.cpp:238
virtual double getDefaultAsDouble()
Get the default setting this flag would have it not set.
Definition: Property.cpp:102
virtual const char * getName()
IProperty implementation.
Definition: Property.cpp:72
virtual const char * getUnit()
Get any sub-unit this option or constant belongs to.
Definition: Property.cpp:84
WARNING: Do not use logging in this class, and do not set any static file variables to values other t...