From 873617092a79138ad53738663e3b0aa54d209e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Tue, 20 Aug 2024 20:24:28 +0200 Subject: [PATCH] Basic custom Ogg Opus comment tag write support These changes let PyOgg users add custom comment tags to the generated stream, as opposed to the current behavior of hardcoding no comment tags on all cases, which can come in handy for stream identification requirements. While at it, I've tweaked the vendor string from `ENCODER=PyOgg` to `PyOgg `, where `` is the value of the `__version__` variable at `__init__.py`. The rationale for this change is that this brings the vendor string format more in line with the conventions used by libopus and other encoders (such as FFmpeg and libavcodec), while also being more useful to track the precise encoder code used to generate the stream thanks to the addition of the version number. --- pyogg/ogg_opus_writer.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/pyogg/ogg_opus_writer.py b/pyogg/ogg_opus_writer.py index 547d0f5..accc3c9 100644 --- a/pyogg/ogg_opus_writer.py +++ b/pyogg/ogg_opus_writer.py @@ -4,6 +4,7 @@ import random import struct from typing import ( + List, Optional, Union, BinaryIO @@ -11,6 +12,7 @@ from . import ogg from . import opus +from . import __version__ from .opus_buffered_encoder import OpusBufferedEncoder #from .opus_encoder import OpusEncoder from .pyogg_error import PyOggError @@ -55,6 +57,9 @@ def __init__(self, # Create a new stream state with a random serial number self._stream_state = self._create_stream_state() + # Do not add any user comments to the generated stream + self._user_comments: List[str] = [] + # Create a packet (reused for each pass) self._ogg_packet = ogg.ogg_packet() self._packet_valid = False @@ -122,6 +127,22 @@ def write(self, pcm: memoryview) -> None: self._write_to_oggopus(pcm) + def add_user_comment(self, comment: str) -> None: + """Appends an user comment string to the generated Ogg Opus stream. + + Because such user comments are stored in Ogg Opus stream headers, calling + this method after they have been written (i.e., a call to the `write()` + method has been made) will have no effect. No user comments are written + by default. + + Comment strings are meant to be short, for the purposes of stream + identification only, in the `KEY=VALUE` format. The headers format provides + some leeway to allow longer and differently formatted comments, however. + For more information and a list of conventional comment string key values, + please check out .""" + self._user_comments.append(comment) + + def _write_to_oggopus(self, pcm: memoryview, flush: bool = False) -> None: assert self._encoder is not None @@ -318,15 +339,22 @@ def _make_comment_header(self): """ signature = b"OpusTags" - vendor_string = b"ENCODER=PyOgg" + vendor_string = f"PyOgg {__version__}".encode("utf-8") vendor_string_length = struct.pack("