Skip to content

Commit

Permalink
Add PyavOutput support for vc4 platforms
Browse files Browse the repository at this point in the history
Make the PyavOutput work with existing non-libav encoders that don't
pass a "packet" - so instead we have to make one up.

Signed-off-by: David Plowman <[email protected]>
  • Loading branch information
davidplowman committed Nov 5, 2024
1 parent 413c098 commit a88056f
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 5 deletions.
4 changes: 4 additions & 0 deletions picamera2/encoders/h264_encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ def _start(self):
self._controls += [(V4L2_CID_MPEG_VIDEO_H264_MIN_QP, self.qp)]
self._controls += [(V4L2_CID_MPEG_VIDEO_H264_MAX_QP, self.qp)]

# The output objects may need to know what kind of stream this is.
for out in self._output:
out._add_stream("video", "h264")

super()._start()

def _setup(self, quality):
Expand Down
7 changes: 7 additions & 0 deletions picamera2/encoders/mjpeg_encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,10 @@ def _setup(self, quality):
actual_complexity = self.width * self.height * getattr(self, "framerate", 30)
reference_bitrate = BITRATE_TABLE[quality] * 1000000
self.bitrate = int(reference_bitrate * actual_complexity / reference_complexity)

def _start(self):
# The output objects may need to know what kind of stream this is.
for out in self._output:
out._add_stream("video", "mjpeg", rate=30) # seem to need a rate to prevent timestamp warnings

super()._start()
24 changes: 19 additions & 5 deletions picamera2/outputs/pyavoutput.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import av

from fractions import Fraction

from .output import Output


Expand Down Expand Up @@ -50,15 +52,26 @@ def stop(self):
except Exception:
pass
self._container = None
self._streams = {}

def outputframe(self, frame, keyframe=True, timestamp=None, packet=None, audio=False):
"""Output an encoded frame using PyAv."""
if self.recording and self._container:
orig_stream = packet.stream
if orig_stream not in self._streams:
raise RuntimeError("Stream not found in PyavOutput")
# Here we replace in encoder's stream by the corresponding one for our output container.
packet.stream = self._streams[orig_stream]
orig_stream = None
# We must make a packet that looks like it came from our own container's version of the stream.
if not packet:
# No packet present. It must have come from a video encoder that isn't using libav, so make one up.
packet = av.Packet(frame)
packet.dts = timestamp
packet.pts = timestamp
packet.time_base = Fraction(1, 1000000)
packet.stream = self._streams["video"]
else:
# We can perform a switcheroo on the packet's stream, swapping the encoder's version for ours!
orig_stream = packet.stream
if orig_stream not in self._streams:
raise RuntimeError("Stream not found in PyavOutput")
packet.stream = self._streams[orig_stream]

try:
self._container.mux(packet)
Expand All @@ -74,4 +87,5 @@ def outputframe(self, frame, keyframe=True, timestamp=None, packet=None, audio=F
# Put the original stream back, just in case the encoder has multiple outputs and will pass
# it to each one.
packet.stream = orig_stream

self.outputtimestamp(timestamp)

0 comments on commit a88056f

Please sign in to comment.