diff --git a/av/audio/frame.pyx b/av/audio/frame.pyx index 1638fb753..99852c285 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(self.ptr.format) - self._init_planes(AudioPlane) + if self.planes is None: + self._init_planes(AudioPlane) def __repr__(self): return '' % ( diff --git a/av/codec/context.pyx b/av/codec/context.pyx index 7958a515b..27d7c515f 100644 --- a/av/codec/context.pyx +++ b/av/codec/context.pyx @@ -265,6 +265,25 @@ cdef class CodecContext(object): break return out + def _send_packet_and_recv_reuse(self, Packet packet): + + cdef Frame frame + + cdef int res + with nogil: + res = lib.avcodec_send_packet(self.ptr, &packet.struct if packet is not None else NULL) + err_check(res) + + while True: + frame = self._recv_frame() + if frame: + self._setup_decoded_frame(frame, packet) + yield frame + self._next_frame = frame + else: + break + return + cdef _prepare_frames_for_encode(self, Frame frame): return [frame] @@ -357,6 +376,24 @@ cdef class CodecContext(object): res.append(frame) return res + def xdecode(self, packet=None): + """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 + you do not want the library to automatically re-order frames for you + (if they are encoded with a codec that has B-frames). + + """ + + if not self.codec.ptr: + raise ValueError('cannot decode unknown codec') + + self.open(strict=False) + + for frame in self._send_packet_and_recv_reuse(packet): + yield frame + return + cdef _setup_decoded_frame(self, Frame frame, Packet packet): # Propagate our manual times. diff --git a/av/container/input.pyx b/av/container/input.pyx index 62fb284b2..9cf140546 100644 --- a/av/container/input.pyx +++ b/av/container/input.pyx @@ -163,6 +163,23 @@ cdef class InputContainer(Container): for frame in packet.decode(): yield frame + def xdecode(self, *args, **kwargs): + """xdecode(streams=None, video=None, audio=None, subtitles=None, data=None) + + Yields a series of :class:`.Frame` from the given set of streams:: + + for frame in container.decode(): + # Do something with `frame`. + + .. seealso:: :meth:`.StreamContainer.get` for the interpretation of + the arguments. + + """ + id(kwargs) # Avoid Cython bug; see demux(). + for packet in self.demux(*args, **kwargs): + for frame in packet.xdecode(): + yield frame + def seek(self, offset, whence='time', backward=True, any_frame=False, stream=None): """Seek to a (key)frame nearsest to the given timestamp. diff --git a/av/packet.pyx b/av/packet.pyx index 976f0c930..44763d3ce 100644 --- a/av/packet.pyx +++ b/av/packet.pyx @@ -101,6 +101,13 @@ cdef class Packet(Buffer): """ return self._stream.decode(self) + def xdecode(self): + """ + Send the packet's data to the decoder and return a list of + :class:`.AudioFrame`, :class:`.VideoFrame` or :class:`.SubtitleSet`. + """ + return self._stream.xdecode(self) + @deprecation.method def decode_one(self): """ diff --git a/av/stream.pyx b/av/stream.pyx index c6076ef6d..4cc6e6e98 100644 --- a/av/stream.pyx +++ b/av/stream.pyx @@ -144,6 +144,13 @@ cdef class Stream(object): """ return self.codec_context.decode(packet) + def xdecode(self, packet=None): + """ + Decode a :class:`.Packet` and return a list of :class:`.AudioFrame` + or :class:`.VideoFrame`. + """ + return self.codec_context.xdecode(packet) + @deprecation.method def seek(self, offset, whence='time', backward=True, any_frame=False): """ diff --git a/av/video/frame.pyx b/av/video/frame.pyx index fbcd2b3d6..002eaafa8 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(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`,