From 1f10e615c524888a4bfda983edbe84b6478fdbee Mon Sep 17 00:00:00 2001 From: MagicBear <mb@bilibili.com> Date: Thu, 6 Dec 2018 01:26:00 +0800 Subject: [PATCH 1/2] expose codec level & flags --- av/packet.pyx | 3 +++ av/stream.pyx | 4 ++++ include/libavcodec/avcodec.pxd | 1 + 3 files changed, 8 insertions(+) diff --git a/av/packet.pyx b/av/packet.pyx index 976f0c9..f022570 100644 --- a/av/packet.pyx +++ b/av/packet.pyx @@ -204,6 +204,9 @@ cdef class Packet(Buffer): if self.struct.duration != lib.AV_NOPTS_VALUE: return self.struct.duration + property flags: + def __get__(self): return self.struct.flags + property is_keyframe: def __get__(self): return bool(self.struct.flags & lib.AV_PKT_FLAG_KEY) diff --git a/av/stream.pyx b/av/stream.pyx index c6076ef..b3da9ed 100644 --- a/av/stream.pyx +++ b/av/stream.pyx @@ -183,6 +183,10 @@ cdef class Stream(object): else: return None + property level: + def __get__(self): + return self._codec_context.level + property index: """ The index of this stream in its :class:`.Container`. diff --git a/include/libavcodec/avcodec.pxd b/include/libavcodec/avcodec.pxd index 99cb27e..257f833 100644 --- a/include/libavcodec/avcodec.pxd +++ b/include/libavcodec/avcodec.pxd @@ -120,6 +120,7 @@ cdef extern from "libavcodec/avcodec.pyav.h" nogil: int thread_type int profile + int level AVDiscard skip_frame AVFrame* coded_frame -- GitLab From 92de0c56240b170daee433ba4ad0a2be965f1c22 Mon Sep 17 00:00:00 2001 From: MagicBear <mb@bilibili.com> Date: Thu, 6 Dec 2018 04:47:57 +0800 Subject: [PATCH 2/2] Reuse video & audio frame without allow new --- av/audio/codeccontext.pyx | 37 ++++++++++++++++++++++++++++++++++ av/audio/frame.pyx | 3 ++- av/codec/context.pxd | 6 +++--- av/codec/context.pyx | 8 +++++--- av/frame.pyx | 1 + av/subtitles/codeccontext.pyx | 2 +- av/video/codeccontext.pyx | 38 ++++++++++++++++++++++++++++++++++- av/video/frame.pyx | 3 ++- 8 files changed, 88 insertions(+), 10 deletions(-) diff --git a/av/audio/codeccontext.pyx b/av/audio/codeccontext.pyx index aa4c15c..5811624 100644 --- a/av/audio/codeccontext.pyx +++ b/av/audio/codeccontext.pyx @@ -55,6 +55,43 @@ cdef class AudioCodecContext(CodecContext): return frames + cdef _send_packet_and_recv(self, Packet packet, bint reuse = False): + + cdef Frame frame + + cdef AudioFrame aframe + + cdef int res + with nogil: + res = lib.avcodec_send_packet(self.ptr, &packet.struct if packet is not None else NULL) + err_check(res) + + out = [] + saved_samples = 0 + while True: + frame = self._recv_frame() + if frame: + if len(out) != 0 and reuse: + return self._send_packet_and_recv(packet, reuse=False) + + self._setup_decoded_frame(frame, packet) + if reuse: + self._next_frame = frame + if self._save_frame is None: + self._save_frame = self._alloc_next_frame() + aframe = self._save_frame + aframe._copy_internal_attributes(frame, data_layout = True) + saved_samples = frame.ptr.nb_samples + out.append(frame) + else: + if reuse and len(out) == 1: + # reset frame data to original + aframe = self._next_frame + aframe._copy_internal_attributes(self._save_frame, data_layout = True) + aframe.ptr.nb_samples = saved_samples + break + return out + cdef Frame _alloc_next_frame(self): return alloc_audio_frame() diff --git a/av/audio/frame.pyx b/av/audio/frame.pyx index 1638fb7..99852c2 100644 --- a/av/audio/frame.pyx +++ b/av/audio/frame.pyx @@ -89,7 +89,8 @@ cdef class AudioFrame(Frame): cdef _init_user_attributes(self): self.layout = get_audio_layout(0, self.ptr.channel_layout) self.format = get_audio_format(<lib.AVSampleFormat>self.ptr.format) - self._init_planes(AudioPlane) + if self.planes is None: + self._init_planes(AudioPlane) def __repr__(self): return '<av.%s %d, pts=%s, %d samples at %dHz, %s, %s at 0x%x>' % ( diff --git a/av/codec/context.pxd b/av/codec/context.pxd index c349ed2..fb6b20f 100644 --- a/av/codec/context.pxd +++ b/av/codec/context.pxd @@ -43,7 +43,7 @@ cdef class CodecContext(object): # Wraps both versions of the transcode API, returning lists. cpdef encode(self, Frame frame=?) - cpdef decode(self, Packet packet=?) + cpdef decode(self, Packet packet=?, bint reuse = ?) # Used by both transcode APIs to setup user-land objects. # TODO: Remove the `Packet` from `_setup_decoded_frame` (because flushing @@ -60,13 +60,13 @@ cdef class CodecContext(object): # the buffer as often as possible. cdef _send_frame_and_recv(self, Frame frame) cdef _recv_packet(self) - cdef _send_packet_and_recv(self, Packet packet) + cdef _send_packet_and_recv(self, Packet packet, bint reuse = ?) cdef _recv_frame(self) # Implemented by children for the generic send/recv API, so we have the # correct subclass of Frame. cdef Frame _next_frame cdef Frame _alloc_next_frame(self) - + cdef Frame _save_frame cdef CodecContext wrap_codec_context(lib.AVCodecContext*, const lib.AVCodec*, bint allocated) diff --git a/av/codec/context.pyx b/av/codec/context.pyx index 7958a51..31966a5 100644 --- a/av/codec/context.pyx +++ b/av/codec/context.pyx @@ -74,6 +74,7 @@ cdef class CodecContext(object): self.options = {} self.stream_index = -1 # This is set by the container immediately. + self._save_frame = None cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec): @@ -247,7 +248,7 @@ cdef class CodecContext(object): break return out - cdef _send_packet_and_recv(self, Packet packet): + cdef _send_packet_and_recv(self, Packet packet, bint reuse = False): cdef Frame frame @@ -260,6 +261,7 @@ cdef class CodecContext(object): while True: frame = self._recv_frame() if frame: + self._setup_decoded_frame(frame, packet) out.append(frame) else: break @@ -336,7 +338,7 @@ cdef class CodecContext(object): # are off! packet._time_base = self.ptr.time_base - cpdef decode(self, Packet packet=None): + cpdef decode(self, Packet packet=None, bint reuse = False): """Decode a list of :class:`.Frame` from the given :class:`.Packet`. If the packet is None, the buffers will be flushed. This is useful if @@ -351,7 +353,7 @@ cdef class CodecContext(object): self.open(strict=False) res = [] - for frame in self._send_packet_and_recv(packet): + for frame in self._send_packet_and_recv(packet, reuse=reuse): if isinstance(frame, Frame): self._setup_decoded_frame(frame, packet) res.append(frame) diff --git a/av/frame.pyx b/av/frame.pyx index b8497d2..d8d0107 100644 --- a/av/frame.pyx +++ b/av/frame.pyx @@ -39,6 +39,7 @@ cdef class Frame(object): # ourselves to the maximum plane count (as determined only by VideoFrames # so far), in case the library implementation does not set the last # plane to NULL. + cdef int max_plane_count = self._max_plane_count() cdef int plane_count = 0 while plane_count < max_plane_count and self.ptr.extended_data[plane_count]: diff --git a/av/subtitles/codeccontext.pyx b/av/subtitles/codeccontext.pyx index ef2dff7..9420d05 100644 --- a/av/subtitles/codeccontext.pyx +++ b/av/subtitles/codeccontext.pyx @@ -8,7 +8,7 @@ from av.utils cimport err_check cdef class SubtitleCodecContext(CodecContext): - cdef _send_packet_and_recv(self, Packet packet): + cdef _send_packet_and_recv(self, Packet packet, bint reuse = False): cdef SubtitleProxy proxy = SubtitleProxy() cdef int got_frame = 0 diff --git a/av/video/codeccontext.pyx b/av/video/codeccontext.pyx index ceb12a1..884e171 100644 --- a/av/video/codeccontext.pyx +++ b/av/video/codeccontext.pyx @@ -12,7 +12,6 @@ from av.video.frame cimport VideoFrame, alloc_video_frame from av.video.reformatter cimport VideoReformatter - cdef class VideoCodecContext(CodecContext): def __cinit__(self, *args, **kwargs): @@ -63,6 +62,43 @@ cdef class VideoCodecContext(CodecContext): cdef Frame _alloc_next_frame(self): return alloc_video_frame() + cdef _send_packet_and_recv(self, Packet packet, bint reuse = False): + + cdef Frame frame + + cdef VideoFrame vframe + + cdef int res + with nogil: + res = lib.avcodec_send_packet(self.ptr, &packet.struct if packet is not None else NULL) + err_check(res) + + out = [] + while True: + frame = self._recv_frame() + if frame: + if len(out) != 0 and reuse: + return self._send_packet_and_recv(packet, reuse=False) + + self._setup_decoded_frame(frame, packet) + if reuse: + self._next_frame = frame + + if self._save_frame is None: + self._save_frame = self._alloc_next_frame() + vframe = self._save_frame + vframe._copy_internal_attributes(frame, data_layout = True) + else: + self._setup_decoded_frame(frame, packet) + out.append(frame) + else: + if reuse and len(out) == 1: + # reset frame data to original + vframe = self._next_frame + vframe._copy_internal_attributes(self._save_frame, data_layout = True) + break + return out + cdef _setup_decoded_frame(self, Frame frame, Packet packet): CodecContext._setup_decoded_frame(self, frame, packet) cdef VideoFrame vframe = frame diff --git a/av/video/frame.pyx b/av/video/frame.pyx index fbcd2b3..002eaaf 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -96,7 +96,8 @@ cdef class VideoFrame(Frame): cdef _init_user_attributes(self): self.format = get_video_format(<lib.AVPixelFormat>self.ptr.format, self.ptr.width, self.ptr.height) - self._init_planes(VideoPlane) + if self.planes is None: + self._init_planes(VideoPlane) def __dealloc__(self): # The `self._buffer` member is only set if *we* allocated the buffer in `_init`, -- GitLab