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