diff --git a/av/audio/frame.pyx b/av/audio/frame.pyx index 97de3cc53..ff8255748 100644 --- a/av/audio/frame.pyx +++ b/av/audio/frame.pyx @@ -173,6 +173,24 @@ cdef class AudioFrame(Frame): def __set__(self, value): self.ptr.sample_rate = value + property channels: + """ + number of audio channels. + + :type: int + """ + def __get__(self): + return self.ptr.channels + + property channel_layout: + """ + Channel layout of the audio data. + + :type: int + """ + def __get__(self): + return self.ptr.channel_layout + def to_ndarray(self, **kwargs): """Get a numpy array of this frame. diff --git a/av/chapter.pxd b/av/chapter.pxd new file mode 100644 index 000000000..005207167 --- /dev/null +++ b/av/chapter.pxd @@ -0,0 +1,19 @@ +cimport libav as lib + +from av.container.core cimport Container + + +cdef class Chapter(object): + + # Chapter attributes. + cdef readonly Container container + + cdef lib.AVChapter *_chapter + cdef readonly dict metadata + + # Private API. + cdef _init(self, Container, lib.AVChapter*) + cdef _finalize_for_output(self) + + +cdef Chapter wrap_chapter(Container, lib.AVChapter*) diff --git a/av/chapter.pyx b/av/chapter.pyx new file mode 100644 index 000000000..d44bb7c68 --- /dev/null +++ b/av/chapter.pyx @@ -0,0 +1,76 @@ +from av.utils cimport ( + avdict_to_dict, + avrational_to_fraction, + dict_to_avdict, + to_avrational +) + +cdef object _cinit_bypass_sentinel = object() + + + +cdef Chapter wrap_chapter(Container container, lib.AVChapter *c_chapter): + """Build an av.Chapter for an existing AVChapter. + + The AVChapter MUST be fully constructed and ready for use before this is + called. + + """ + + cdef Chapter py_chapter + py_chapter = Chapter.__new__(Chapter, _cinit_bypass_sentinel) + + py_chapter._init(container, c_chapter) + return py_chapter + + +cdef class Chapter(object): + """ + A single chapter within a :class:`.Container`. + """ + + def __cinit__(self, name): + if name is _cinit_bypass_sentinel: + return + raise RuntimeError('cannot manually instatiate Chapter') + + cdef _init(self, Container container, lib.AVChapter *chapter): + + self.container = container + self._chapter = chapter + + self.metadata = avdict_to_dict( + chapter.metadata, + encoding=self.container.metadata_encoding, + errors=self.container.metadata_errors, + ) + + def __repr__(self): + return '' % ( + self.__class__.__name__, + id(self), + ) + + cdef _finalize_for_output(self): + + dict_to_avdict( + &self._chapter.metadata, self.metadata, + encoding=self.container.metadata_encoding, + errors=self.container.metadata_errors, + ) + + if not self._chapter.time_base.num: + self._chapter.time_base = self._codec_context.time_base + + property time_base: + """ + The unit of time (in fractional seconds) in which timestamps are expressed. + + :type: :class:`~fractions.Fraction` or ``None`` + + """ + def __get__(self): + return avrational_to_fraction(&self._chapter.time_base) + + def __set__(self, value): + to_avrational(value, &self._chapter.time_base) diff --git a/av/codec/codec.pyx b/av/codec/codec.pyx index 4bbbcf369..9a9109fbb 100644 --- a/av/codec/codec.pyx +++ b/av/codec/codec.pyx @@ -52,7 +52,6 @@ Capabilities = define_enum('Capabilities', 'av.codec', ( """Codec uses get_buffer() for allocating buffers and supports custom allocators. If not set, it might not use get_buffer() at all or use operations that assume the buffer was allocated by avcodec_default_get_buffer."""), - ('TRUNCATED', lib.AV_CODEC_CAP_TRUNCATED), ('HWACCEL', 1 << 4), ('DELAY', lib.AV_CODEC_CAP_DELAY, """Encoder or decoder requires flushing with NULL input at the end in order to @@ -102,8 +101,6 @@ Capabilities = define_enum('Capabilities', 'av.codec', ( """Codec supports slice-based (or partition-based) multithreading."""), ('PARAM_CHANGE', lib.AV_CODEC_CAP_PARAM_CHANGE, """Codec supports changed parameters at any point."""), - ('AUTO_THREADS', lib.AV_CODEC_CAP_AUTO_THREADS, - """Codec supports avctx->thread_count == 0 (auto)."""), ('VARIABLE_FRAME_SIZE', lib.AV_CODEC_CAP_VARIABLE_FRAME_SIZE, """Audio encoder supports receiving a different number of samples in each call."""), ('AVOID_PROBING', lib.AV_CODEC_CAP_AVOID_PROBING, @@ -114,10 +111,6 @@ Capabilities = define_enum('Capabilities', 'av.codec', ( the stream. A decoder marked with this flag should only be used as last resort choice for probing."""), - ('INTRA_ONLY', lib.AV_CODEC_CAP_INTRA_ONLY, - """Codec is intra only."""), - ('LOSSLESS', lib.AV_CODEC_CAP_LOSSLESS, - """Codec is lossless."""), ('HARDWARE', lib.AV_CODEC_CAP_HARDWARE, """Codec is backed by a hardware implementation. Typically used to identify a non-hwaccel hardware decoder. For information about hwaccels, use @@ -228,7 +221,10 @@ cdef class Codec(object): E.g: ``'audio'``, ``'video'``, ``'subtitle'``. """ - return lib.av_get_media_type_string(self.ptr.type) + cdef char * s = lib.av_get_media_type_string(self.ptr.type) + if s == NULL: + s = "" + return s property id: def __get__(self): return self.ptr.id @@ -308,7 +304,6 @@ cdef class Codec(object): draw_horiz_band = capabilities.flag_property('DRAW_HORIZ_BAND') dr1 = capabilities.flag_property('DR1') - truncated = capabilities.flag_property('TRUNCATED') hwaccel = capabilities.flag_property('HWACCEL') delay = capabilities.flag_property('DELAY') small_last_frame = capabilities.flag_property('SMALL_LAST_FRAME') @@ -320,11 +315,8 @@ cdef class Codec(object): frame_threads = capabilities.flag_property('FRAME_THREADS') slice_threads = capabilities.flag_property('SLICE_THREADS') param_change = capabilities.flag_property('PARAM_CHANGE') - auto_threads = capabilities.flag_property('AUTO_THREADS') variable_frame_size = capabilities.flag_property('VARIABLE_FRAME_SIZE') avoid_probing = capabilities.flag_property('AVOID_PROBING') - # intra_only = capabilities.flag_property('INTRA_ONLY') # Dupes. - # lossless = capabilities.flag_property('LOSSLESS') # Dupes. hardware = capabilities.flag_property('HARDWARE') hybrid = capabilities.flag_property('HYBRID') encoder_reordered_opaque = capabilities.flag_property('ENCODER_REORDERED_OPAQUE') diff --git a/av/codec/context.pyx b/av/codec/context.pyx index 5c8314615..2cdf7ef5d 100644 --- a/av/codec/context.pyx +++ b/av/codec/context.pyx @@ -96,9 +96,6 @@ Flags = define_enum('Flags', __name__, ( """Only decode/encode grayscale."""), ('PSNR', lib.AV_CODEC_FLAG_PSNR, """error[?] variables will be set during encoding."""), - ('TRUNCATED', lib.AV_CODEC_FLAG_TRUNCATED, - """Input bitstream might be truncated at a random location - instead of only at frame boundaries."""), ('INTERLACED_DCT', lib.AV_CODEC_FLAG_INTERLACED_DCT, """Use interlaced DCT."""), ('LOW_DELAY', lib.AV_CODEC_FLAG_LOW_DELAY, @@ -122,8 +119,6 @@ Flags2 = define_enum('Flags2', __name__, ( """Skip bitstream encoding."""), ('LOCAL_HEADER', lib.AV_CODEC_FLAG2_LOCAL_HEADER, """Place global headers at every keyframe instead of in extradata."""), - ('DROP_FRAME_TIMECODE', lib.AV_CODEC_FLAG2_DROP_FRAME_TIMECODE, - """Timecode is in drop frame format. DEPRECATED!!!!"""), ('CHUNKS', lib.AV_CODEC_FLAG2_CHUNKS, """Input bitstream might be truncated at a packet boundaries instead of only at frame boundaries."""), @@ -168,10 +163,6 @@ cdef class CodecContext(object): self.ptr.thread_count = 0 self.ptr.thread_type = 2 - # Use "ass" format for subtitles (default as of FFmpeg 5.0), not the - # deprecated "ass_with_timings" formats. - self.ptr.sub_text_format = 0 - def _get_flags(self): return self.ptr.flags @@ -195,7 +186,6 @@ cdef class CodecContext(object): loop_filter = flags.flag_property('LOOP_FILTER') gray = flags.flag_property('GRAY') psnr = flags.flag_property('PSNR') - truncated = flags.flag_property('TRUNCATED') interlaced_dct = flags.flag_property('INTERLACED_DCT') low_delay = flags.flag_property('LOW_DELAY') global_header = flags.flag_property('GLOBAL_HEADER') @@ -219,7 +209,6 @@ cdef class CodecContext(object): fast = flags2.flag_property('FAST') no_output = flags2.flag_property('NO_OUTPUT') local_header = flags2.flag_property('LOCAL_HEADER') - drop_frame_timecode = flags2.flag_property('DROP_FRAME_TIMECODE') chunks = flags2.flag_property('CHUNKS') ignore_crop = flags2.flag_property('IGNORE_CROP') show_all = flags2.flag_property('SHOW_ALL') diff --git a/av/container/chapters.pxd b/av/container/chapters.pxd new file mode 100644 index 000000000..295f043e3 --- /dev/null +++ b/av/container/chapters.pxd @@ -0,0 +1,8 @@ +from av.chapter cimport Chapter + + +cdef class ChapterContainer(object): + + cdef list _chapters + + cdef add_chapter(self, Chapter chapter) diff --git a/av/container/chapters.pyx b/av/container/chapters.pyx new file mode 100644 index 000000000..d8d15c520 --- /dev/null +++ b/av/container/chapters.pyx @@ -0,0 +1,41 @@ + +cimport libav as lib + + +def _flatten(input_): + for x in input_: + if isinstance(x, (tuple, list)): + for y in _flatten(x): + yield y + else: + yield x + + +cdef class ChapterContainer(object): + + """ + + A tuple-like container of :class:`Chapter`. + + :: + + # Access chapters like a list: + first = container.chapters[0] + + """ + + def __cinit__(self): + self._chapters = [] + + cdef add_chapter(self, Chapter chapter): + self._chapters.append(chapter) + + # Basic tuple interface. + def __len__(self): + return len(self._chapters) + + def __iter__(self): + return iter(self._chapters) + + def __getitem__(self, index): + return self._chapters[index] diff --git a/av/container/core.pyx b/av/container/core.pyx index d21893c43..b0a87fa74 100755 --- a/av/container/core.pyx +++ b/av/container/core.pyx @@ -29,7 +29,7 @@ cdef object _cinit_sentinel = object() # We want to use the monotonic clock if it is available. cdef object clock = getattr(time, 'monotonic', time.time) -cdef int interrupt_cb (void *p) nogil: +cdef int interrupt_cb (void *p) noexcept nogil: cdef timeout_info info = dereference( p) if info.timeout < 0: # timeout < 0 means no timeout @@ -56,7 +56,7 @@ cdef int pyav_io_open(lib.AVFormatContext *s, lib.AVIOContext **pb, const char *url, int flags, - lib.AVDictionary **options) nogil: + lib.AVDictionary **options) noexcept nogil: with gil: return pyav_io_open_gil(s, pb, url, flags, options) @@ -104,7 +104,7 @@ cdef int pyav_io_open_gil(lib.AVFormatContext *s, cdef void pyav_io_close(lib.AVFormatContext *s, - lib.AVIOContext *pb) nogil: + lib.AVIOContext *pb) noexcept nogil: with gil: pyav_io_close_gil(s, pb) @@ -157,8 +157,6 @@ Flags = define_enum('Flags', __name__, ( This flag is mainly intended for testing."""), ('SORT_DTS', lib.AVFMT_FLAG_SORT_DTS, "Try to interleave outputted packets by dts (using this flag can slow demuxing down)."), - ('PRIV_OPT', lib.AVFMT_FLAG_PRIV_OPT, - "Enable use of private options by delaying codec open (this could be made default once all code is converted)."), ('FAST_SEEK', lib.AVFMT_FLAG_FAST_SEEK, "Enable fast, but inaccurate seeks for some formats."), ('SHORTEST', lib.AVFMT_FLAG_SHORTEST, @@ -185,6 +183,8 @@ cdef class Container(object): if isinstance(file_, str): self.name = file_ + elif isinstance(file_, os.PathLike): + self.name = os.fspath(file_) else: self.name = str(getattr(file_, 'name', '')) @@ -329,7 +329,6 @@ cdef class Container(object): flush_packets = flags.flag_property('FLUSH_PACKETS') bit_exact = flags.flag_property('BITEXACT') sort_dts = flags.flag_property('SORT_DTS') - priv_opt = flags.flag_property('PRIV_OPT') fast_seek = flags.flag_property('FAST_SEEK') shortest = flags.flag_property('SHORTEST') auto_bsf = flags.flag_property('AUTO_BSF') diff --git a/av/container/input.pyx b/av/container/input.pyx index e508f16f4..64e87f44d 100644 --- a/av/container/input.pyx +++ b/av/container/input.pyx @@ -1,11 +1,13 @@ from libc.stdint cimport int64_t -from libc.stdlib cimport free, malloc +from libc.stdlib cimport free, malloc, calloc from av.codec.context cimport CodecContext, wrap_codec_context from av.container.streams cimport StreamContainer +from av.container.chapters cimport ChapterContainer from av.dictionary cimport _Dictionary from av.error cimport err_check from av.packet cimport Packet +from av.chapter cimport Chapter, wrap_chapter from av.stream cimport Stream, wrap_stream from av.utils cimport avdict_to_dict @@ -69,7 +71,13 @@ cdef class InputContainer(Container): free(c_options) self.streams = StreamContainer() - for i in range(self.ptr.nb_streams): + self._update_streams() + + self.metadata = avdict_to_dict(self.ptr.metadata, self.metadata_encoding, self.metadata_errors) + + def _update_streams(self): + # Internal function to update the `streams` container with initial or only new streams. + for i in range(len(self.streams), self.ptr.nb_streams): stream = self.ptr.streams[i] codec = lib.avcodec_find_decoder(stream.codecpar.codec_id) if codec: @@ -83,8 +91,6 @@ cdef class InputContainer(Container): py_codec_context = None self.streams.add_stream(wrap_stream(self, stream, py_codec_context)) - self.metadata = avdict_to_dict(self.ptr.metadata, self.metadata_encoding, self.metadata_errors) - def __dealloc__(self): close_input(self) @@ -104,6 +110,34 @@ cdef class InputContainer(Container): property size: def __get__(self): return lib.avio_size(self.ptr.pb) + property max_analyze_duration: + def __get__(self): return self.ptr.max_analyze_duration + def __set__(self, value): self.ptr.max_analyze_duration = value + + property nb_chapters: + """ + Number of chapters in chapters list. + + - demuxing: set by libavformat + + :type: int + + """ + def __get__(self): + return self.ptr.nb_chapters + + property chapters: + def __get__(self): + chapters = self._chapters + if chapters is None: + # First time, create the container + chapters = ChapterContainer() + for i in range(self.nb_chapters): + chapter = wrap_chapter(self, self.ptr.chapters[i]) + chapters.add_chapter(chapter) + self._chapters = chapters + return chapters + def close(self): close_input(self) @@ -131,11 +165,14 @@ cdef class InputContainer(Container): # (and others). id(kwargs) - streams = self.streams.get(*args, **kwargs) - - cdef bint *include_stream = malloc(self.ptr.nb_streams * sizeof(bint)) - if include_stream == NULL: - raise MemoryError() + # If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also + # appear in av_read_frame(). + # http://ffmpeg.org/doxygen/trunk/structAVFormatContext.html + # To seamlessly support a possibly-increasing number of streams, + # allocate include_stream only when needed and track the allocation + # size with nb_streams. + cdef unsigned int nb_streams = 0 + cdef bint *include_stream = NULL cdef unsigned int i cdef Packet packet @@ -144,14 +181,6 @@ cdef class InputContainer(Container): self.set_timeout(self.read_timeout) try: - for i in range(self.ptr.nb_streams): - include_stream[i] = False - for stream in streams: - i = stream.index - if i>= self.ptr.nb_streams: - raise ValueError('stream index %d out of range' % i) - include_stream[i] = True - while True: packet = Packet() @@ -163,19 +192,35 @@ cdef class InputContainer(Container): except EOFError: break + if packet.ptr.stream_index>= nb_streams: + # Either this is the first time or new streams were added. + + # Learn any new streams... + self._update_streams() + + nb_streams = self.ptr.nb_streams + if packet.ptr.stream_index>= nb_streams: + raise ValueError('stream index %d out of range' % i) + + # Reset the include_stream array... + free(include_stream) + include_stream = calloc(nb_streams, sizeof(bint)) + if include_stream == NULL: + raise MemoryError() + for stream in self.streams.get(*args, **kwargs): + i = stream.index + if i>= nb_streams: + raise ValueError('stream index %d out of range' % i) + include_stream[i] = True + if include_stream[packet.ptr.stream_index]: - # If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams - # may also appear in av_read_frame(). - # http://ffmpeg.org/doxygen/trunk/structAVFormatContext.html - # TODO: find better way to handle this - if packet.ptr.stream_index < len(self.streams): - packet._stream = self.streams[packet.ptr.stream_index] - # Keep track of this so that remuxing is easier. - packet._time_base = packet._stream.ptr.time_base - yield packet + packet._stream = self.streams[packet.ptr.stream_index] + # Keep track of this so that remuxing is easier. + packet._time_base = packet._stream.ptr.time_base + yield packet # Flush! - for i in range(self.ptr.nb_streams): + for i in range(nb_streams): if include_stream[i]: packet = Packet() packet._stream = self.streams[i] diff --git a/av/container/output.pyx b/av/container/output.pyx index a454e121e..86528a8a7 100644 --- a/av/container/output.pyx +++ b/av/container/output.pyx @@ -4,10 +4,12 @@ import os from av.codec.codec cimport Codec from av.codec.context cimport CodecContext, wrap_codec_context +from av.container.chapters cimport ChapterContainer from av.container.streams cimport StreamContainer from av.dictionary cimport _Dictionary from av.error cimport err_check from av.packet cimport Packet +from av.chapter cimport Chapter, wrap_chapter from av.stream cimport Stream, wrap_stream from av.utils cimport dict_to_avdict, to_avrational @@ -43,6 +45,37 @@ cdef class OutputContainer(Container): with nogil: lib.av_packet_free(&self.packet_ptr) + property nb_chapters: + """ + Number of chapters in chapters list. + + When muxing, chapters are normally written in the file header, + so nb_chapters should normally be initialized before write_header + is called. Some muxers (e.g. mov and mkv) can also write chapters + in the trailer. To write chapters in the trailer, nb_chapters + must be zero when write_header is called and non-zero when + write_trailer is called. + + - muxing: set by user + + :type: int + + """ + def __get__(self): + return self.ptr.nb_chapters + + property chapters: + def __get__(self): + chapters = self._chapters + if chapters is None: + # First time, create the container + chapters = ChapterContainer() + for i in range(self.nb_chapters): + chapter = wrap_chapter(self, self.ptr.chapters[i]) + chapters.add_chapter(chapter) + self._chapters = chapters + return chapters + def add_stream(self, codec_name=None, object rate=None, Stream template=None, options=None, **kwargs): """add_stream(codec_name, rate=None) diff --git a/av/container/pyio.pyx b/av/container/pyio.pyx index 17d977f3e..9d45ecc41 100644 --- a/av/container/pyio.pyx +++ b/av/container/pyio.pyx @@ -4,7 +4,7 @@ cimport libav as lib from av.error cimport stash_exception -ctypedef int64_t (*seek_func_t)(void *opaque, int64_t offset, int whence) nogil +ctypedef int64_t (*seek_func_t)(void *opaque, int64_t offset, int whence) noexcept nogil cdef class PyIOFile(object): @@ -76,7 +76,7 @@ cdef class PyIOFile(object): lib.av_freep(&self.buffer) -cdef int pyio_read(void *opaque, uint8_t *buf, int buf_size) nogil: +cdef int pyio_read(void *opaque, uint8_t *buf, int buf_size) noexcept nogil: with gil: return pyio_read_gil(opaque, buf, buf_size) @@ -95,7 +95,7 @@ cdef int pyio_read_gil(void *opaque, uint8_t *buf, int buf_size): return stash_exception() -cdef int pyio_write(void *opaque, uint8_t *buf, int buf_size) nogil: +cdef int pyio_write(void *opaque, uint8_t *buf, int buf_size) noexcept nogil: with gil: return pyio_write_gil(opaque, buf, buf_size) @@ -114,7 +114,7 @@ cdef int pyio_write_gil(void *opaque, uint8_t *buf, int buf_size): return stash_exception() -cdef int64_t pyio_seek(void *opaque, int64_t offset, int whence) nogil: +cdef int64_t pyio_seek(void *opaque, int64_t offset, int whence) noexcept nogil: # Seek takes the standard flags, but also a ad-hoc one which means that # the library wants to know how large the file is. We are generally # allowed to ignore this. diff --git a/av/filter/pad.pyx b/av/filter/pad.pyx index 64ec9a6b1..50bc4507b 100644 --- a/av/filter/pad.pyx +++ b/av/filter/pad.pyx @@ -36,7 +36,10 @@ cdef class FilterPad(object): :type: str """ - return lib.av_get_media_type_string(lib.avfilter_pad_get_type(self.base_ptr, self.index)) + cdef char * s = lib.av_get_media_type_string(lib.avfilter_pad_get_type(self.base_ptr, self.index)) + if s == NULL: + s = "" + return s cdef class FilterContextPad(FilterPad): diff --git a/av/frame.pyx b/av/frame.pyx index 3717ddc81..d09ee7af5 100644 --- a/av/frame.pyx +++ b/av/frame.pyx @@ -66,6 +66,36 @@ cdef class Frame(object): self._time_base = dst + property pkt_size: + """ + Size of the corresponding packet containing the compressed frame. + + :type: int + """ + def __get__(self): + # It is set to a negative value if unknown. + if self.ptr.pkt_size>= 0: + return self.ptr.pkt_size + + property pkt_pos: + """ + Reordered pos from the last AVPacket that has been input into the decoder. + + :type: int + """ + def __get__(self): + if self.ptr.pkt_pos != -1: + return self.ptr.pkt_pos + + property pkt_duration: + """duration of the corresponding packet, expressed in AVStream->time_base units, 0 if unknown. + + :type: int + """ + def __get__(self): + if self.ptr.pkt_duration != lib.AV_NOPTS_VALUE: + return self.ptr.pkt_duration + property dts: """ The decoding timestamp in :attr:`time_base` units for this frame. @@ -123,6 +153,14 @@ cdef class Frame(object): def __set__(self, value): to_avrational(value, &self._time_base) + property key_frame: + """Is this frame a key frame? + + Wraps :ffmpeg:`AVFrame.key_frame`. + + """ + def __get__(self): return self.ptr.key_frame + property is_corrupt: """ Is this frame corrupt? @@ -131,6 +169,17 @@ cdef class Frame(object): """ def __get__(self): return self.ptr.decode_error_flags != 0 or bool(self.ptr.flags & lib.AV_FRAME_FLAG_CORRUPT) + property best_effort_timestamp: + """ + Frame timestamp estimated using various heuristics, in stream time base + + :type: int + """ + def __get__(self): + if self.ptr.best_effort_timestamp == lib.AV_NOPTS_VALUE: + return None + return self.ptr.best_effort_timestamp + @property def side_data(self): if self._side_data is None: diff --git a/av/logging.pyx b/av/logging.pyx index 1bdb7fab7..2253560ad 100644 --- a/av/logging.pyx +++ b/av/logging.pyx @@ -208,7 +208,7 @@ cdef struct log_context: lib.AVClass *class_ const char *name -cdef const char *log_context_name(void *ptr) nogil: +cdef const char *log_context_name(void *ptr) noexcept nogil: cdef log_context *obj = ptr return obj.name @@ -229,7 +229,7 @@ cpdef log(int level, str name, str message): free(obj) -cdef void log_callback(void *ptr, int level, const char *format, lib.va_list args) nogil: +cdef void log_callback(void *ptr, int level, const char *format, lib.va_list args) noexcept nogil: cdef bint inited = lib.Py_IsInitialized() if not inited and not print_after_shutdown: diff --git a/av/packet.pyx b/av/packet.pyx index 0687b2237..089846c33 100644 --- a/av/packet.pyx +++ b/av/packet.pyx @@ -172,6 +172,12 @@ cdef class Packet(Buffer): if self.ptr.pos != -1: return self.ptr.pos + def __set__(self, value): + if value is None: + self.ptr.pos = -1 + else: + self.ptr.pos = value + property size: """ The size in bytes of this packet's data. @@ -193,8 +199,23 @@ cdef class Packet(Buffer): if self.ptr.duration != lib.AV_NOPTS_VALUE: return self.ptr.duration + def __set__(self, value): + if value is None: + self.ptr.duration = lib.AV_NOPTS_VALUE + else: + self.ptr.duration = value + property is_keyframe: def __get__(self): return bool(self.ptr.flags & lib.AV_PKT_FLAG_KEY) property is_corrupt: def __get__(self): return bool(self.ptr.flags & lib.AV_PKT_FLAG_CORRUPT) + + property is_discard: + def __get__(self): return bool(self.ptr.flags & lib.AV_PKT_FLAG_DISCARD) + + property is_trusted: + def __get__(self): return bool(self.ptr.flags & lib.AV_PKT_FLAG_TRUSTED) + + property is_disposable: + def __get__(self): return bool(self.ptr.flags & lib.AV_PKT_FLAG_DISPOSABLE) diff --git a/av/sidedata/sidedata.pyx b/av/sidedata/sidedata.pyx index ec7de5997..79e0b08eb 100644 --- a/av/sidedata/sidedata.pyx +++ b/av/sidedata/sidedata.pyx @@ -101,4 +101,28 @@ cdef class _SideDataContainer(object): class SideDataContainer(_SideDataContainer, Mapping): - pass + """SideDataContainer provides a Mapping-compatible interface. + + While _SideDataContainer is a sequence indexed by position, + SideDataContainer is a full mapping with keys/items/values methods. + However, contrary to usual mappings, SideDataContainer's default iterator + is still by value. + + Design note: SideData types can be integers so they cannot be used as + mapping keys since __getitem__ could not differentiate an integer key from + a plain index. Therefore, the 0-based list index is used as mapping key. + Still, SideDataContainer inherits _SideDataContainer's support for indexing + by SideData type. + """ + + def keys(self): + "D.keys() -> a set-like object providing a view on D's keys" + return range(len(self)) + + def items(self): + "D.items() -> a set-like object providing a view on D's items" + return enumerate(self._by_index) + + def values(self): + "D.values() -> an object providing a view on D's values" + return list(self._by_index) diff --git a/av/stream.pxd b/av/stream.pxd index 5ad3b965e..3b522bb14 100644 --- a/av/stream.pxd +++ b/av/stream.pxd @@ -20,8 +20,6 @@ cdef class Stream(object): # Private API. cdef _init(self, Container, lib.AVStream*, CodecContext) cdef _finalize_for_output(self) - cdef _set_time_base(self, value) - cdef _set_id(self, value) cdef Stream wrap_stream(Container, lib.AVStream*, CodecContext) diff --git a/av/stream.pyx b/av/stream.pyx index 971eaded1..9d25fd0ce 100644 --- a/av/stream.pyx +++ b/av/stream.pyx @@ -1,11 +1,13 @@ import warnings from cpython cimport PyWeakref_NewRef +from cpython.object cimport PyObject_GenericSetAttr from libc.stdint cimport int64_t, uint8_t from libc.string cimport memcpy cimport libav as lib from av.codec.context cimport wrap_codec_context +from av.enum cimport define_enum from av.error cimport err_check from av.packet cimport Packet from av.utils cimport ( @@ -53,6 +55,39 @@ cdef Stream wrap_stream(Container container, lib.AVStream *c_stream, CodecContex return py_stream +Dispositions = define_enum('Dispositions', 'av.stream', ( + ('NONE', 0), + ('DEFAULT', lib.AV_DISPOSITION_DEFAULT, """Default stream."""), + ('DUB', lib.AV_DISPOSITION_DUB, """Dub stream."""), + ('ORIGINAL', lib.AV_DISPOSITION_ORIGINAL, """Original stream."""), + ('COMMENT', lib.AV_DISPOSITION_COMMENT, """Comment stream."""), + ('LYRICS', lib.AV_DISPOSITION_LYRICS, """Lyrics stream."""), + ('KARAOKE', lib.AV_DISPOSITION_KARAOKE, """Karaoke stream."""), + ('FORCED', lib.AV_DISPOSITION_FORCED, + """Track should be used during playback by default. Useful for subtitle + track that should be displayed even when user did not explicitly ask for + subtitles."""), + ('HEARING_IMPAIRED', lib.AV_DISPOSITION_HEARING_IMPAIRED, """Stream for hearing impaired audiences."""), + ('VISUAL_IMPAIRED', lib.AV_DISPOSITION_VISUAL_IMPAIRED, """Stream for visual impaired audiences."""), + ('CLEAN_EFFECTS', lib.AV_DISPOSITION_CLEAN_EFFECTS, """Stream without voice."""), + ('ATTACHED_PIC', lib.AV_DISPOSITION_ATTACHED_PIC, + """The stream is stored in the file as an attached picture/"cover art" + (e.g. APIC frame in ID3v2). The first (usually only) packet associated + with it will be returned among the first few packets read from the file + unless seeking takes place. It can also be accessed at any time in + AVStream.attached_pic."""), + ('TIMED_THUMBNAILS', lib.AV_DISPOSITION_TIMED_THUMBNAILS, + """The stream is sparse, and contains thumbnail images, often + corresponding to chapter markers. Only ever used with + AV_DISPOSITION_ATTACHED_PIC."""), + ('CAPTIONS', lib.AV_DISPOSITION_CAPTIONS, """To specify text track kind (different from subtitles default)."""), + ('DESCRIPTIONS', lib.AV_DISPOSITION_DESCRIPTIONS, """Descriptions stream."""), + ('METADATA', lib.AV_DISPOSITION_METADATA, """Metadata stream."""), + ('DEPENDENT', lib.AV_DISPOSITION_DEPENDENT, """Dependent audio stream (mix_type=0 in mpegts)."""), + ('STILL_IMAGE', lib.AV_DISPOSITION_STILL_IMAGE, """Still images in video stream (still_picture_flag=1 in mpegts)."""), +), is_flags=True) + + cdef class Stream(object): """ A single stream of audio, video or subtitles within a :class:`.Container`. @@ -111,17 +146,34 @@ cdef class Stream(object): if self.codec_context is not None: return getattr(self.codec_context, name) + raise AttributeError(name) + def __setattr__(self, name, value): - if name == "id": - self._set_id(value) + # Properties of Stream, or classes derived from Stream, can be set + # directly. Filter the "local" attributes here: + if name.startswith('_') \ + or name in ( + 'id', + 'disposition', + 'time_base', + 'sample_aspect_ratio', + ) \ + or name.startswith('disposition_'): + # Both of the below Python calls fail: + # TypeError: can't apply this __setattr__ to av.video.stream.VideoStream object + # super(Stream, self).__setattr__(name, value) + # object.__setattr__(self, name, value) + PyObject_GenericSetAttr(self, name, value) return # Convenience setter for codec context properties. if self.codec_context is not None: + # All other attributes are set on the codec_context. This matches with + # the __getattr__ implementation which gets unknown attributes from + # codec_context too. setattr(self.codec_context, name, value) - if name == "time_base": - self._set_time_base(value) + raise AttributeError(name) cdef _finalize_for_output(self): @@ -181,14 +233,11 @@ cdef class Stream(object): def __get__(self): return self.ptr.id - cdef _set_id(self, value): - """ - Setter used by __setattr__ for the id property. - """ - if value is None: - self.ptr.id = 0 - else: - self.ptr.id = value + def __set__(self, value): + if value is None: + self.ptr.id = 0 + else: + self.ptr.id = value property profile: """ @@ -220,11 +269,8 @@ cdef class Stream(object): def __get__(self): return avrational_to_fraction(&self.ptr.time_base) - cdef _set_time_base(self, value): - """ - Setter used by __setattr__ for the time_base property. - """ - to_avrational(value, &self.ptr.time_base) + def __set__(self, value): + to_avrational(value, &self.ptr.time_base) property average_rate: """ @@ -268,6 +314,21 @@ cdef class Stream(object): cdef lib.AVRational val = lib.av_guess_frame_rate(NULL, self.ptr, NULL) return avrational_to_fraction(&val) + property sample_aspect_ratio: + """The sample aspect ratio of this stream. + + - encoding: Set by user. + - decoding: Set by libavformat. + + :type: :class:`~fractions.Fraction` or ``None`` + + """ + def __get__(self): + return avrational_to_fraction(&self.ptr.sample_aspect_ratio) + + def __set__(self, value): + to_avrational(value, &self.ptr.sample_aspect_ratio) + property start_time: """ The presentation timestamp in :attr:`time_base` units of the first @@ -290,6 +351,29 @@ cdef class Stream(object): if self.ptr.duration != lib.AV_NOPTS_VALUE: return self.ptr.duration + @Dispositions.property + def disposition(self): + """Flag property of :class:`.Dispositions`""" + return self._stream.disposition + + disposition_default = disposition.flag_property('DEFAULT') + disposition_dub = disposition.flag_property('DUB') + disposition_original = disposition.flag_property('ORIGINAL') + disposition_comment = disposition.flag_property('COMMENT') + disposition_lyrics = disposition.flag_property('LYRICS') + disposition_karaoke = disposition.flag_property('KARAOKE') + disposition_forced = disposition.flag_property('FORCED') + disposition_hearing_impaired = disposition.flag_property('HEARING_IMPAIRED') + disposition_visual_impaired = disposition.flag_property('VISUAL_IMPAIRED') + disposition_clean_effects = disposition.flag_property('CLEAN_EFFECTS') + disposition_attached_pic = disposition.flag_property('ATTACHED_PIC') + disposition_timed_thumbnails = disposition.flag_property('TIMED_THUMBNAILS') + disposition_captions = disposition.flag_property('CAPTIONS') + disposition_descriptions = disposition.flag_property('DESCRIPTIONS') + disposition_metadata = disposition.flag_property('METADATA') + disposition_dependent = disposition.flag_property('DEPENDENT') + disposition_still_image = disposition.flag_property('STILL_IMAGE') + property frames: """ The number of frames this stream contains. @@ -319,4 +403,7 @@ cdef class Stream(object): :type: str """ - return lib.av_get_media_type_string(self.ptr.codecpar.codec_type) + cdef char * s = lib.av_get_media_type_string(self.ptr.codecpar.codec_type) + if s == NULL: + s = "" + return s diff --git a/av/video/codeccontext.pyx b/av/video/codeccontext.pyx index 8dac3b3fe..b53b65574 100644 --- a/av/video/codeccontext.pyx +++ b/av/video/codeccontext.pyx @@ -159,6 +159,12 @@ cdef class VideoCodecContext(CodecContext): def __get__(self): return self.ptr.coded_width + def __set__(self, value): + self.ptr.coded_width = value + property coded_height: def __get__(self): return self.ptr.coded_height + + def __set__(self, value): + self.ptr.coded_height = value diff --git a/av/video/format.pyx b/av/video/format.pyx index b96658272..03a2a2a92 100644 --- a/av/video/format.pyx +++ b/av/video/format.pyx @@ -47,10 +47,10 @@ cdef class VideoFormat(object): self.ptr = lib.av_pix_fmt_desc_get(pix_fmt) self.width = width self.height = height - self.components = tuple( + self.components = tuple([ VideoFormatComponent(self, i) for i in range(self.ptr.nb_components) - ) + ]) def __repr__(self): if self.width or self.height: diff --git a/av/video/frame.pyx b/av/video/frame.pyx index ac226fa0f..600d84403 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -4,7 +4,7 @@ from libc.stdint cimport uint8_t from av.enum cimport define_enum from av.error cimport err_check -from av.utils cimport check_ndarray, check_ndarray_shape +from av.utils cimport check_ndarray, check_ndarray_shape, avrational_to_fraction from av.video.format cimport VideoFormat, get_pix_fmt, get_video_format from av.video.plane cimport VideoPlane @@ -32,6 +32,81 @@ PictureType = define_enum('PictureType', __name__, ( ('BI', lib.AV_PICTURE_TYPE_BI, "BI type"), )) +ColorPrimariesType = define_enum('ColorPrimariesType', __name__, ( + ('reserved0', lib.AVCOL_PRI_RESERVED0, 'Reserved (0)'), + ('bt709', lib.AVCOL_PRI_BT709, 'bt709'), + ('unknown', lib.AVCOL_PRI_UNSPECIFIED, 'Unspecified'), + ('reserved', lib.AVCOL_PRI_RESERVED, 'Reserved'), + ('bt470m', lib.AVCOL_PRI_BT470M, 'bt470m'), + ('bt470bg', lib.AVCOL_PRI_BT470BG, 'bt470bg'), + ('smpte170m', lib.AVCOL_PRI_SMPTE170M, 'smpte170m'), + ('smpte240m', lib.AVCOL_PRI_SMPTE240M, 'smpte240m'), + ('film', lib.AVCOL_PRI_FILM, 'Film'), + ('bt2020', lib.AVCOL_PRI_BT2020, 'ITU-R BT2020'), + ('smpte428', lib.AVCOL_PRI_SMPTE428, 'SMPTE ST 428-1 (CIE 1931 XYZ)'), + ('smpte431', lib.AVCOL_PRI_SMPTE431, 'SMPTE ST 431-2 (2011) / DCI P3'), + ('smpte432', lib.AVCOL_PRI_SMPTE432, 'SMPTE ST 432-1 (2010) / P3 D65 / Display P3'), + # AVCOL_PRI_EBU3213 # introduced in n4.4-dev: + # (LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO)> (56, 34, 100) + # ('ebu3213', lib.AVCOL_PRI_EBU3213, 'EBU Tech. 3213-E / JEDEC P22 phosphors'), +)) + +ColorTransferCharacteristicType = define_enum('ColorTransferCharacteristicType', __name__, ( + ('reserved0', lib.AVCOL_TRC_RESERVED0, 'Reserved (0)'), + ('bt709', lib.AVCOL_TRC_BT709, 'bt709'), + ('unknown', lib.AVCOL_TRC_UNSPECIFIED, 'Unspecified'), + ('reserved', lib.AVCOL_TRC_RESERVED, 'Reserved'), + ('bt470m', lib.AVCOL_TRC_GAMMA22, 'bt470m'), + ('bt470bg', lib.AVCOL_TRC_GAMMA28, 'bt470bg'), + ('smpte170m', lib.AVCOL_TRC_SMPTE170M, 'smpte170m'), + ('smpte240m', lib.AVCOL_TRC_SMPTE240M, 'smpte240m'), + ('linear', lib.AVCOL_TRC_LINEAR, 'Linear transfer characteristics'), + ('log100', lib.AVCOL_TRC_LOG, 'Logarithmic transfer characteristic'), + ('log316', lib.AVCOL_TRC_LOG_SQRT, 'Logarithmic transfer characteristic'), + ('iec61966-2-4', lib.AVCOL_TRC_IEC61966_2_4, 'IEC 61966-2-4'), + ('bt1361e', lib.AVCOL_TRC_BT1361_ECG, 'ITU-R BT1361 Extended Colour Gamut'), + ('iec61966-2-1', lib.AVCOL_TRC_IEC61966_2_1, 'IEC 61966-2-1 (sRGB or sYCC)'), + ('bt2020-10', lib.AVCOL_TRC_BT2020_10, 'ITU-R BT2020 for 10-bit system'), + ('bt2020-12', lib.AVCOL_TRC_BT2020_12, 'ITU-R BT2020 for 12-bit system'), + ('smpte2084', lib.AVCOL_TRC_SMPTE2084, 'SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems'), + ('smpte428', lib.AVCOL_TRC_SMPTE428, 'SMPTE ST 428-1'), + ('arib-std-b67', lib.AVCOL_TRC_ARIB_STD_B67, 'ARIB STD-B67 (Hybrid log-gamma)'), +)) + + +ColorSpaceType = define_enum('ColorSpaceType', __name__, ( + ('gbr', lib.AVCOL_SPC_RGB, 'RGB/GBR/IEC 61966-2-1 (sRGB)'), + ('bt709', lib.AVCOL_SPC_BT709, 'bt709'), + ('unknown', lib.AVCOL_SPC_UNSPECIFIED, 'Unspecified'), + ('reserved', lib.AVCOL_SPC_RESERVED, 'Reserved'), + ('fcc', lib.AVCOL_SPC_FCC, 'FCC Title 47 Code of Federal Regulations 73.682 (a)(20)'), + ('bt470bg', lib.AVCOL_SPC_BT470BG, 'bt470bg'), + ('smpte170m', lib.AVCOL_SPC_SMPTE170M, 'smpte170m'), + ('smpte240m', lib.AVCOL_SPC_SMPTE240M, 'smpte240m'), + ('ycgco', lib.AVCOL_SPC_YCGCO, 'ycgco'), + ('bt2020nc', lib.AVCOL_SPC_BT2020_NCL, 'ITU-R BT2020 non-constant luminance system'), + ('bt2020c', lib.AVCOL_SPC_BT2020_CL, 'ITU-R BT2020 constant luminance system'), + ('smpte2085', lib.AVCOL_SPC_SMPTE2085, 'SMPTE 2085, Y\'D\'zD\'x'), + ('chroma-derived-nc', lib.AVCOL_SPC_CHROMA_DERIVED_NCL, 'Chromaticity-derived non-constant luminance system'), + ('chroma-derived-c', lib.AVCOL_SPC_CHROMA_DERIVED_CL, 'Chromaticity-derived constant luminance system'), + ('ictcp', lib.AVCOL_SPC_ICTCP, 'ITU-R BT.2100-0, ICtCp'), +)) + +ColorRangeType = define_enum('ColorRangeType', __name__, ( + ('unknown', lib.AVCOL_RANGE_UNSPECIFIED, 'Unspecified'), + ('tv', lib.AVCOL_RANGE_MPEG, 'MPEG (tv)'), + ('pc', lib.AVCOL_RANGE_JPEG, 'JPEG (pc)'), +)) + +ChromaLocationType = define_enum('ChromaLocationType', __name__, ( + ('unknown', lib.AVCHROMA_LOC_UNSPECIFIED, 'Unspecified'), + ('left', lib.AVCHROMA_LOC_LEFT, 'Left'), + ('center', lib.AVCHROMA_LOC_CENTER, 'Center'), + ('topleft', lib.AVCHROMA_LOC_TOPLEFT, 'Top left'), + ('top', lib.AVCHROMA_LOC_TOP, 'Top'), + ('bottomleft', lib.AVCHROMA_LOC_BOTTOMLEFT, 'Bottom left'), + ('bottom', lib.AVCHROMA_LOC_BOTTOM, 'Bottom'), +)) cdef byteswap_array(array, bint big_endian): if (sys.byteorder == 'big') != big_endian: @@ -124,7 +199,7 @@ cdef class VideoFrame(Frame): self.__class__.__name__, self.index, self.pts, - self.format.name, + self.format.name if self.format is not None else None, self.width, self.height, id(self), @@ -161,13 +236,13 @@ cdef class VideoFrame(Frame): """Height of the image, in pixels.""" def __get__(self): return self.ptr.height - property key_frame: - """Is this frame a key frame? + property repeat_pict: + """When decoding, this signals how much the picture must be delayed. - Wraps :ffmpeg:`AVFrame.key_frame`. + Wraps :ffmpeg:`AVFrame.repeat_pict`. """ - def __get__(self): return self.ptr.key_frame + def __get__(self): return self.ptr.repeat_pict property interlaced_frame: """Is this frame an interlaced or progressive? @@ -177,6 +252,39 @@ cdef class VideoFrame(Frame): """ def __get__(self): return self.ptr.interlaced_frame + property top_field_first: + """If the content is interlaced, is top field displayed first. + + Wraps :ffmpeg:`AVFrame.top_field_first`. + + """ + def __get__(self): return self.ptr.top_field_first + + property sample_aspect_ratio: + """Sample aspect ratio for the video frame, 0/1 if unknown/unspecified. + + Wraps :ffmpeg:`AVFrame.sample_aspect_ratio`. + + """ + def __get__(self): + return avrational_to_fraction(&self.ptr.sample_aspect_ratio) + + property coded_picture_number: + """picture number in bitstream order + + Wraps :ffmpeg:`AVFrame.coded_picture_number`. + + """ + def __get__(self): return self.ptr.coded_picture_number + + property display_picture_number: + """picture number in display order + + Wraps :ffmpeg:`AVFrame.display_picture_number`. + + """ + def __get__(self): return self.ptr.display_picture_number + @property def pict_type(self): """One of :class:`.PictureType`. @@ -190,6 +298,56 @@ cdef class VideoFrame(Frame): def pict_type(self, value): self.ptr.pict_type = PictureType[value].value + property color_range: + """MPEG vs JPEG YUV range + + Wraps :ffmpeg:`AVFrame.color_range`. + + """ + def __get__(self): + if self.ptr.color_range != lib.AVCOL_RANGE_UNSPECIFIED: + return ColorRangeType.get(self.ptr.color_range, create=True) + + property color_primaries: + """Color primaries + + Wraps :ffmpeg:`AVFrame.color_primaries`. + + """ + def __get__(self): + if self.ptr.color_primaries != lib.AVCOL_PRI_UNSPECIFIED: + return ColorPrimariesType.get(self.ptr.color_primaries, create=True) + + property color_trc: + """Color transfer characteristics + + Wraps :ffmpeg:`AVFrame.color_trc`. + + """ + def __get__(self): + if self.ptr.color_trc != lib.AVCOL_TRC_UNSPECIFIED: + return ColorTransferCharacteristicType.get(self.ptr.color_trc, create=True) + + property color_space: + """YUV colorspace type + + Wraps :ffmpeg:`AVFrame.colorspace`. + + """ + def __get__(self): + if self.ptr.colorspace != lib.AVCOL_SPC_UNSPECIFIED: + return ColorSpaceType.get(self.ptr.colorspace, create=True) + + property chroma_location: + """Chroma location + + Wraps :ffmpeg:`AVFrame.chroma_location`. + + """ + def __get__(self): + if self.ptr.chroma_location != lib.AVCHROMA_LOC_UNSPECIFIED: + return ChromaLocationType.get(self.ptr.chroma_location, create=True) + def reformat(self, *args, **kwargs): """reformat(width=None, height=None, format=None, src_colorspace=None, dst_colorspace=None, interpolation=None) diff --git a/docs/api/stream.rst b/docs/api/stream.rst index 49ff05ac8..d4bfd86f8 100644 --- a/docs/api/stream.rst +++ b/docs/api/stream.rst @@ -115,5 +115,14 @@ Others .. autoattribute:: Stream.language +.. autoattribute:: Stream.disposition + +.. autoclass:: Dispositions + + Wraps :ffmpeg:`AVStream.disposition` (``AV_DISPOSITION_*``). + + .. enumtable:: av.stream.Dispositions + :class: av.stream.Stream + diff --git a/docs/api/video.rst b/docs/api/video.rst index 5e47b1db8..552e13e86 100644 --- a/docs/api/video.rst +++ b/docs/api/video.rst @@ -59,8 +59,18 @@ Types ~~~~~ .. autoattribute:: VideoFrame.key_frame +.. autoattribute:: VideoFrame.repeat_pict .. autoattribute:: VideoFrame.interlaced_frame +.. autoattribute:: VideoFrame.top_field_first +.. autoattribute:: VideoFrame.sample_aspect_ratio +.. autoattribute:: VideoFrame.coded_picture_number +.. autoattribute:: VideoFrame.display_picture_number .. autoattribute:: VideoFrame.pict_type +.. autoattribute:: VideoFrame.color_range +.. autoattribute:: VideoFrame.color_primaries +.. autoattribute:: VideoFrame.color_trc +.. autoattribute:: VideoFrame.color_space +.. autoattribute:: VideoFrame.chroma_location .. autoclass:: av.video.frame.PictureType diff --git a/include/libavcodec/avcodec.pxd b/include/libavcodec/avcodec.pxd index 1e6111808..dc6914736 100644 --- a/include/libavcodec/avcodec.pxd +++ b/include/libavcodec/avcodec.pxd @@ -51,11 +51,8 @@ cdef extern from "libavcodec/avcodec.h" nogil: AV_CODEC_CAP_FRAME_THREADS AV_CODEC_CAP_SLICE_THREADS AV_CODEC_CAP_PARAM_CHANGE - AV_CODEC_CAP_AUTO_THREADS AV_CODEC_CAP_VARIABLE_FRAME_SIZE AV_CODEC_CAP_AVOID_PROBING - AV_CODEC_CAP_INTRA_ONLY - AV_CODEC_CAP_LOSSLESS AV_CODEC_CAP_HARDWARE AV_CODEC_CAP_HYBRID AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE @@ -76,7 +73,6 @@ cdef extern from "libavcodec/avcodec.h" nogil: AV_CODEC_FLAG_LOOP_FILTER AV_CODEC_FLAG_GRAY AV_CODEC_FLAG_PSNR - AV_CODEC_FLAG_TRUNCATED AV_CODEC_FLAG_INTERLACED_DCT AV_CODEC_FLAG_LOW_DELAY AV_CODEC_FLAG_GLOBAL_HEADER @@ -89,7 +85,6 @@ cdef extern from "libavcodec/avcodec.h" nogil: AV_CODEC_FLAG2_FAST AV_CODEC_FLAG2_NO_OUTPUT AV_CODEC_FLAG2_LOCAL_HEADER - AV_CODEC_FLAG2_DROP_FRAME_TIMECODE AV_CODEC_FLAG2_CHUNKS AV_CODEC_FLAG2_IGNORE_CROP AV_CODEC_FLAG2_SHOW_ALL @@ -100,6 +95,9 @@ cdef extern from "libavcodec/avcodec.h" nogil: cdef enum: AV_PKT_FLAG_KEY AV_PKT_FLAG_CORRUPT + AV_PKT_FLAG_DISCARD + AV_PKT_FLAG_TRUSTED + AV_PKT_FLAG_DISPOSABLE cdef enum: AV_FRAME_FLAG_CORRUPT @@ -225,7 +223,7 @@ cdef extern from "libavcodec/avcodec.h" nogil: int channel_layout # Subtitles. - int sub_text_format + # TODO #: .. todo:: ``get_buffer`` is deprecated for get_buffer2 in newer versions of FFmpeg. int get_buffer(AVCodecContext *ctx, AVFrame *frame) @@ -299,6 +297,76 @@ cdef extern from "libavcodec/avcodec.h" nogil: int size AVDictionary *metadata + cdef enum AVColorPrimaries: + AVCOL_PRI_RESERVED0 + AVCOL_PRI_BT709 + AVCOL_PRI_UNSPECIFIED + AVCOL_PRI_RESERVED + AVCOL_PRI_BT470M + AVCOL_PRI_BT470BG + AVCOL_PRI_SMPTE170M + AVCOL_PRI_SMPTE240M + AVCOL_PRI_FILM + AVCOL_PRI_BT2020 + AVCOL_PRI_SMPTE428 + AVCOL_PRI_SMPTE431 + AVCOL_PRI_SMPTE432 + # AVCOL_PRI_EBU3213 # introduced in n4.4-dev: + # (LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO)> (56, 34, 100) + # AVCOL_PRI_EBU3213 + + cdef enum AVColorTransferCharacteristic: + AVCOL_TRC_RESERVED0 + AVCOL_TRC_BT709 + AVCOL_TRC_UNSPECIFIED + AVCOL_TRC_RESERVED + AVCOL_TRC_GAMMA22 + AVCOL_TRC_GAMMA28 + AVCOL_TRC_SMPTE170M + AVCOL_TRC_SMPTE240M + AVCOL_TRC_LINEAR + AVCOL_TRC_LOG + AVCOL_TRC_LOG_SQRT + AVCOL_TRC_IEC61966_2_4 + AVCOL_TRC_BT1361_ECG + AVCOL_TRC_IEC61966_2_1 + AVCOL_TRC_BT2020_10 + AVCOL_TRC_BT2020_12 + AVCOL_TRC_SMPTE2084 + AVCOL_TRC_SMPTE428 + AVCOL_TRC_ARIB_STD_B67 + + cdef enum AVColorSpace: + AVCOL_SPC_RGB + AVCOL_SPC_BT709 + AVCOL_SPC_UNSPECIFIED + AVCOL_SPC_RESERVED + AVCOL_SPC_FCC + AVCOL_SPC_BT470BG + AVCOL_SPC_SMPTE170M + AVCOL_SPC_SMPTE240M + AVCOL_SPC_YCGCO + AVCOL_SPC_BT2020_NCL + AVCOL_SPC_BT2020_CL + AVCOL_SPC_SMPTE2085 + AVCOL_SPC_CHROMA_DERIVED_NCL + AVCOL_SPC_CHROMA_DERIVED_CL + AVCOL_SPC_ICTCP + + cdef enum AVColorRange: + AVCOL_RANGE_UNSPECIFIED + AVCOL_RANGE_MPEG + AVCOL_RANGE_JPEG + + cdef enum AVChromaLocation: + AVCHROMA_LOC_UNSPECIFIED + AVCHROMA_LOC_LEFT + AVCHROMA_LOC_CENTER + AVCHROMA_LOC_TOPLEFT + AVCHROMA_LOC_TOP + AVCHROMA_LOC_BOTTOMLEFT + AVCHROMA_LOC_BOTTOM + # See: http://ffmpeg.org/doxygen/trunk/structAVFrame.html cdef struct AVFrame: uint8_t *data[4]; @@ -309,7 +377,9 @@ cdef extern from "libavcodec/avcodec.h" nogil: int key_frame # 0 or 1. AVPictureType pict_type + int repeat_pict int interlaced_frame # 0 or 1. + int top_field_first int width int height @@ -322,10 +392,18 @@ cdef extern from "libavcodec/avcodec.h" nogil: int channels # Number of audio channels int channel_layout # Audio channel_layout + AVRational sample_aspect_ratio + int64_t pts int64_t pkt_dts + int64_t best_effort_timestamp + + int coded_picture_number + int display_picture_number int pkt_size + int64_t pkt_pos + int64_t pkt_duration uint8_t **base void *opaque @@ -333,6 +411,12 @@ cdef extern from "libavcodec/avcodec.h" nogil: int flags int decode_error_flags + AVColorRange color_range + AVColorPrimaries color_primaries + AVColorTransferCharacteristic color_trc + AVColorSpace colorspace + AVChromaLocation chroma_location + cdef AVFrame* avcodec_alloc_frame() diff --git a/include/libavformat/avformat.pxd b/include/libavformat/avformat.pxd index ed3e503f5..da690d6ec 100644 --- a/include/libavformat/avformat.pxd +++ b/include/libavformat/avformat.pxd @@ -3,6 +3,13 @@ from libc.stdint cimport int64_t, uint64_t cdef extern from "libavformat/avformat.h" nogil: + # AV_DISPOSITION_STILL_IMAGE was introduced in libav-4.1 + """ + #ifndef AV_DISPOSITION_STILL_IMAGE + # define AV_DISPOSITION_STILL_IMAGE 0x100000 ///< still images in video stream (still_picture_flag=1 in mpegts) + #endif + """ + cdef int avformat_version() cdef char* avformat_configuration() cdef char* avformat_license() @@ -19,6 +26,26 @@ cdef extern from "libavformat/avformat.h" nogil: cdef int AVIO_FLAG_WRITE + #AVStream.disposition + cdef enum: + AV_DISPOSITION_DEFAULT + AV_DISPOSITION_DUB + AV_DISPOSITION_ORIGINAL + AV_DISPOSITION_COMMENT + AV_DISPOSITION_LYRICS + AV_DISPOSITION_KARAOKE + AV_DISPOSITION_FORCED + AV_DISPOSITION_HEARING_IMPAIRED + AV_DISPOSITION_VISUAL_IMPAIRED + AV_DISPOSITION_CLEAN_EFFECTS + AV_DISPOSITION_ATTACHED_PIC + AV_DISPOSITION_TIMED_THUMBNAILS + AV_DISPOSITION_CAPTIONS + AV_DISPOSITION_DESCRIPTIONS + AV_DISPOSITION_METADATA + AV_DISPOSITION_DEPENDENT + AV_DISPOSITION_STILL_IMAGE + cdef enum AVMediaType: AVMEDIA_TYPE_UNKNOWN AVMEDIA_TYPE_VIDEO @@ -42,6 +69,8 @@ cdef extern from "libavformat/avformat.h" nogil: int64_t nb_frames int64_t cur_dts + int disposition + AVDictionary *metadata AVRational avg_frame_rate @@ -146,7 +175,6 @@ cdef extern from "libavformat/avformat.h" nogil: AVFMT_FLAG_FLUSH_PACKETS AVFMT_FLAG_BITEXACT AVFMT_FLAG_SORT_DTS - AVFMT_FLAG_PRIV_OPT AVFMT_FLAG_FAST_SEEK AVFMT_FLAG_SHORTEST AVFMT_FLAG_AUTO_BSF @@ -162,6 +190,16 @@ cdef extern from "libavformat/avformat.h" nogil: cdef AVInputFormat* av_find_input_format(const char *name) + cdef struct AVChapter: + # #if FF_API_CHAPTER_ID_INT # (LIBAVFORMAT_VERSION_MAJOR < 59) + int id # unique ID to identify the chapter + # #else + # int64_t id # unique ID to identify the chapter + # #endif + AVRational time_base # time base in which the start/end timestamps are specified + int64_t start, end # chapter start/end time in time_base units + AVDictionary *metadata + # http://ffmpeg.org/doxygen/trunk/structAVFormatContext.html cdef struct AVFormatContext: @@ -199,6 +237,9 @@ cdef extern from "libavformat/avformat.h" nogil: AVIOContext *pb ) + unsigned int nb_chapters + AVChapter **chapters + cdef AVFormatContext* avformat_alloc_context() # .. c:function:: avformat_open_input(...) @@ -278,6 +319,15 @@ cdef extern from "libavformat/avformat.h" nogil: AVCodec *c ) + AVChapter *avpriv_new_chapter( + AVFormatContext *s, + int id, + AVRational time_base, + int64_t start, + int64_t end, + const char *title, + ) + cdef int avformat_alloc_output_context2( AVFormatContext **ctx, AVOutputFormat *oformat, diff --git a/scratchpad/remux.py b/scratchpad/remux.py index 99de07c2c..dff9e997b 100644 --- a/scratchpad/remux.py +++ b/scratchpad/remux.py @@ -43,7 +43,9 @@ or (stream.type == "subtitle" and not args.nosubtitle) or (stream.type == "data" and not args.nodata) ): - in_to_out[stream] = output.add_stream(template=stream) + in_to_out[stream] = ostream = output.add_stream(template=stream) + if ostream.time_base is None: + ostream.time_base = stream.time_base for i, packet in enumerate(input_.demux(list(in_to_out.keys()))): diff --git a/setup.py b/setup.py index dcd9962d4..6a16296ff 100644 --- a/setup.py +++ b/setup.py @@ -7,9 +7,9 @@ import subprocess import sys +from setuptools import Extension, find_packages, setup from Cython.Build import cythonize from Cython.Compiler.AutoDocTransforms import EmbedSignature -from setuptools import Extension, find_packages, setup FFMPEG_LIBRARIES = [

AltStyle によって変換されたページ (->オリジナル) /