PyAV icon indicating copy to clipboard operation
PyAV copied to clipboard

Make options changeable after codec or (de)muxer has been opened

Open Tjoppen opened this issue 3 years ago • 1 comments

Overview

Currently, as best I can tell, it is only possible to set options once: when a codec or (de)muxer is opened. There are cases where one might want to change some option afterward. My use case is changing -crf in the libx264 encoder during encoding based on user preference and changing network conditions.

Existing FFmpeg API

The av_opt_set*() in libavutil/opt.h. av_opt_get*() are probably also prudent to implement.

Doxy link: https://ffmpeg.org/doxygen/trunk/group__avoptions.html

Expected PyAV API

The av_opt functions are very generic, so the easiest route would probably be to guarantee that all relevant classes have self.ptr exposed that is guaranteed to be passable to the av_opt functions. This could be hidden from the user so that they only have to pass a CodecContext, OutputContainer or InputContainer.

Example based on aiortc's h264.py:

from av.avutil import av_opt_set_int, av_opt_get_int

class H264Encoder(Encoder):
    # [...]
    def set_crf(self, crf: int)
        av_opt_set_int(self.codec, "crf", crf) # search_flags could be optional, default to 0

    def get_crf(self) -> int:
        return av_opt_get_int(self.codec, "crf")

Errors returned by av_opt_get_int() could be raised as Exceptions, which simplifies the get functions like above.

It would also be useful to expose AV_OPT_SEARCH_* and optionally allow setting them on the set and get calls.

Tjoppen avatar Aug 23 '22 08:08 Tjoppen

Here's a little proof-of-concept that works well enough for my purposes. In context.pyx:

cdef class CodecContext(object):
    # Default search_flags to AV_OPT_SEARCH_CHILDREN
    def opt_set_int(self, name: str, value: int, search_flags: int = 1):
        ret = lib.av_opt_set_int(self.ptr, name, value, search_flags)
        if ret != 0:
            raise RuntimeError(f'av_opt_set_int() = {ret}')

    def opt_get_int(self, name: str, search_flags: int = 1):
        cdef int64_t val
        ret = lib.av_opt_get_int(self.ptr, name, search_flags, &val)
        if ret != 0:
            raise RuntimeError(f'av_opt_get_int() = {ret}')
        return val

And of course in avutil.pxd right after av_opt_set_int():

    cdef int av_opt_get_int(
        void *obj,
        char *name,
        int search_flags,
        int64_t *out_val
    )

It'd be better if all classes inherited some base class from which void* ptr can be gotten, perhaps via an abstract property or something.

Tjoppen avatar Aug 24 '22 12:08 Tjoppen

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

github-actions[bot] avatar Dec 23 '22 02:12 github-actions[bot]