avb++.h

#include <primo/avblocks/avb++.h>

Includes

  • primo/avblocks/avb.h

  • primo/platform/error_facility.h

  • primo/platform/reference++.h

  • primo/platform/ustring.h

  • string

  • concepts

  • type_traits

  • memory

  • functional

  • optional

  • stdexcept

Classes

Name

Description

primo::avblocks::modern::TAVBlocksException

Exception thrown when an AVBlocks operation fails.

primo::avblocks::modern::TAudioStreamInfo

Fluent wrapper for primo::codecs::AudioStreamInfo, which describes an elementary audio stream.

primo::avblocks::modern::TDataStreamInfo

Fluent wrapper for a generic data stream.

primo::avblocks::modern::TErrorInfo

Copy of the error information reported by AVBlocks: facility, code, message and hint.

primo::avblocks::modern::TLibrary

RAII owner of the AVBlocks library and a static facade over primo::avblocks::Library.

primo::avblocks::modern::TLicenseInfo

Fluent wrapper for primo::license::LicenseInfo, which describes the license state of the library.

primo::avblocks::modern::TMediaBuffer

Fluent wrapper for primo::codecs::MediaBuffer, a buffer for media data.

primo::avblocks::modern::TMediaInfoT

Fluent wrapper for primo::avblocks::MediaInfo, which analyzes a file or a stream and reports its type and properties.

primo::avblocks::modern::TMediaPin

Fluent wrapper for primo::avblocks::MediaPin, which represents an elementary media stream.

primo::avblocks::modern::TMediaPinList

Non-owning view of a MediaPinList.

primo::avblocks::modern::TMediaSample

Fluent wrapper for primo::codecs::MediaSample, one or more samples of audio or video data.

primo::avblocks::modern::TMediaSocketList

Non-owning view of a MediaSocketList.

primo::avblocks::modern::TMediaSocketT

Fluent wrapper for primo::avblocks::MediaSocket, an input or output point of a transcoder.

primo::avblocks::modern::TMetaAttribute

Fluent wrapper for primo::codecs::MetaAttribute.

primo::avblocks::modern::TMetaAttributeList

Non-owning view of a MetaAttributeList.

primo::avblocks::modern::TMetaPicture

Fluent wrapper for primo::codecs::MetaPicture.

primo::avblocks::modern::TMetaPictureList

Non-owning view of a MetaPictureList.

primo::avblocks::modern::TMetadata

Fluent wrapper for primo::codecs::Metadata.

primo::avblocks::modern::TParamsMixin

CRTP mixin that adds the typed, fluent addParam() to a class that owns a primo::avblocks::ParameterList (TMediaPin and TMediaSocketT).

primo::avblocks::modern::TStreamInfo

CRTP base for TAudioStreamInfo, TVideoStreamInfo, and TDataStreamInfo.

primo::avblocks::modern::TTranscoderCallback

Internal adapter that bridges std::function callbacks to the raw primo::avblocks::TranscoderCallback interface.

primo::avblocks::modern::TTranscoderT

Fluent wrapper for primo::avblocks::Transcoder, which encodes, decodes and transforms audio and video.

primo::avblocks::modern::TVideoStreamInfo

Fluent wrapper for primo::codecs::VideoStreamInfo, which describes an elementary video stream.

Namespaces

  • primo

  • primo::avblocks

  • primo::avblocks::modern

Macros and Functions

Defines

TRUE
FALSE

Source

   1#pragma once
   2
   3#include <primo/avblocks/avb.h>
   4#include <primo/platform/error_facility.h>
   5#include <primo/platform/reference++.h>
   6#include <primo/platform/ustring.h>
   7
   8#include <string>
   9#include <concepts>
  10#include <type_traits>
  11#include <memory>
  12#include <functional>
  13#include <optional>
  14#include <stdexcept>
  15
  16#ifndef TRUE
  17#define TRUE 1
  18#endif
  19
  20#ifndef FALSE
  21#define FALSE 0
  22#endif
  23
  24namespace primo::avblocks::modern {
  25
  26/**
  27 * RAII owner of the AVBlocks library and a static facade over @c primo::avblocks::Library.
  28 *
  29 * Constructing a @c TLibrary initializes the library and destroying it shuts the library down. Create one
  30 * instance before using any other class in this header and keep it alive for as long as AVBlocks is used.
  31 * The static members mirror the functions of the @c primo::avblocks::Library namespace.
  32 *
  33 * Not copyable and not movable.
  34 * @code
  35 * TLibrary lib;
  36 * std::cout << TLibrary::description() << std::endl;
  37 * @endcode
  38 */
  39class TLibrary {
  40public:
  41    /// Initializes the AVBlocks library.
  42    TLibrary() {
  43        primo::avblocks::Library::initialize();
  44    }
  45    
  46    /// Shuts the AVBlocks library down and frees the resources it uses.
  47    ~TLibrary() {
  48        primo::avblocks::Library::shutdown();
  49    }
  50    
  51    TLibrary(const TLibrary&) = delete;
  52    TLibrary& operator=(const TLibrary&) = delete;
  53    TLibrary(TLibrary&&) = delete;
  54    TLibrary& operator=(TLibrary&&) = delete;
  55
  56    // ---- Static accessors mirroring primo::avblocks::Library namespace ----
  57
  58    /**
  59     * Initializes the library. Must be called once before the library is used unless a @c TLibrary instance
  60     * is created.
  61     * @return @c true if the library is initialized successfully.
  62     */
  63    static bool initialize() {
  64        return primo::avblocks::Library::initialize() == TRUE;
  65    }
  66
  67    /// Closes the library and frees the resources it uses. Call it when the library is not needed anymore.
  68    static void shutdown() {
  69        primo::avblocks::Library::shutdown();
  70    }
  71
  72    /**
  73     * Returns the build description: full version, architecture, configuration (Release/Debug),
  74     * license (OEM/Demo) and platform.
  75     */
  76    static const char* description() {
  77        return primo::avblocks::Library::description();
  78    }
  79
  80    /// Returns the library major version.
  81    static int32_t majorVersion() {
  82        return primo::avblocks::Library::majorVersion();
  83    }
  84
  85    /// Returns the library minor version.
  86    static int32_t minorVersion() {
  87        return primo::avblocks::Library::minorVersion();
  88    }
  89
  90    /// Returns the library patch version. It changes for bug fixes and small improvements that do not affect the API.
  91    static int32_t patchVersion() {
  92        return primo::avblocks::Library::patchVersion();
  93    }
  94
  95    /**
  96     * Sets whether TLS (Transport Layer Security) is used to validate the license. TLS is not used by default.
  97     * Call it before @c setLicense() to have an effect.
  98     * @param useTls @c true to validate the license over TLS.
  99     */
 100    static void setLicenseTls(bool useTls) {
 101        primo::avblocks::Library::setLicenseTls(useTls ? TRUE : FALSE);
 102    }
 103
 104    /**
 105     * Sets a license string. The license is appended to the library license state.
 106     * @param license The license string. If @c nullptr the library license state is cleared.
 107     * @return The license status after the license is set, as a combination of
 108     *         @c primo::license::LicenseStatusFlags::Enum flags.
 109     */
 110    static int32_t setLicense(const char* license) {
 111        return primo::avblocks::Library::setLicense(license);
 112    }
 113};
 114
 115/**
 116 * Fluent wrapper for @c primo::license::LicenseInfo, which describes the license state of the library.
 117 *
 118 * Owns the raw object through a @c primo::ref. Not copyable, movable.
 119 */
 120class TLicenseInfo {
 121    primo::ref<primo::license::LicenseInfo> info_;
 122
 123public:
 124    /**
 125     * Creates a license info object for the library (@c Library::createLicenseInfo).
 126     */
 127    TLicenseInfo()
 128        : info_(primo::avblocks::Library::createLicenseInfo()) {}
 129
 130    /**
 131     * Takes ownership of an existing @c LicenseInfo reference.
 132     */
 133    explicit TLicenseInfo(primo::ref<primo::license::LicenseInfo> info)
 134        : info_(std::move(info)) {}
 135
 136    // Non-copyable, movable
 137    TLicenseInfo(const TLicenseInfo&) = delete;
 138    TLicenseInfo& operator=(const TLicenseInfo&) = delete;
 139    /// @{
 140    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
 141    TLicenseInfo(TLicenseInfo&&) = default;
 142    TLicenseInfo& operator=(TLicenseInfo&&) = default;
 143    /// @}
 144
 145    /// Returns the current license status as a combination of LicenseStatusFlags::Enum flags.
 146    primo::license::LicenseStatusFlags::Enum licenseStatus() const {
 147        return static_cast<primo::license::LicenseStatusFlags::Enum>(info_->licenseStatus());
 148    }
 149
 150    /// Returns true if the specified product/feature is licensed.
 151    /// Pass nullptr for product/feature to use library defaults.
 152    bool isLicensed(const char* product = nullptr, const char* feature = nullptr) const {
 153        return info_->isLicensed(product, feature) == TRUE;
 154    }
 155
 156    /// Returns the raw underlying pointer. For interop with the core API only.
 157    primo::license::LicenseInfo* get() const { return info_.get(); }
 158};
 159
 160/**
 161 * Copy of the error information reported by AVBlocks: facility, code, message and hint.
 162 *
 163 * The strings are copied out of the source @c primo::error::ErrorInfo, so a @c TErrorInfo stays valid after
 164 * the source object is gone. A default-constructed @c TErrorInfo is empty.
 165 */
 166class TErrorInfo {
 167    int32_t facility_{};
 168    int32_t code_{};
 169    std::string message_;
 170    std::string hint_;
 171
 172public:
 173    /// Creates an empty error info: zero facility and code, empty message and hint.
 174    TErrorInfo() = default;
 175
 176    /**
 177     * Copies the error information from @p error.
 178     * @param error The error to copy. If @c nullptr the result is an empty error info.
 179     */
 180    explicit TErrorInfo(const primo::error::ErrorInfo* error) {
 181        if (error) {
 182            facility_ = error->facility();
 183            code_     = error->code();
 184            if (error->message()) message_ = std::string(primo::ustring(error->message()));
 185            if (error->hint())    hint_    = std::string(primo::ustring(error->hint()));
 186        }
 187    }
 188
 189    /// Returns the error facility, i.e. the component that reports the error.
 190    int32_t            facility() const { return facility_; }
 191    /// Returns the error code. Its meaning depends on the facility.
 192    int32_t            code()     const { return code_; }
 193    /// Returns the error message, a textual representation of the error code. Empty if not set.
 194    const std::string& message()  const { return message_; }
 195    /// Returns a diagnostic hint, suitable for error logs and debugging. Empty if not set.
 196    const std::string& hint()     const { return hint_; }
 197};
 198
 199/**
 200 * Exception thrown when an AVBlocks operation fails.
 201 *
 202 * @c what() returns the context of the failure followed by the error message, when there is one.
 203 * Use @c error() to get the complete error information.
 204 */
 205class TAVBlocksException : public std::runtime_error {
 206    TErrorInfo error_;
 207public:
 208    /**
 209     * Creates the exception.
 210     * @param context Short description of the failed operation, e.g. "Failed to open transcoder".
 211     * @param error   Error information reported by AVBlocks.
 212     */
 213    TAVBlocksException(const char* context, const TErrorInfo& error)
 214        : std::runtime_error(
 215            error.message().empty()
 216                ? std::string(context)
 217                : std::string(context) + ": " + error.message())
 218        , error_(error) {}
 219
 220    /// Returns the error information reported by AVBlocks.
 221    const TErrorInfo& error() const { return error_; }
 222};
 223
 224/**
 225 * Fluent wrapper for @c primo::codecs::MediaBuffer, a buffer for media data.
 226 *
 227 * A buffer has a capacity, which is the size of its memory block, and a data range, the part of the block that
 228 * holds valid data. The valid data is contiguous and can start at any offset in the block.
 229 *
 230 * Owns the raw object through a @c primo::ref. Not copyable, movable.
 231 */
 232class TMediaBuffer {
 233    primo::ref<primo::codecs::MediaBuffer> buffer_;
 234
 235public:
 236    /// Creates an empty buffer with zero capacity.
 237    TMediaBuffer()
 238        : buffer_(primo::avblocks::Library::createMediaBuffer()) {}
 239
 240    /**
 241     * Creates a buffer with internal storage.
 242     * @param capacity Buffer capacity in bytes.
 243     */
 244    explicit TMediaBuffer(int32_t capacity)
 245        : buffer_(primo::avblocks::Library::createMediaBuffer(capacity)) {}
 246
 247    /**
 248     * Takes ownership of an existing @c MediaBuffer reference.
 249     */
 250    explicit TMediaBuffer(primo::ref<primo::codecs::MediaBuffer> buffer)
 251        : buffer_(std::move(buffer)) {}
 252
 253    /**
 254     * Wraps a non-owned @c MediaBuffer pointer, retaining it.
 255     */
 256    explicit TMediaBuffer(primo::codecs::MediaBuffer* buffer)
 257        : buffer_(buffer) { if (buffer) buffer->retain(); }
 258
 259    // Delete copy operations
 260    TMediaBuffer(const TMediaBuffer&) = delete;
 261    TMediaBuffer& operator=(const TMediaBuffer&) = delete;
 262
 263    // Enable move operations
 264    /// @{
 265    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
 266    TMediaBuffer(TMediaBuffer&&) = default;
 267    TMediaBuffer& operator=(TMediaBuffer&&) = default;
 268    /// @}
 269
 270    /// @{
 271    /**
 272     * Attaches an external memory block as the buffer storage. Any internal storage is freed.
 273     * The block is not copied and must stay valid for as long as it is attached.
 274     * @param data Pointer to the memory block.
 275     * @param size Size of the block in bytes.
 276     * @param copy When @c true (default) the whole block is marked as valid data, i.e. @c dataSize() equals
 277     *             @p size. When @c false the data size is 0.
 278     * @return This object, for chaining.
 279     */
 280    TMediaBuffer&& attach(const uint8_t* data, size_t size, bool copy = true) && {
 281        buffer_->attach(const_cast<uint8_t*>(data), static_cast<int32_t>(size), copy ? TRUE : FALSE);
 282        return std::move(*this);
 283    }
 284
 285    TMediaBuffer& attach(const uint8_t* data, size_t size, bool copy = true) & {
 286        buffer_->attach(const_cast<uint8_t*>(data), static_cast<int32_t>(size), copy ? TRUE : FALSE);
 287        return *this;
 288    }
 289    /// @}
 290
 291    // -- Getters --
 292
 293    /**
 294     * Returns a pointer to the start of the buffer block. It is valid only when @c capacity() is greater than 0.
 295     */
 296    uint8_t*    start()           const { return buffer_->start(); }
 297    /// Returns the size of the buffer block in bytes. Can be zero.
 298    int32_t     capacity()        const { return buffer_->capacity(); }
 299    /**
 300     * Returns @c true if the buffer uses an external memory block attached with @c attach().
 301     */
 302    bool        external()        const { return buffer_->external() == TRUE; }
 303    /**
 304     * Returns a pointer to the first byte of valid data. Same as @c start() + @c dataOffset().
 305     */
 306    const uint8_t* data()         const { return buffer_->data(); }
 307    /**
 308     * Returns the size of the valid data in bytes, from 0 to @c capacity().
 309     */
 310    int32_t     dataSize()        const { return buffer_->dataSize(); }
 311    /// Returns the offset in bytes at which the valid data starts in the buffer block.
 312    int32_t     dataOffset()      const { return buffer_->dataOffset(); }
 313    /**
 314     * Returns the free space in bytes that can be used to append data without reallocation: @c capacity() - @c dataSize().
 315     */
 316    int32_t     freeSpace()       const { return buffer_->freeSpace(); }
 317    /**
 318     * Returns the contiguous free space in bytes after the valid data, which can be used to append data without
 319     * @c normalize(): @c capacity() - @c dataSize() - @c dataOffset(). It is at most @c freeSpace().
 320     */
 321    int32_t     freeLinearSpace() const { return buffer_->freeLinearSpace(); }
 322
 323    // -- Fluent setter --
 324
 325    /**
 326     * Sets the range of valid data. Both values are validated so that the range is always inside the buffer.
 327     * @param offset Offset at which valid data starts, from 0 to @c capacity() - 1.
 328     * @param size   Size of the valid data, from 0 to @c capacity().
 329     * @return This object, for chaining.
 330     */
 331    TMediaBuffer& setData(int32_t offset, int32_t size) {
 332        buffer_->setData(offset, size);
 333        return *this;
 334    }
 335
 336    // -- Operations --
 337
 338    /**
 339     * Allocates internal storage. Storage is allocated only if @p size exceeds the current capacity;
 340     * the actual capacity may be larger than requested.
 341     * @param size     Requested size in bytes.
 342     * @param keepData Whether to keep the existing data when the storage is reallocated.
 343     * @return @c true on success.
 344     */
 345    bool alloc(int32_t size, bool keepData) {
 346        return buffer_->alloc(size, keepData ? TRUE : FALSE) == TRUE;
 347    }
 348
 349    /**
 350     * Allocates internal storage with the specified memory alignment. Storage is allocated only if @p size
 351     * exceeds the current capacity.
 352     * @param alignment Alignment in bytes. Must be a power of 2 and at least 8.
 353     * @param size      Requested size in bytes.
 354     * @param keepData  Whether to keep the existing data when the storage is reallocated.
 355     * @return @c true on success.
 356     */
 357    bool alignedAlloc(int32_t alignment, int32_t size, bool keepData) {
 358        return buffer_->alignedAlloc(alignment, size, keepData ? TRUE : FALSE) == TRUE;
 359    }
 360
 361    /// Frees the internal storage. Does nothing if an external memory block is attached. Returns this object, for chaining.
 362    TMediaBuffer& free() {
 363        buffer_->free();
 364        return *this;
 365    }
 366
 367    /// Clears the data. The capacity does not change. Returns this object, for chaining.
 368    TMediaBuffer& clear() {
 369        buffer_->clear();
 370        return *this;
 371    }
 372
 373    /**
 374     * Moves the valid data to the start of the buffer, which maximizes the free linear space. Useful after data
 375     * is added by copying it directly after the existing data and then extended with @c setData().
 376     * The capacity does not change. Returns this object, for chaining.
 377     */
 378    TMediaBuffer& normalize() {
 379        buffer_->normalize();
 380        return *this;
 381    }
 382
 383    /**
 384     * Appends data to the end of the valid data. Internal storage grows when needed; external storage does not.
 385     * @param data     Pointer to the data to append.
 386     * @param dataSize Number of bytes to append.
 387     * @return @c true on success.
 388     */
 389    bool append(const void* data, int32_t dataSize) {
 390        return buffer_->append(data, dataSize) == TRUE;
 391    }
 392
 393    /**
 394     * Removes data from the beginning of the valid data. The capacity does not change.
 395     * @param dataSize Number of bytes to remove.
 396     * @return This object, for chaining.
 397     */
 398    TMediaBuffer& remove(int32_t dataSize) {
 399        buffer_->remove(dataSize);
 400        return *this;
 401    }
 402
 403    /**
 404     * Detaches the external memory block. Returns a pointer to it, or @c nullptr if none was attached.
 405     */
 406    uint8_t* detach() {
 407        return buffer_->detach();
 408    }
 409
 410    /// Creates a deep copy. If the buffer uses an external memory block only the pointer to it is copied.
 411    TMediaBuffer clone() const {
 412        return TMediaBuffer(primo::ref<primo::codecs::MediaBuffer>(buffer_->clone()));
 413    }
 414
 415    /// Returns the raw underlying pointer. For interop with the core API only.
 416    primo::codecs::MediaBuffer* get() const { return buffer_.get(); }
 417};
 418
 419/**
 420 * Fluent wrapper for @c primo::codecs::MediaSample, one or more samples of audio or video data.
 421 *
 422 * Holds compressed or uncompressed data in a @c TMediaBuffer together with timing and stream information.
 423 * Owns the raw object through a @c primo::ref. Not copyable, movable.
 424 */
 425class TMediaSample {
 426    primo::ref<primo::codecs::MediaSample> sample_;
 427    
 428public:
 429    /// Creates an empty media sample.
 430    TMediaSample() 
 431        : sample_(primo::avblocks::Library::createMediaSample()) {}
 432    
 433    /**
 434     * Takes ownership of an existing @c MediaSample reference.
 435     */
 436    explicit TMediaSample(primo::ref<primo::codecs::MediaSample> sample)
 437        : sample_(std::move(sample)) {}
 438    
 439    // Delete copy operations
 440    TMediaSample(const TMediaSample&) = delete;
 441    TMediaSample& operator=(const TMediaSample&) = delete;
 442    
 443    // Enable move operations
 444    /// @{
 445    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
 446    TMediaSample(TMediaSample&&) = default;
 447    TMediaSample& operator=(TMediaSample&&) = default;
 448    /// @}
 449
 450    // -- Fluent buffer setters --
 451
 452    /// @{
 453    /**
 454     * Sets the buffer of the sample. The sample retains the buffer and releases the previous one.
 455     * @param buf The new buffer.
 456     * @return This object, for chaining.
 457     */
 458    TMediaSample&& buffer(TMediaBuffer&& buf) && {
 459        sample_->setBuffer(buf.get());
 460        return std::move(*this);
 461    }
 462
 463    TMediaSample& buffer(TMediaBuffer&& buf) & {
 464        sample_->setBuffer(buf.get());
 465        return *this;
 466    }
 467    /// @}
 468
 469    /// Removes the buffer from the sample. Returns this object, for chaining.
 470    TMediaSample& buffer(std::nullptr_t) {
 471        sample_->setBuffer(nullptr);
 472        return *this;
 473    }
 474
 475    // -- Const getters --
 476
 477    /// Returns the buffer of the sample. The returned wrapper is invalid if the sample has no buffer.
 478    TMediaBuffer buffer() const {
 479        return TMediaBuffer(sample_->buffer());
 480    }
 481
 482    /// Returns the presentation timestamp (PTS) of the sample in seconds.
 483    double  startTime()    const { return sample_->startTime(); }
 484    /// Returns the end time of the sample in seconds. It is optional and valid only if greater than the start time.
 485    double  endTime()      const { return sample_->endTime(); }
 486    /**
 487     * Returns the sample flags, a combination of @c primo::codecs::MediaSampleFlags::Enum values.
 488     */
 489    int32_t flags()        const { return sample_->flags(); }
 490    /// Returns the zero-based index of the elementary stream in the container to which the sample belongs.
 491    int32_t streamNumber() const { return sample_->streamNumber(); }
 492
 493    /// Returns the picture type (I/P/B/etc.) of a demuxed, decoded or encoded frame. Currently set only by the MPEG-2 encoder.
 494    primo::codecs::PictureType::Enum pictureType() const {
 495        return sample_->pictureType();
 496    }
 497
 498    /// Returns the frame type of a demuxed, decoded or encoded frame. Currently used only by the G.711 a-law and u-law codecs.
 499    primo::codecs::FrameType::Enum frameType() const {
 500        return sample_->frameType();
 501    }
 502
 503    /**
 504     * Calculates the exact number of bytes needed for a video frame with the given properties.
 505     * @param width       Frame width in pixels.
 506     * @param height      Frame height in pixels.
 507     * @param colorFormat How colors are coded in the frame.
 508     * @return The size in bytes, or 0 if it cannot be calculated.
 509     */
 510    int32_t videoBufferSizeInBytes(int32_t width, int32_t height,
 511                                   primo::codecs::ColorFormat::Enum colorFormat) const {
 512        return sample_->videoBufferSizeInBytes(width, height, colorFormat);
 513    }
 514
 515    // -- Fluent setters --
 516
 517    /**
 518     * Sets the presentation timestamp (PTS) of the sample.
 519     * @param time Time in seconds.
 520     * @return This object, for chaining.
 521     */
 522    TMediaSample& startTime(double time)    { sample_->setStartTime(time); return *this; }
 523    /**
 524     * Sets the end time of the sample. It is optional and valid only if greater than the start time.
 525     * @param time Time in seconds.
 526     * @return This object, for chaining.
 527     */
 528    TMediaSample& endTime(double time)      { sample_->setEndTime(time);   return *this; }
 529    /**
 530     * Sets the sample flags.
 531     * @param f A combination of @c primo::codecs::MediaSampleFlags::Enum values.
 532     * @return This object, for chaining.
 533     */
 534    TMediaSample& flags(int32_t f)          { sample_->setFlags(f);        return *this; }
 535    /**
 536     * Sets the elementary stream to which the sample belongs.
 537     * @param n Zero-based index of the elementary stream in the container.
 538     * @return This object, for chaining.
 539     */
 540    TMediaSample& streamNumber(int32_t n)   { sample_->setStreamNumber(n); return *this; }
 541
 542    /**
 543     * Sets the required picture type (I/P/B/etc.). Currently ignored by all encoders.
 544     * @return This object, for chaining.
 545     */
 546    TMediaSample& pictureType(primo::codecs::PictureType::Enum pt) {
 547        sample_->setPictureType(pt);
 548        return *this;
 549    }
 550
 551    /**
 552     * Sets the frame type. Currently used only by the G.711 a-law and u-law codecs.
 553     * @return This object, for chaining.
 554     */
 555    TMediaSample& frameType(primo::codecs::FrameType::Enum ft) {
 556        sample_->setFrameType(ft);
 557        return *this;
 558    }
 559
 560    // -- Operations --
 561
 562    /// Resets the sample to its default values and clears the data in its buffer. Returns this object, for chaining.
 563    TMediaSample& reset() {
 564        sample_->reset();
 565        return *this;
 566    }
 567
 568    /// Creates a deep copy of the sample.
 569    TMediaSample clone() const {
 570        return TMediaSample(primo::ref<primo::codecs::MediaSample>(sample_->clone()));
 571    }
 572
 573    /// Returns the raw underlying pointer. For interop with the core API only.
 574    primo::codecs::MediaSample* get() const { return sample_.get(); }
 575};
 576
 577// ---- Metadata wrappers -------------------------------------------------------
 578
 579/**
 580 * Fluent wrapper for @c primo::codecs::MetaAttribute.
 581 *
 582 * Owns the raw object through a @c primo::ref<MetaAttribute>.
 583 * Non-copyable, movable. Constructed from a factory-owned object (via
 584 * @c primo::ref) or from a non-owned pointer (via raw-pointer ctor, which
 585 * retains it).
 586 */
 587class TMetaAttribute {
 588    primo::ref<primo::codecs::MetaAttribute> attr_;
 589
 590public:
 591    /// Creates an empty meta attribute.
 592    TMetaAttribute()
 593        : attr_(primo::avblocks::Library::createMetaAttribute()) {}
 594
 595    /**
 596     * Takes ownership of an existing @c MetaAttribute reference.
 597     */
 598    explicit TMetaAttribute(primo::ref<primo::codecs::MetaAttribute> attr)
 599        : attr_(std::move(attr)) {}
 600
 601    /// Wraps a non-owned @c MetaAttribute pointer, retaining it.
 602    explicit TMetaAttribute(primo::codecs::MetaAttribute* attr)
 603        : attr_(attr) { if (attr) attr->retain(); }
 604
 605    TMetaAttribute(const TMetaAttribute&) = delete;
 606    TMetaAttribute& operator=(const TMetaAttribute&) = delete;
 607    /// @{
 608    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
 609    TMetaAttribute(TMetaAttribute&&) = default;
 610    TMetaAttribute& operator=(TMetaAttribute&&) = default;
 611    /// @}
 612
 613    // -- Getters --
 614
 615    /// Returns the attribute name as an ANSI string (see @c primo::codecs::Meta constants).
 616    const char* name() const { return attr_ ? attr_->name() : nullptr; }
 617
 618    /// Returns the attribute value as a UTF-8 @c std::string.
 619    std::string value() const {
 620        return attr_ ? std::string(primo::ustring(attr_->value())) : std::string{};
 621    }
 622
 623    /// Returns @c true if the attribute is immutable.
 624    bool immutable() const { return attr_ && attr_->immutable() == TRUE; }
 625
 626    // -- Fluent setters --
 627
 628    /// Sets the attribute name (@p name is an ANSI string).
 629    TMetaAttribute& name(const char* n) { if (attr_) attr_->setName(n); return *this; }
 630
 631    /// Sets the attribute value (@p val is a UTF-8 C-string).
 632    TMetaAttribute& value(const char* val) {
 633        if (attr_) attr_->setValue(primo::ustring(val));
 634        return *this;
 635    }
 636
 637    // -- Operations --
 638
 639    /// Creates a full independent clone. The returned object is always mutable.
 640    TMetaAttribute clone() const {
 641        return TMetaAttribute(
 642            primo::ref<primo::codecs::MetaAttribute>(attr_->clone()));
 643    }
 644
 645    /// Returns @c true when this handle wraps a non-null underlying object.
 646    bool valid() const { return attr_.get() != nullptr; }
 647
 648    /// Returns the raw underlying pointer. For interop with the core API only.
 649    primo::codecs::MetaAttribute* get() const { return attr_.get(); }
 650};
 651
 652/**
 653 * Non-owning view of a @c MetaAttributeList.
 654 *
 655 * Returned by @c TMetadata::attributes().
 656 */
 657class TMetaAttributeList {
 658    primo::codecs::MetaAttributeList* list_;
 659
 660public:
 661    /**
 662     * Wraps a non-owned @c MetaAttributeList pointer.
 663     */
 664    explicit TMetaAttributeList(primo::codecs::MetaAttributeList* list) : list_(list) {}
 665
 666    /**
 667     * Proxy returned by the non-const lvalue @c operator[](int32_t).
 668     * Supports both read (implicit conversion to @c TMetaAttribute) and
 669     * write (@c operator= calls @c setAt on the parent list).
 670     */
 671    struct ItemRef {
 672        /// The list this proxy refers to.
 673        TMetaAttributeList& list_;
 674        /// Index of the item the proxy refers to.
 675        int32_t index_;
 676        /// Read — implicit conversion to a non-owning attribute wrapper.
 677        operator TMetaAttribute() const { return TMetaAttribute(list_.list_->at(index_)); }
 678        /// Write — calls @c setAt; returns @c true on success.
 679        bool operator=(TMetaAttribute&& a) { return list_.list_->setAt(index_, a.get()) == TRUE; }
 680    };
 681
 682    /// Returns the number of attributes.
 683    int32_t count() const { return list_ ? list_->count() : 0; }
 684
 685    /// Returns the attribute at @p index (non-owning, retaining).
 686    TMetaAttribute at(int32_t index) const { return TMetaAttribute(list_->at(index)); }
 687
 688    /// Non-const lvalue subscript — returns a proxy supporting read and @c setAt write.
 689    ItemRef operator[](int32_t index) & { return {*this, index}; }
 690
 691    /// Returns the first attribute whose name matches @p name (non-owning, retaining).
 692    /// Check @c TMetaAttribute::valid() before use.
 693    TMetaAttribute itemByName(const char* name) const {
 694        return TMetaAttribute(list_ ? list_->itemByName(name) : nullptr);
 695    }
 696
 697    /// Subscript by name — same as @c itemByName(@p name).
 698    TMetaAttribute operator[](const char* name) const & {
 699        return TMetaAttribute(list_ ? list_->itemByName(name) : nullptr);
 700    }
 701
 702    /// Replaces the attribute at @p index with @p attr. Returns @c true on success.
 703    bool setAt(int32_t index, TMetaAttribute&& attr) {
 704        return list_ && list_->setAt(index, attr.get()) == TRUE;
 705    }
 706
 707    /// Inserts @p attr before @p index, shifting later items. Returns @c true on success.
 708    bool insert(int32_t index, TMetaAttribute&& attr) {
 709        return list_ && list_->insert(index, attr.get()) == TRUE;
 710    }
 711
 712    /// Returns @c true when the list (and its parent @c Metadata) is immutable.
 713    bool immutable() const { return list_ && list_->immutable() == TRUE; }
 714
 715    /// Removes the attribute at @p index. Returns @c true on success.
 716    bool remove(int32_t index) { return list_ && list_->remove(index) == TRUE; }
 717
 718    /// Removes all attributes from the list. Returns @c true on success.
 719    bool clear() { return list_ && list_->clear() == TRUE; }
 720};
 721
 722/**
 723 * Fluent wrapper for @c primo::codecs::MetaPicture.
 724 *
 725 * Owns the raw object through a @c primo::ref<MetaPicture>.
 726 * Non-copyable, movable.
 727 */
 728class TMetaPicture {
 729    primo::ref<primo::codecs::MetaPicture> pic_;
 730
 731public:
 732    /// Creates an empty meta picture.
 733    TMetaPicture()
 734        : pic_(primo::avblocks::Library::createMetaPicture()) {}
 735
 736    /**
 737     * Takes ownership of an existing @c MetaPicture reference.
 738     */
 739    explicit TMetaPicture(primo::ref<primo::codecs::MetaPicture> pic)
 740        : pic_(std::move(pic)) {}
 741
 742    /// Wraps a non-owned @c MetaPicture pointer, retaining it.
 743    explicit TMetaPicture(primo::codecs::MetaPicture* pic)
 744        : pic_(pic) { if (pic) pic->retain(); }
 745
 746    TMetaPicture(const TMetaPicture&) = delete;
 747    TMetaPicture& operator=(const TMetaPicture&) = delete;
 748    /// @{
 749    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
 750    TMetaPicture(TMetaPicture&&) = default;
 751    TMetaPicture& operator=(TMetaPicture&&) = default;
 752    /// @}
 753
 754    // -- Getters --
 755
 756    /// Returns the MIME type as an ANSI string (see @c primo::codecs::MimeType constants).
 757    const char*                              mimeType()    const { return pic_ ? pic_->mimeType() : nullptr; }
 758    /// Returns the picture type (front cover, back cover, artist, etc.).
 759    primo::codecs::MetaPictureType::Enum     pictureType() const { return pic_ ? pic_->pictureType() : primo::codecs::MetaPictureType::Other; }
 760    /// Returns the picture description as a UTF-8 @c std::string (empty if none).
 761    std::string description() const {
 762        return (pic_ && pic_->description())
 763            ? std::string(primo::ustring(pic_->description()))
 764            : std::string{};
 765    }
 766    /**
 767     * Returns a pointer to the image data (jpeg, png, etc., as given by @c mimeType()). The size is @c dataSize().
 768     */
 769    const uint8_t* data()     const { return pic_ ? pic_->data()     : nullptr; }
 770    /// Returns the number of image bytes.
 771    int32_t        dataSize() const { return pic_ ? pic_->dataSize() : 0; }
 772    /**
 773     * Returns @c true if the picture is immutable.
 774     */
 775    bool           immutable() const { return pic_ && pic_->immutable() == TRUE; }
 776
 777    // -- Fluent setters --
 778
 779    /**
 780     * Sets the MIME type (see @c primo::codecs::MimeType constants).
 781     * @return This object, for chaining.
 782     */
 783    TMetaPicture& mimeType(const char* mt)                          { if (pic_) pic_->setMimeType(mt); return *this; }
 784    /**
 785     * Sets the picture type (front cover, back cover, artist, etc.).
 786     * @return This object, for chaining.
 787     */
 788    TMetaPicture& pictureType(primo::codecs::MetaPictureType::Enum pt) { if (pic_) pic_->setPictureType(pt); return *this; }
 789    /**
 790     * Sets the picture description (@p desc is a UTF-8 C-string). Pass @c nullptr to clear it.
 791     * @return This object, for chaining.
 792     */
 793    TMetaPicture& description(const char* desc) {
 794        if (pic_) pic_->setDescription(primo::ustring(desc));
 795        return *this;
 796    }
 797    /**
 798     * Sets the image data. The data is copied.
 799     * @param d    Pointer to the image data.
 800     * @param size Size of the image in bytes.
 801     * @return This object, for chaining.
 802     */
 803    TMetaPicture& data(const void* d, int32_t size) { if (pic_) pic_->setData(d, size); return *this; }
 804
 805    // -- Operations --
 806
 807    /// Creates a full independent clone. The returned object is always mutable.
 808    TMetaPicture clone() const {
 809        return TMetaPicture(primo::ref<primo::codecs::MetaPicture>(pic_->clone()));
 810    }
 811
 812    /// Returns @c true when this handle wraps a non-null underlying object.
 813    bool valid() const { return pic_.get() != nullptr; }
 814
 815    /// Returns the raw underlying pointer. For interop with the core API only.
 816    primo::codecs::MetaPicture* get() const { return pic_.get(); }
 817};
 818
 819/**
 820 * Non-owning view of a @c MetaPictureList.
 821 *
 822 * Returned by @c TMetadata::pictures().
 823 */
 824class TMetaPictureList {
 825    primo::codecs::MetaPictureList* list_;
 826
 827public:
 828    /**
 829     * Wraps a non-owned @c MetaPictureList pointer.
 830     */
 831    explicit TMetaPictureList(primo::codecs::MetaPictureList* list) : list_(list) {}
 832
 833    /**
 834     * Proxy returned by the non-const lvalue @c operator[](int32_t).
 835     * Supports both read (implicit conversion to @c TMetaPicture) and
 836     * write (@c operator= calls @c setAt on the parent list).
 837     */
 838    struct ItemRef {
 839        /// The list this proxy refers to.
 840        TMetaPictureList& list_;
 841        /// Index of the item the proxy refers to.
 842        int32_t index_;
 843        /// Read — implicit conversion to a non-owning picture wrapper.
 844        operator TMetaPicture() const { return TMetaPicture(list_.list_->at(index_)); }
 845        /// Write — calls @c setAt; returns @c true on success.
 846        bool operator=(TMetaPicture&& p) { return list_.list_->setAt(index_, p.get()) == TRUE; }
 847    };
 848
 849    /// Returns the number of pictures.
 850    int32_t count() const { return list_ ? list_->count() : 0; }
 851
 852    /// Returns the picture at @p index (non-owning, retaining).
 853    TMetaPicture at(int32_t index) const { return TMetaPicture(list_->at(index)); }
 854
 855    /// Non-const lvalue subscript — returns a proxy supporting read and @c setAt write.
 856    ItemRef operator[](int32_t index) & { return {*this, index}; }
 857
 858    /// Const subscript — same as @c at(@p index).
 859    TMetaPicture operator[](int32_t index) const & { return TMetaPicture(list_->at(index)); }
 860
 861    /// Replaces the picture at @p index with @p pic. Returns @c true on success.
 862    bool setAt(int32_t index, TMetaPicture&& pic) {
 863        return list_ && list_->setAt(index, pic.get()) == TRUE;
 864    }
 865
 866    /// Inserts @p pic before @p index, shifting later items. Returns @c true on success.
 867    bool insert(int32_t index, TMetaPicture&& pic) {
 868        return list_ && list_->insert(index, pic.get()) == TRUE;
 869    }
 870
 871    /// Returns @c true when the list (and its parent @c Metadata) is immutable.
 872    bool immutable() const { return list_ && list_->immutable() == TRUE; }
 873
 874    /// Removes the picture at @p index. Returns @c true on success.
 875    bool remove(int32_t index) { return list_ && list_->remove(index) == TRUE; }
 876
 877    /// Removes all pictures from the list. Returns @c true on success.
 878    bool clear() { return list_ && list_->clear() == TRUE; }
 879};
 880
 881/**
 882 * Fluent wrapper for @c primo::codecs::Metadata.
 883 *
 884 * Owns the raw object through a @c primo::ref<Metadata>.
 885 * Non-copyable, movable. Obtained from @c TMediaSocketT::metadata() or
 886 * created directly via the default constructor.
 887 */
 888class TMetadata {
 889    primo::ref<primo::codecs::Metadata> meta_;
 890
 891public:
 892    /// Creates an empty metadata object.
 893    TMetadata()
 894        : meta_(primo::avblocks::Library::createMetadata()) {}
 895
 896    /**
 897     * Takes ownership of an existing @c Metadata reference.
 898     */
 899    explicit TMetadata(primo::ref<primo::codecs::Metadata> meta)
 900        : meta_(std::move(meta)) {}
 901
 902    /// Wraps a non-owned @c Metadata pointer, retaining it.
 903    explicit TMetadata(primo::codecs::Metadata* meta)
 904        : meta_(meta) { if (meta) meta->retain(); }
 905
 906    TMetadata(const TMetadata&) = delete;
 907    TMetadata& operator=(const TMetadata&) = delete;
 908    /// @{
 909    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
 910    TMetadata(TMetadata&&) = default;
 911    TMetadata& operator=(TMetadata&&) = default;
 912    /// @}
 913
 914    // -- List accessors --
 915
 916    /// Returns a non-owning view of the attribute list.
 917    TMetaAttributeList attributes() const {
 918        return TMetaAttributeList(meta_ ? meta_->attributes() : nullptr);
 919    }
 920
 921    /// Returns a non-owning view of the picture list.
 922    TMetaPictureList pictures() const {
 923        return TMetaPictureList(meta_ ? meta_->pictures() : nullptr);
 924    }
 925
 926    // -- List mutation --
 927
 928    /// @{
 929    /**
 930     * Appends an attribute to the attribute list.
 931     * @param attr The attribute to add. The list retains it.
 932     * @return This object, for chaining.
 933     */
 934    TMetadata&& addAttribute(TMetaAttribute&& attr) && {
 935        meta_->attributes()->add(attr.get());
 936        return std::move(*this);
 937    }
 938
 939    TMetadata& addAttribute(TMetaAttribute&& attr) & {
 940        meta_->attributes()->add(attr.get());
 941        return *this;
 942    }
 943    /// @}
 944
 945    /// @{
 946    /**
 947     * Appends a picture to the picture list.
 948     * @param pic The picture to add. The list retains it.
 949     * @return This object, for chaining.
 950     */
 951    TMetadata&& addPicture(TMetaPicture&& pic) && {
 952        meta_->pictures()->add(pic.get());
 953        return std::move(*this);
 954    }
 955
 956    TMetadata& addPicture(TMetaPicture&& pic) & {
 957        meta_->pictures()->add(pic.get());
 958        return *this;
 959    }
 960    /// @}
 961
 962    // -- Getters --
 963
 964    /**
 965     * Returns @c true if the metadata is immutable.
 966     */
 967    bool immutable() const { return meta_ && meta_->immutable() == TRUE; }
 968
 969    // -- Operations --
 970
 971    /// Creates a full independent clone. The returned object is always mutable.
 972    TMetadata clone() const {
 973        return TMetadata(primo::ref<primo::codecs::Metadata>(meta_->clone()));
 974    }
 975
 976    /// Returns @c true when this handle wraps a non-null underlying object.
 977    bool valid() const { return meta_.get() != nullptr; }
 978
 979    /// Returns the raw underlying pointer. For interop with the core API only.
 980    primo::codecs::Metadata* get() const { return meta_.get(); }
 981};
 982
 983// ---- StreamInfo CRTP base and subclasses -------------------------------------
 984
 985/**
 986 * CRTP base for @c TAudioStreamInfo, @c TVideoStreamInfo, and @c TDataStreamInfo.
 987 *
 988 * Holds the @c primo::ref<RawT> and exposes all properties defined on the
 989 * raw @c primo::codecs::StreamInfo interface, with fluent setters that return
 990 * @c Derived& (on lvalues) or @c Derived&& (on temporaries) so chaining works naturally
 991 * on the concrete subclass. The rvalue overloads let a chained temporary be returned or
 992 * moved, e.g. @c return @c TVideoStreamInfo().frameWidth(352).frameHeight(288);
 993 */
 994template<typename Derived, typename RawT>
 995class TStreamInfo {
 996protected:
 997    /// The wrapped raw stream info.
 998    primo::ref<RawT> info_;
 999
1000    /// Takes ownership of an existing raw stream info reference.
1001    explicit TStreamInfo(primo::ref<RawT> info)
1002        : info_(std::move(info)) {}
1003
1004    /// Wraps a non-owned pointer, retaining it. Used by subclass raw-pointer constructors.
1005    explicit TStreamInfo(RawT* info)
1006        : info_(info) { if (info) info->retain(); }
1007
1008    TStreamInfo(const TStreamInfo&) = delete;
1009    TStreamInfo& operator=(const TStreamInfo&) = delete;
1010    /// @{
1011    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
1012    TStreamInfo(TStreamInfo&&) = default;
1013    TStreamInfo& operator=(TStreamInfo&&) = default;
1014    /// @}
1015
1016private:
1017    Derived& self() { return static_cast<Derived&>(*this); }
1018    const Derived& self() const { return static_cast<const Derived&>(*this); }
1019
1020public:
1021    // -- Fluent setters --
1022
1023    /// @{
1024    /**
1025     * Sets the stream type.
1026     * @param type Major stream type.
1027     * @return This object, for chaining.
1028     */
1029    Derived& streamType(primo::codecs::StreamType::Enum type) & {
1030        info_->setStreamType(type);
1031        return self();
1032    }
1033
1034    Derived&& streamType(primo::codecs::StreamType::Enum type) && {
1035        info_->setStreamType(type);
1036        return std::move(self());
1037    }
1038    /// @}
1039
1040    /// @{
1041    /**
1042     * Sets the stream subtype.
1043     * @param subType Stream subtype.
1044     * @return This object, for chaining.
1045     */
1046    Derived& streamSubType(primo::codecs::StreamSubType::Enum subType) & {
1047        info_->setStreamSubType(subType);
1048        return self();
1049    }
1050
1051    Derived&& streamSubType(primo::codecs::StreamSubType::Enum subType) && {
1052        info_->setStreamSubType(subType);
1053        return std::move(self());
1054    }
1055    /// @}
1056
1057    /// @{
1058    /**
1059     * Sets the stream duration.
1060     * @param d Duration in seconds.
1061     * @return This object, for chaining.
1062     */
1063    Derived& duration(double d) & {
1064        info_->setDuration(d);
1065        return self();
1066    }
1067
1068    Derived&& duration(double d) && {
1069        info_->setDuration(d);
1070        return std::move(self());
1071    }
1072    /// @}
1073
1074    /// @{
1075    /**
1076     * Sets the stream bitrate.
1077     * @param bps Bitrate in bits per second.
1078     * @return This object, for chaining.
1079     */
1080    Derived& bitrate(int32_t bps) & {
1081        info_->setBitrate(bps);
1082        return self();
1083    }
1084
1085    Derived&& bitrate(int32_t bps) && {
1086        info_->setBitrate(bps);
1087        return std::move(self());
1088    }
1089    /// @}
1090
1091    /// @{
1092    /**
1093     * Sets the stream bitrate mode.
1094     * @param mode A @c primo::codecs::BitrateMode::Enum constant.
1095     * @return This object, for chaining.
1096     */
1097    Derived& bitrateMode(int32_t mode) & {
1098        info_->setBitrateMode(mode);
1099        return self();
1100    }
1101
1102    Derived&& bitrateMode(int32_t mode) && {
1103        info_->setBitrateMode(mode);
1104        return std::move(self());
1105    }
1106    /// @}
1107
1108    /// @{
1109    /**
1110     * Sets the ID of the elementary stream.
1111     * @param id Elementary stream ID.
1112     * @return This object, for chaining.
1113     */
1114    Derived& ID(int32_t id) & {
1115        info_->setID(id);
1116        return self();
1117    }
1118
1119    Derived&& ID(int32_t id) && {
1120        info_->setID(id);
1121        return std::move(self());
1122    }
1123    /// @}
1124
1125    /// @{
1126    /**
1127     * Sets the program to which the elementary stream belongs.
1128     * @param num Program number.
1129     * @return This object, for chaining.
1130     */
1131    Derived& programNumber(int32_t num) & {
1132        info_->setProgramNumber(num);
1133        return self();
1134    }
1135
1136    Derived&& programNumber(int32_t num) && {
1137        info_->setProgramNumber(num);
1138        return std::move(self());
1139    }
1140    /// @}
1141
1142    /// @{
1143    /**
1144     * Sets decoder specific configuration data. It is typically obtained from a demuxer and is required to decode certain stream types.
1145     * @param buf Buffer with the configuration data. Can be @c nullptr.
1146     * @return This object, for chaining.
1147     */
1148    Derived& configData(primo::codecs::MediaBuffer* buf) & {
1149        info_->setConfigData(buf);
1150        return self();
1151    }
1152
1153    Derived&& configData(primo::codecs::MediaBuffer* buf) && {
1154        info_->setConfigData(buf);
1155        return std::move(self());
1156    }
1157    /// @}
1158
1159    // -- Getters --
1160
1161    /// Returns the media type of the stream. It cannot be changed.
1162    primo::codecs::MediaType::Enum     mediaType()     const { return info_->mediaType(); }
1163    /// Returns the stream type.
1164    primo::codecs::StreamType::Enum    streamType()    const { return info_->streamType(); }
1165    /// Returns the stream subtype.
1166    primo::codecs::StreamSubType::Enum streamSubType() const { return info_->streamSubType(); }
1167    /// Returns the stream duration in seconds.
1168    double                             duration()      const { return info_->duration(); }
1169    /// Returns the stream bitrate in bits per second.
1170    int32_t                            bitrate()       const { return info_->bitrate(); }
1171    /**
1172     * Returns the stream bitrate mode, a @c primo::codecs::BitrateMode::Enum constant.
1173     */
1174    int32_t                            bitrateMode()   const { return info_->bitrateMode(); }
1175    /// Returns the ID of the elementary stream.
1176    int32_t                            ID()            const { return info_->ID(); }
1177    /// Returns the number of the program to which the elementary stream belongs.
1178    int32_t                            programNumber() const { return info_->programNumber(); }
1179    /**
1180     * Returns @c true if the object is immutable. An immutable object cannot be modified.
1181     */
1182    bool                               immutable()     const { return info_->immutable() == TRUE; }
1183    /**
1184     * Returns the decoder specific configuration data, or @c nullptr if not set.
1185     */
1186    primo::codecs::MediaBuffer*        configData()    const { return info_->configData(); }
1187
1188    // -- Operations --
1189
1190    /// Resets all fields to their defaults. Returns @c false if the object is immutable.
1191    bool reset() { return info_->reset() == TRUE; }
1192
1193    /// Returns a mutable deep copy. The cloned object is always mutable regardless
1194    /// of whether the source was immutable.
1195    Derived clone() const {
1196        return Derived(primo::ref<RawT>(static_cast<RawT*>(info_->clone())));
1197    }
1198
1199    /// Returns the raw underlying pointer. For interop with the C API only.
1200    RawT* get() const { return info_.get(); }
1201};
1202
1203/**
1204 * Fluent wrapper for @c primo::codecs::AudioStreamInfo, which describes an elementary audio stream.
1205 *
1206 * The properties common to all streams are inherited from @c TStreamInfo. The setters can be chained;
1207 * on a temporary they return the object by rvalue reference so that the chain can be returned or moved:
1208 * @code
1209 * TAudioStreamInfo audio = TAudioStreamInfo().channels(2).sampleRate(44100).bitsPerSample(16);
1210 * @endcode
1211 * Owns the raw object through a @c primo::ref. Not copyable, movable.
1212 */
1213class TAudioStreamInfo
1214    : public TStreamInfo<TAudioStreamInfo, primo::codecs::AudioStreamInfo> {
1215    using Base = TStreamInfo<TAudioStreamInfo, primo::codecs::AudioStreamInfo>;
1216
1217public:
1218    /// Creates an audio stream info with default values.
1219    TAudioStreamInfo()
1220        : Base(primo::ref<primo::codecs::AudioStreamInfo>(
1221              primo::avblocks::Library::createAudioStreamInfo())) {}
1222
1223    /**
1224     * Takes ownership of an existing @c AudioStreamInfo reference.
1225     */
1226    explicit TAudioStreamInfo(primo::ref<primo::codecs::AudioStreamInfo> info)
1227        : Base(std::move(info)) {}
1228
1229    /// Wraps a non-owned @c AudioStreamInfo pointer, retaining it.
1230    explicit TAudioStreamInfo(primo::codecs::AudioStreamInfo* info)
1231        : Base(info) {}
1232
1233    TAudioStreamInfo(const TAudioStreamInfo&) = delete;
1234    TAudioStreamInfo& operator=(const TAudioStreamInfo&) = delete;
1235    /// @{
1236    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
1237    TAudioStreamInfo(TAudioStreamInfo&&) = default;
1238    TAudioStreamInfo& operator=(TAudioStreamInfo&&) = default;
1239    /// @}
1240
1241    // -- Fluent setters --
1242
1243    /// @{
1244    /**
1245     * Sets the number of audio channels.
1246     * @param channels Number of channels.
1247     * @return This object, for chaining.
1248     */
1249    TAudioStreamInfo& channels(int32_t channels) & {
1250        info_->setChannels(channels);
1251        return *this;
1252    }
1253
1254    TAudioStreamInfo&& channels(int32_t channels) && {
1255        info_->setChannels(channels);
1256        return std::move(*this);
1257    }
1258    /// @}
1259
1260    /// @{
1261    /**
1262     * Sets the audio sample rate.
1263     * @param sampleRate Sample rate in Hz.
1264     * @return This object, for chaining.
1265     */
1266    TAudioStreamInfo& sampleRate(int32_t sampleRate) & {
1267        info_->setSampleRate(sampleRate);
1268        return *this;
1269    }
1270
1271    TAudioStreamInfo&& sampleRate(int32_t sampleRate) && {
1272        info_->setSampleRate(sampleRate);
1273        return std::move(*this);
1274    }
1275    /// @}
1276
1277    /// @{
1278    /**
1279     * Sets the number of bits per sample (sample resolution), usually 8, 16, 24 or 32.
1280     * @param bits Bits per sample.
1281     * @return This object, for chaining.
1282     */
1283    TAudioStreamInfo& bitsPerSample(int32_t bits) & {
1284        info_->setBitsPerSample(bits);
1285        return *this;
1286    }
1287
1288    TAudioStreamInfo&& bitsPerSample(int32_t bits) && {
1289        info_->setBitsPerSample(bits);
1290        return std::move(*this);
1291    }
1292    /// @}
1293
1294    /// @{
1295    /**
1296     * Sets the audio format flags.
1297     * @param flags A combination of @c primo::codecs::PcmFlags::Enum values.
1298     * @return This object, for chaining.
1299     */
1300    TAudioStreamInfo& pcmFlags(int32_t flags) & {
1301        info_->setPcmFlags(flags);
1302        return *this;
1303    }
1304
1305    TAudioStreamInfo&& pcmFlags(int32_t flags) && {
1306        info_->setPcmFlags(flags);
1307        return std::move(*this);
1308    }
1309    /// @}
1310
1311    /// @{
1312    /**
1313     * Sets the channel layout, the mapping between channels and speakers.
1314     * @param layout A combination of @c primo::codecs::AudioChannelFlags::Enum values.
1315     * @return This object, for chaining.
1316     */
1317    TAudioStreamInfo& channelLayout(int32_t layout) & {
1318        info_->setChannelLayout(layout);
1319        return *this;
1320    }
1321
1322    TAudioStreamInfo&& channelLayout(int32_t layout) && {
1323        info_->setChannelLayout(layout);
1324        return std::move(*this);
1325    }
1326    /// @}
1327
1328    /// @{
1329    /**
1330     * Sets the number of bytes occupied by an audio frame, the smallest discrete unit of an audio stream. It is normally valid (greater than 0) for LPCM streams and 0 for compressed streams.
1331     * @param bytes Bytes per frame.
1332     * @return This object, for chaining.
1333     */
1334    TAudioStreamInfo& bytesPerFrame(int32_t bytes) & {
1335        info_->setBytesPerFrame(bytes);
1336        return *this;
1337    }
1338
1339    TAudioStreamInfo&& bytesPerFrame(int32_t bytes) && {
1340        info_->setBytesPerFrame(bytes);
1341        return std::move(*this);
1342    }
1343    /// @}
1344
1345    // -- Getters --
1346
1347    /// Returns the number of audio channels.
1348    int32_t channels()      const { return info_->channels(); }
1349    /// Returns the audio sample rate in Hz.
1350    int32_t sampleRate()    const { return info_->sampleRate(); }
1351    /// Returns the number of bits per sample. Normally 0 for a compressed audio stream.
1352    int32_t bitsPerSample() const { return info_->bitsPerSample(); }
1353    /**
1354     * Returns the audio format flags, a combination of @c primo::codecs::PcmFlags::Enum values.
1355     */
1356    int32_t pcmFlags()      const { return info_->pcmFlags(); }
1357    /**
1358     * Returns the channel layout, a combination of @c primo::codecs::AudioChannelFlags::Enum values.
1359     */
1360    int32_t channelLayout() const { return info_->channelLayout(); }
1361    /// Returns the number of bytes occupied by an audio frame. Normally 0 for a compressed audio stream.
1362    int32_t bytesPerFrame() const { return info_->bytesPerFrame(); }
1363
1364    // -- Operations --
1365
1366    /// Creates a full independent clone. The returned object is always mutable.
1367    TAudioStreamInfo clone() const {
1368        return TAudioStreamInfo(
1369            primo::ref<primo::codecs::AudioStreamInfo>(info_->clone()));
1370    }
1371
1372    /// Copies all fields to @p dst. Returns @c false if @p dst is null.
1373    bool copyTo(TAudioStreamInfo& dst) const {
1374        return info_->copyTo(dst.get()) == TRUE;
1375    }
1376};
1377
1378/**
1379 * Fluent wrapper for @c primo::codecs::VideoStreamInfo, which describes an elementary video stream.
1380 *
1381 * The properties common to all streams are inherited from @c TStreamInfo. The setters can be chained;
1382 * on a temporary they return the object by rvalue reference so that the chain can be returned or moved:
1383 * @code
1384 * TVideoStreamInfo video = TVideoStreamInfo().frameWidth(352).frameHeight(288).frameRate(25.0);
1385 * @endcode
1386 * Owns the raw object through a @c primo::ref. Not copyable, movable.
1387 */
1388class TVideoStreamInfo
1389    : public TStreamInfo<TVideoStreamInfo, primo::codecs::VideoStreamInfo> {
1390    using Base = TStreamInfo<TVideoStreamInfo, primo::codecs::VideoStreamInfo>;
1391
1392public:
1393    /// Creates a video stream info with default values.
1394    TVideoStreamInfo()
1395        : Base(primo::ref<primo::codecs::VideoStreamInfo>(
1396              primo::avblocks::Library::createVideoStreamInfo())) {}
1397
1398    /**
1399     * Takes ownership of an existing @c VideoStreamInfo reference.
1400     */
1401    explicit TVideoStreamInfo(primo::ref<primo::codecs::VideoStreamInfo> info)
1402        : Base(std::move(info)) {}
1403
1404    /// Wraps a non-owned @c VideoStreamInfo pointer, retaining it. Used by @c TMediaPin::videoStreamInfo().
1405    explicit TVideoStreamInfo(primo::codecs::VideoStreamInfo* info)
1406        : Base(info) {}
1407
1408    TVideoStreamInfo(const TVideoStreamInfo&) = delete;
1409    TVideoStreamInfo& operator=(const TVideoStreamInfo&) = delete;
1410    /// @{
1411    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
1412    TVideoStreamInfo(TVideoStreamInfo&&) = default;
1413    TVideoStreamInfo& operator=(TVideoStreamInfo&&) = default;
1414    /// @}
1415
1416    // -- Fluent setters --
1417
1418    /// @{
1419    /**
1420     * Sets the video color format.
1421     * @param format A @c primo::codecs::ColorFormat::Enum constant.
1422     * @return This object, for chaining.
1423     */
1424    TVideoStreamInfo& colorFormat(primo::codecs::ColorFormat::Enum format) & {
1425        info_->setColorFormat(format);
1426        return *this;
1427    }
1428
1429    TVideoStreamInfo&& colorFormat(primo::codecs::ColorFormat::Enum format) && {
1430        info_->setColorFormat(format);
1431        return std::move(*this);
1432    }
1433    /// @}
1434
1435    /// @{
1436    /**
1437     * Sets the video frame width.
1438     * @param width Width in pixels.
1439     * @return This object, for chaining.
1440     */
1441    TVideoStreamInfo& frameWidth(int32_t width) & {
1442        info_->setFrameWidth(width);
1443        return *this;
1444    }
1445
1446    TVideoStreamInfo&& frameWidth(int32_t width) && {
1447        info_->setFrameWidth(width);
1448        return std::move(*this);
1449    }
1450    /// @}
1451
1452    /// @{
1453    /**
1454     * Sets the video frame height.
1455     * @param height Height in pixels.
1456     * @return This object, for chaining.
1457     */
1458    TVideoStreamInfo& frameHeight(int32_t height) & {
1459        info_->setFrameHeight(height);
1460        return *this;
1461    }
1462
1463    TVideoStreamInfo&& frameHeight(int32_t height) && {
1464        info_->setFrameHeight(height);
1465        return std::move(*this);
1466    }
1467    /// @}
1468
1469    /// @{
1470    /**
1471     * Sets the horizontal dimension of the display aspect ratio. The value has no meaning on its own; together with @c displayRatioHeight() it expresses the ratio.
1472     * @param width Horizontal ratio.
1473     * @return This object, for chaining.
1474     */
1475    TVideoStreamInfo& displayRatioWidth(int32_t width) & {
1476        info_->setDisplayRatioWidth(width);
1477        return *this;
1478    }
1479
1480    TVideoStreamInfo&& displayRatioWidth(int32_t width) && {
1481        info_->setDisplayRatioWidth(width);
1482        return std::move(*this);
1483    }
1484    /// @}
1485
1486    /// @{
1487    /**
1488     * Sets the vertical dimension of the display aspect ratio. The value has no meaning on its own; together with @c displayRatioWidth() it expresses the ratio.
1489     * @param height Vertical ratio.
1490     * @return This object, for chaining.
1491     */
1492    TVideoStreamInfo& displayRatioHeight(int32_t height) & {
1493        info_->setDisplayRatioHeight(height);
1494        return *this;
1495    }
1496
1497    TVideoStreamInfo&& displayRatioHeight(int32_t height) && {
1498        info_->setDisplayRatioHeight(height);
1499        return std::move(*this);
1500    }
1501    /// @}
1502
1503    /// @{
1504    /**
1505     * Sets the video frame rate.
1506     * @param fps Frames per second.
1507     * @return This object, for chaining.
1508     */
1509    TVideoStreamInfo& frameRate(double fps) & {
1510        info_->setFrameRate(fps);
1511        return *this;
1512    }
1513
1514    TVideoStreamInfo&& frameRate(double fps) && {
1515        info_->setFrameRate(fps);
1516        return std::move(*this);
1517    }
1518    /// @}
1519
1520    /// @{
1521    /**
1522     * Sets the scan type of the video frame.
1523     * @param scan A @c primo::codecs::ScanType::Enum constant.
1524     * @return This object, for chaining.
1525     */
1526    TVideoStreamInfo& scanType(primo::codecs::ScanType::Enum scan) & {
1527        info_->setScanType(scan);
1528        return *this;
1529    }
1530
1531    TVideoStreamInfo&& scanType(primo::codecs::ScanType::Enum scan) && {
1532        info_->setScanType(scan);
1533        return std::move(*this);
1534    }
1535    /// @}
1536
1537    /// @{
1538    /**
1539     * Sets the image stride (pitch): the distance in bytes from the start of one row of pixels in memory to the start of the next row.
1540     * @param stride Stride in bytes.
1541     * @return This object, for chaining.
1542     */
1543    TVideoStreamInfo& stride(int32_t stride) & {
1544        info_->setStride(stride);
1545        return *this;
1546    }
1547
1548    TVideoStreamInfo&& stride(int32_t stride) && {
1549        info_->setStride(stride);
1550        return std::move(*this);
1551    }
1552    /// @}
1553
1554    /// @{
1555    /**
1556     * Sets whether the frame is stored upside down in memory, as is typical for bitmaps.
1557     * @param bottomUp @c true if the frame is stored upside down, @c false if the top row is first in memory.
1558     * @return This object, for chaining.
1559     */
1560    TVideoStreamInfo& frameBottomUp(bool bottomUp) & {
1561        info_->setFrameBottomUp(bottomUp ? TRUE : FALSE);
1562        return *this;
1563    }
1564
1565    TVideoStreamInfo&& frameBottomUp(bool bottomUp) && {
1566        info_->setFrameBottomUp(bottomUp ? TRUE : FALSE);
1567        return std::move(*this);
1568    }
1569    /// @}
1570
1571    // -- Getters --
1572
1573    /// Returns the video color format.
1574    primo::codecs::ColorFormat::Enum colorFormat()      const { return info_->colorFormat(); }
1575    /// Returns the video frame width in pixels.
1576    int32_t                          frameWidth()       const { return info_->frameWidth(); }
1577    /// Returns the video frame height in pixels.
1578    int32_t                          frameHeight()      const { return info_->frameHeight(); }
1579    /// Returns the horizontal dimension of the display aspect ratio.
1580    int32_t                          displayRatioWidth() const { return info_->displayRatioWidth(); }
1581    /// Returns the vertical dimension of the display aspect ratio.
1582    int32_t                          displayRatioHeight() const { return info_->displayRatioHeight(); }
1583    /// Returns the video frame rate in frames per second.
1584    double                           frameRate()        const { return info_->frameRate(); }
1585    /// Returns the scan type of the video frame.
1586    primo::codecs::ScanType::Enum    scanType()         const { return info_->scanType(); }
1587    /// Returns the image stride (pitch) in bytes.
1588    int32_t                          stride()           const { return info_->stride(); }
1589    /**
1590     * Returns @c true if the frame is stored upside down in memory.
1591     */
1592    bool                             frameBottomUp()    const { return info_->frameBottomUp() == TRUE; }
1593
1594    // -- Operations --
1595
1596    /// Creates a full independent clone. The returned object is always mutable.
1597    TVideoStreamInfo clone() const {
1598        return TVideoStreamInfo(
1599            primo::ref<primo::codecs::VideoStreamInfo>(info_->clone()));
1600    }
1601
1602    /// Copies all fields to @p dst. Returns @c false if @p dst is null.
1603    bool copyTo(TVideoStreamInfo& dst) const {
1604        return info_->copyTo(dst.get()) == TRUE;
1605    }
1606};
1607
1608/**
1609 * Fluent wrapper for a generic data stream.
1610 *
1611 * Wraps @c primo::codecs::StreamInfo as returned by
1612 * @c Library::createDataStreamInfo(). The media type is always
1613 * @c MediaType::Data and cannot be changed. There are no subclass-specific
1614 * properties beyond those exposed by the @c TStreamInfo CRTP base.
1615 */
1616class TDataStreamInfo
1617    : public TStreamInfo<TDataStreamInfo, primo::codecs::StreamInfo> {
1618    using Base = TStreamInfo<TDataStreamInfo, primo::codecs::StreamInfo>;
1619
1620public:
1621    /// Creates a data stream info with default values.
1622    TDataStreamInfo()
1623        : Base(primo::ref<primo::codecs::StreamInfo>(
1624              primo::avblocks::Library::createDataStreamInfo())) {}
1625
1626    /**
1627     * Takes ownership of an existing @c StreamInfo reference.
1628     */
1629    explicit TDataStreamInfo(primo::ref<primo::codecs::StreamInfo> info)
1630        : Base(std::move(info)) {}
1631
1632    /// Wraps a non-owned @c StreamInfo pointer, retaining it. Used by @c TMediaPin::streamInfo().
1633    explicit TDataStreamInfo(primo::codecs::StreamInfo* info)
1634        : Base(info) {}
1635
1636    TDataStreamInfo(const TDataStreamInfo&) = delete;
1637    TDataStreamInfo& operator=(const TDataStreamInfo&) = delete;
1638    /// @{
1639    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
1640    TDataStreamInfo(TDataStreamInfo&&) = default;
1641    TDataStreamInfo& operator=(TDataStreamInfo&&) = default;
1642    /// @}
1643
1644    // -- Operations --
1645
1646    /// Creates a full independent clone. The returned object is always mutable.
1647    TDataStreamInfo clone() const {
1648        return TDataStreamInfo(
1649            primo::ref<primo::codecs::StreamInfo>(info_->clone()));
1650    }
1651
1652    /// Copies all fields to @p dst. Returns @c false if @p dst is null.
1653    bool copyTo(TDataStreamInfo& dst) const {
1654        return info_->copyTo(dst.get()) == TRUE;
1655    }
1656};
1657
1658/**
1659 * CRTP mixin that adds the typed, fluent @c addParam() to a class that owns a @c primo::avblocks::ParameterList
1660 * (@c TMediaPin and @c TMediaSocketT). @p Derived must provide a private @c paramList() and declare this mixin a friend.
1661 *
1662 * Adds a codec parameter (fluent). @p name is a parameter name constant, e.g. @c Param::Video::Crop::Left or
1663 * @c Param::Encoder::Audio::AAC::StereoMode. The overload is chosen by the type of @p value:
1664 * - integer or enum (e.g. @c InterpolationMethod::Super)      -> integer parameter
1665 * - @c float / @c double                                       -> floating point parameter
1666 * - @c std::string / @c std::wstring / string literal          -> string parameter
1667 * - @c TMediaBuffer                                            -> media buffer parameter
1668 * - @c TVideoStreamInfo                                        -> video stream info parameter
1669 *
1670 * Each overload has an lvalue version (returns @c Derived&) and an rvalue version (returns @c Derived&&) so it can be
1671 * used in the middle of a chain on a temporary.
1672 */
1673template<typename Derived>
1674class TParamsMixin {
1675    primo::avblocks::ParameterList* params_() { return static_cast<Derived&>(*this).paramList(); }
1676    Derived& self_() { return static_cast<Derived&>(*this); }
1677
1678public:
1679    /// @{
1680    /**
1681     * Adds an integer parameter. Selected for integral and enum values, e.g. @c InterpolationMethod::Super.
1682     * @param name  Parameter name constant, e.g. @c Param::Video::Crop::Left.
1683     * @param value Parameter value.
1684     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference.
1685     */
1686    template<typename T> requires (std::integral<T> || std::is_enum_v<T>)
1687    Derived& addParam(const char* name, T value) & {
1688        params_()->addInt(name, static_cast<int64_t>(value));
1689        return self_();
1690    }
1691
1692    template<typename T> requires (std::integral<T> || std::is_enum_v<T>)
1693    Derived&& addParam(const char* name, T value) && {
1694        params_()->addInt(name, static_cast<int64_t>(value));
1695        return std::move(self_());
1696    }
1697    /// @}
1698
1699    /// @{
1700    /**
1701     * Adds a floating point parameter. Selected for @c float and @c double values.
1702     * @param name  Parameter name constant, e.g. @c Param::Video::Crop::Left.
1703     * @param value Parameter value.
1704     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference.
1705     */
1706    template<std::floating_point T>
1707    Derived& addParam(const char* name, T value) & {
1708        params_()->addFloat(name, static_cast<double>(value));
1709        return self_();
1710    }
1711
1712    template<std::floating_point T>
1713    Derived&& addParam(const char* name, T value) && {
1714        params_()->addFloat(name, static_cast<double>(value));
1715        return std::move(self_());
1716    }
1717    /// @}
1718
1719    /// @{
1720    /**
1721     * Adds a string parameter. Accepts @c std::string, @c std::wstring and string literals.
1722     * @param name  Parameter name constant, e.g. @c Param::Video::Crop::Left.
1723     * @param value Parameter value.
1724     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference.
1725     */
1726    Derived& addParam(const char* name, const std::string& value) & {
1727        params_()->addString(name, primo::ustring(value));
1728        return self_();
1729    }
1730
1731    Derived&& addParam(const char* name, const std::string& value) && {
1732        params_()->addString(name, primo::ustring(value));
1733        return std::move(self_());
1734    }
1735
1736    Derived& addParam(const char* name, const std::wstring& value) & {
1737        params_()->addString(name, primo::ustring(value));
1738        return self_();
1739    }
1740
1741    Derived&& addParam(const char* name, const std::wstring& value) && {
1742        params_()->addString(name, primo::ustring(value));
1743        return std::move(self_());
1744    }
1745    /// @}
1746
1747    /// @{
1748    /**
1749     * Adds a media buffer parameter.
1750     * @param name  Parameter name constant, e.g. @c Param::Video::Crop::Left.
1751     * @param value The buffer to store in the parameter.
1752     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference.
1753     */
1754    Derived& addParam(const char* name, const TMediaBuffer& value) & {
1755        params_()->addMediaBuffer(name, value.get());
1756        return self_();
1757    }
1758
1759    Derived&& addParam(const char* name, const TMediaBuffer& value) && {
1760        params_()->addMediaBuffer(name, value.get());
1761        return std::move(self_());
1762    }
1763    /// @}
1764
1765    /// @{
1766    /**
1767     * Adds a video stream info parameter.
1768     * @param name  Parameter name constant, e.g. @c Param::Video::Crop::Left.
1769     * @param value The video stream info to store in the parameter.
1770     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference.
1771     */
1772    Derived& addParam(const char* name, const TVideoStreamInfo& value) & {
1773        params_()->addVideoStreamInfo(name, value.get());
1774        return self_();
1775    }
1776
1777    Derived&& addParam(const char* name, const TVideoStreamInfo& value) && {
1778        params_()->addVideoStreamInfo(name, value.get());
1779        return std::move(self_());
1780    }
1781    /// @}
1782};
1783
1784/**
1785 * Fluent wrapper for @c primo::avblocks::MediaPin, which represents an elementary media stream.
1786 *
1787 * A pin can exist for audio, video or any other kind of stream, but AVBlocks processes only audio and video
1788 * streams. Pins are the inputs and outputs of a @c TMediaSocketT. A transcoder matches input and output pins by
1789 * their connection ID (see @c connection()). Codec parameters are added with @c addParam().
1790 *
1791 * Owns the raw object through a @c primo::ref. Not copyable, movable.
1792 */
1793class TMediaPin : public TParamsMixin<TMediaPin> {
1794    friend class TParamsMixin<TMediaPin>;
1795
1796    primo::ref<primo::avblocks::MediaPin> pin_;
1797    
1798public:
1799    /// Creates an empty media pin.
1800    TMediaPin() 
1801        : pin_(primo::avblocks::Library::createMediaPin()) {}
1802    
1803    /**
1804     * Takes ownership of an existing @c MediaPin reference.
1805     */
1806    explicit TMediaPin(primo::ref<primo::avblocks::MediaPin> pin)
1807        : pin_(std::move(pin)) {}
1808
1809    /// Wraps a non-owned @c MediaPin pointer, retaining it. Used by @c TMediaSocketT::pin().
1810    explicit TMediaPin(primo::avblocks::MediaPin* pin)
1811        : pin_(pin) { if (pin) pin->retain(); }
1812    
1813    // Delete copy operations
1814    TMediaPin(const TMediaPin&) = delete;
1815    TMediaPin& operator=(const TMediaPin&) = delete;
1816    
1817    // Enable move operations
1818    /// @{
1819    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
1820    TMediaPin(TMediaPin&&) = default;
1821    TMediaPin& operator=(TMediaPin&&) = default;
1822    /// @}
1823    
1824    /// @{
1825    /**
1826     * Sets the stream info of the pin. The pin retains the stream info object, so the pin and @p info share it.
1827     * @param info Audio or video stream info.
1828     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference.
1829     */
1830    TMediaPin&& streamInfo(const TAudioStreamInfo& info) && {
1831        pin_->setStreamInfo(info.get());
1832        return std::move(*this);
1833    }
1834    
1835    TMediaPin& streamInfo(const TAudioStreamInfo& info) & {
1836        pin_->setStreamInfo(info.get());
1837        return *this;
1838    }
1839    
1840    TMediaPin&& streamInfo(const TVideoStreamInfo& info) && {
1841        pin_->setStreamInfo(info.get());
1842        return std::move(*this);
1843    }
1844    
1845    TMediaPin& streamInfo(const TVideoStreamInfo& info) & {
1846        pin_->setStreamInfo(info.get());
1847        return *this;
1848    }
1849    /// @}
1850    
1851    /// Returns the pin's stream info cast to @c TAudioStreamInfo, wrapping the existing pointer.
1852    TAudioStreamInfo audioStreamInfo() const {
1853        return TAudioStreamInfo(
1854            static_cast<primo::codecs::AudioStreamInfo*>(pin_->streamInfo()));
1855    }
1856
1857    /// Returns the pin's stream info cast to @c TVideoStreamInfo, wrapping the existing pointer.
1858    TVideoStreamInfo videoStreamInfo() const {
1859        return TVideoStreamInfo(
1860            static_cast<primo::codecs::VideoStreamInfo*>(pin_->streamInfo()));
1861    }
1862
1863    /// Returns the pin's stream info as a @c TDataStreamInfo (base @c StreamInfo view),
1864    /// wrapping the existing pointer. Use to access @c mediaType(), @c streamType(),
1865    /// @c ID(), @c programNumber() without knowing the concrete media type.
1866    TDataStreamInfo streamInfo() const {
1867        return TDataStreamInfo(pin_->streamInfo());
1868    }
1869
1870    /// @{
1871    /**
1872     * Sets the connection ID of the pin. A transcoder matches and connects input and output pins by their
1873     * connection ID. Do not confuse it with the stream ID defined for certain stream types.
1874     * @param connectionID @c PinConnection::Disabled to exclude the pin, @c PinConnection::Auto for automatic
1875     *                      matching, or any positive integer to match with an output pin that has the same ID.
1876     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference.
1877     */
1878    TMediaPin&& connection(int32_t connectionID) && {
1879        pin_->setConnection(connectionID);
1880        return std::move(*this);
1881    }
1882
1883    TMediaPin& connection(int32_t connectionID) & {
1884        pin_->setConnection(connectionID);
1885        return *this;
1886    }
1887    /// @}
1888
1889    /// Returns the pin's current connection ID.
1890    int32_t connection() const {
1891        return pin_->connection();
1892    }
1893
1894    /// Returns the raw underlying pointer. For interop with the core API only.
1895    primo::avblocks::MediaPin* get() const { return pin_.get(); }
1896
1897private:
1898    primo::avblocks::ParameterList* paramList() { return pin_->params(); }
1899};
1900
1901/**
1902 * Non-owning view of a @c MediaPinList. Returned by @c TMediaSocketT::pins().
1903 */
1904class TMediaPinList {
1905    primo::avblocks::MediaPinList* list_;
1906
1907public:
1908    /// Wraps a non-owned @c MediaPinList pointer.
1909    explicit TMediaPinList(primo::avblocks::MediaPinList* list) : list_(list) {}
1910
1911    /**
1912     * Proxy returned by the non-const lvalue @c operator[](int32_t).
1913     * Supports both read (implicit conversion to @c TMediaPin) and
1914     * write (@c operator= calls @c setAt on the parent list).
1915     */
1916    struct ItemRef {
1917        /// The list this proxy refers to.
1918        TMediaPinList& list_;
1919        /// Index of the item the proxy refers to.
1920        int32_t index_;
1921        /// Read — implicit conversion to a non-owning pin wrapper.
1922        operator TMediaPin() const { return TMediaPin(list_.list_->at(index_)); }
1923        /// Write — calls @c setAt; returns @c true on success.
1924        bool operator=(TMediaPin&& p) { return list_.list_->setAt(index_, p.get()) == TRUE; }
1925    };
1926
1927    /// Returns the number of pins in the collection.
1928    int32_t count() const { return list_ ? list_->count() : 0; }
1929
1930    /// Returns the pin at @p index, wrapping the existing (non-owned) pointer.
1931    TMediaPin at(int32_t index) const { return TMediaPin(list_->at(index)); }
1932
1933    /// Non-const lvalue subscript — returns a proxy supporting read and @c setAt write.
1934    ItemRef operator[](int32_t index) & { return {*this, index}; }
1935
1936    /// Const subscript — same as @c at(@p index).
1937    TMediaPin operator[](int32_t index) const & { return TMediaPin(list_->at(index)); }
1938
1939    /// Replaces the pin at @p index with @p pin. Returns @c true on success.
1940    bool setAt(int32_t index, TMediaPin&& pin) {
1941        return list_ && list_->setAt(index, pin.get()) == TRUE;
1942    }
1943
1944    /// Inserts @p pin before @p index, shifting later items. Returns @c true on success.
1945    bool insert(int32_t index, TMediaPin&& pin) {
1946        return list_ && list_->insert(index, pin.get()) == TRUE;
1947    }
1948
1949    /// Returns @c true when the list (and its parent @c MediaSocket) is immutable.
1950    bool immutable() const { return list_ && list_->immutable() == TRUE; }
1951
1952    /// Removes the pin at @p index. Returns @c true on success.
1953    bool remove(int32_t index) { return list_ && list_->remove(index) == TRUE; }
1954
1955    /// Removes all pins from the list. Returns @c true on success.
1956    bool clear() { return list_ && list_->clear() == TRUE; }
1957};
1958
1959// String traits for handling both char and wchar_t
1960/**
1961 * Maps the character type of the fluent API (@c char or @c wchar_t) to the matching string type and converts
1962 * to and from @c primo::ustring. Used by @c TMediaSocketT, @c TMediaInfoT and @c TTranscoderT.
1963 */
1964template<typename CharT>
1965struct string_traits;
1966
1967/**
1968 * Narrow strings: @c std::string holding UTF-8.
1969 */
1970template<>
1971struct string_traits<char> {
1972    /**
1973     * The string type: @c std::string.
1974     */
1975    using string_type = std::string;
1976    /**
1977     * Converts a string to a @c primo::ustring.
1978     */
1979    static primo::ustring to_ustring(const string_type& s) {
1980        return primo::ustring(s);
1981    }
1982    /**
1983     * Converts a @c primo::ustring to a string.
1984     */
1985    static string_type from_ustring(const primo::ustring& u) {
1986        return u.str();
1987    }
1988};
1989
1990/**
1991 * Wide strings: @c std::wstring.
1992 */
1993template<>
1994struct string_traits<wchar_t> {
1995    /**
1996     * The string type: @c std::wstring.
1997     */
1998    using string_type = std::wstring;
1999    /**
2000     * Converts a string to a @c primo::ustring.
2001     */
2002    static primo::ustring to_ustring(const string_type& s) {
2003        return primo::ustring(s);
2004    }
2005    /**
2006     * Converts a @c primo::ustring to a string.
2007     */
2008    static string_type from_ustring(const primo::ustring& u) {
2009        return u.wstr();
2010    }
2011};
2012
2013/**
2014 * Fluent wrapper for @c primo::avblocks::MediaSocket, an input or output point of a transcoder.
2015 *
2016 * A socket describes either a container (WAV, AVI, MP4, etc.) with one or more streams, or a single elementary
2017 * stream (MP3, M2V, etc.). Its pins (see @c pins()) describe the contained streams. Socket parameters, such as
2018 * muxer options, are added with @c addParam(). The setters can be chained and return the object by rvalue
2019 * reference on a temporary, so a whole socket can be built in one expression:
2020 * @code
2021 * TMediaSocket("out.mp4").addPin(TMediaPin().streamInfo(video));
2022 * @endcode
2023 * @tparam CharT @c char for UTF-8 @c std::string paths, @c wchar_t for @c std::wstring paths.
2024 * Use the aliases @c TMediaSocket and @c TMediaSocketW. Owns the raw object through a @c primo::ref.
2025 * Not copyable, movable.
2026 */
2027template<typename CharT = char>
2028class TMediaSocketT : public TParamsMixin<TMediaSocketT<CharT>> {
2029    friend class TParamsMixin<TMediaSocketT<CharT>>;
2030    primo::ref<primo::avblocks::MediaSocket> socket_;
2031    
2032public:
2033    /**
2034     * The string type for paths: @c std::string for @c char, @c std::wstring for @c wchar_t.
2035     */
2036    using string_type = typename string_traits<CharT>::string_type;
2037    
2038    /// Creates an empty media socket.
2039    TMediaSocketT() 
2040        : socket_(primo::avblocks::Library::createMediaSocket()) {}
2041    
2042    /**
2043     * Takes ownership of an existing @c MediaSocket reference.
2044     */
2045    explicit TMediaSocketT(primo::ref<primo::avblocks::MediaSocket> socket)
2046        : socket_(std::move(socket)) {}
2047
2048    /// Wraps a non-owned @c MediaSocket pointer, retaining it. Used by @c TMediaInfo::inputs() / @c outputs().
2049    explicit TMediaSocketT(primo::avblocks::MediaSocket* socket)
2050        : socket_(socket) { if (socket) socket->retain(); }
2051
2052    /// Creates a socket from a @c TMediaInfo, wrapping @c Library::createMediaSocket(MediaInfo*).
2053    /// The created socket contains the file path and pins from the media info, ready to be
2054    /// added as a transcoder input. Member template so the body is instantiation-checked,
2055    /// allowing @c TMediaInfo to be defined after @c TMediaSocketT without a circular dependency.
2056    template<typename TMediaInfoArg>
2057    explicit TMediaSocketT(const TMediaInfoArg& info)
2058        : socket_(primo::avblocks::Library::createMediaSocket(info.get())) {
2059    }
2060
2061    /// Creates a socket pre-configured with the named AVBlocks preset
2062    /// (e.g. @c Preset::Video::Generic::MP4::Base_H264_AAC).
2063    explicit TMediaSocketT(const char* preset)
2064        : socket_(primo::avblocks::Library::createMediaSocket(preset)) {}
2065
2066    // Delete copy operations
2067    TMediaSocketT(const TMediaSocketT&) = delete;
2068    TMediaSocketT& operator=(const TMediaSocketT&) = delete;
2069    
2070    // Enable move operations
2071    /// @{
2072    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
2073    TMediaSocketT(TMediaSocketT&&) = default;
2074    TMediaSocketT& operator=(TMediaSocketT&&) = default;
2075    /// @}
2076    
2077    /// @{
2078    /**
2079     * Sets the file associated with the socket.
2080     * @param path File name.
2081     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2082     */
2083    TMediaSocketT&& file(const string_type& path) && {
2084        socket_->setFile(string_traits<CharT>::to_ustring(path));
2085        return std::move(*this);
2086    }
2087    
2088    TMediaSocketT& file(const string_type& path) & {
2089        socket_->setFile(string_traits<CharT>::to_ustring(path));
2090        return *this;
2091    }
2092    /// @}
2093
2094    /// @{
2095    /**
2096     * Clears the file associated with the socket.
2097     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2098     */
2099    TMediaSocketT&& file(std::nullptr_t) && {
2100        socket_->setFile(nullptr);
2101        return std::move(*this);
2102    }
2103
2104    TMediaSocketT& file(std::nullptr_t) & {
2105        socket_->setFile(nullptr);
2106        return *this;
2107    }
2108    /// @}
2109
2110    /// Returns the file path as a @c string_type (UTF-8 @c std::string for @c char, @c std::wstring for @c wchar_t), or empty string if not set.
2111    string_type file() const {
2112        const char_t* f = socket_ ? socket_->file() : nullptr;
2113        return f ? string_traits<CharT>::from_ustring(primo::ustring(f)) : string_type{};
2114    }
2115
2116    /// @{
2117    /**
2118     * Sets explicitly the format of the associated file or stream, e.g. AVI, WAV or MP4. If the socket has a file and
2119     * no type, the type is deduced from the file name extension.
2120     * @param type Stream type.
2121     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2122     */
2123    TMediaSocketT&& streamType(primo::codecs::StreamType::Enum type) && {
2124        socket_->setStreamType(type);
2125        return std::move(*this);
2126    }
2127    
2128    TMediaSocketT& streamType(primo::codecs::StreamType::Enum type) & {
2129        socket_->setStreamType(type);
2130        return *this;
2131    }
2132    /// @}
2133
2134    /// @{
2135    /**
2136     * Sets the user stream, an alternative to a file, that is the source or sink of the socket. The socket retains
2137     * the stream. When both a file and a stream are set, the transcoder uses the stream.
2138     * @param s The stream. Pass @c nullptr to clear a previously set stream.
2139     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2140     */
2141    TMediaSocketT&& stream(primo::Stream* s) && {
2142        socket_->setStream(s);
2143        return std::move(*this);
2144    }
2145
2146    TMediaSocketT& stream(primo::Stream* s) & {
2147        socket_->setStream(s);
2148        return *this;
2149    }
2150    /// @}
2151
2152    /// @{
2153    /**
2154     * Sets explicitly the stream subtype of the associated file or stream.
2155     * @param subType Stream subtype.
2156     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2157     */
2158    TMediaSocketT&& streamSubType(primo::codecs::StreamSubType::Enum subType) && {
2159        socket_->setStreamSubType(subType);
2160        return std::move(*this);
2161    }
2162    
2163    TMediaSocketT& streamSubType(primo::codecs::StreamSubType::Enum subType) & {
2164        socket_->setStreamSubType(subType);
2165        return *this;
2166    }
2167    /// @}
2168    
2169    /// @{
2170    /**
2171     * Adds a pin (elementary stream) to the socket. The pin list retains the pin.
2172     * @param pin The pin to add.
2173     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2174     */
2175    TMediaSocketT&& addPin(TMediaPin&& pin) && {
2176        socket_->pins()->add(pin.get());
2177        return std::move(*this);
2178    }
2179    
2180    TMediaSocketT& addPin(TMediaPin&& pin) & {
2181        socket_->pins()->add(pin.get());
2182        return *this;
2183    }
2184    /// @}
2185
2186    /// @{
2187    /**
2188     * Sets the time position of the socket. On an input socket the transcoder seeks to this position before the
2189     * transcoding starts. It cannot be changed while the transcoder is open. Has no effect if the socket is immutable.
2190     * @param time Time position in seconds.
2191     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2192     */
2193    TMediaSocketT&& timePosition(double time) && {
2194        socket_->setTimePosition(time);
2195        return std::move(*this);
2196    }
2197
2198    TMediaSocketT& timePosition(double time) & {
2199        socket_->setTimePosition(time);
2200        return *this;
2201    }
2202    /// @}
2203
2204    /// @{
2205    /// Gives you a reference to the pin at @p index, so you can do something with it (read it or change it)
2206    /// without leaving a fluent chain.
2207    ///
2208    /// @p fn is called once, immediately, with a @c TMediaPin& that refers to the pin inside this socket (it is not a
2209    /// copy), so changes made through it apply to the socket. The socket itself is returned, so chaining continues.
2210    ///
2211    /// Use this instead of @c pins(index): that returns the pin and ends the chain, so the socket is no longer
2212    /// available to pass on, e.g. to @c TTranscoder::addOutput().
2213    /// @code
2214    /// TTranscoder()
2215    ///     .addInput(TMediaSocket(mediaInfo))
2216    ///     .addOutput(
2217    ///         TMediaSocket(mediaInfo)
2218    ///             .file("out.mp4")
2219    ///             .pinRef(0, [](TMediaPin& pin) {
2220    ///                 pin.videoStreamInfo().frameRate(30.0);
2221    ///             }))
2222    ///     .open().run().close();
2223    /// @endcode
2224    /// @param index Zero-based index of the pin.
2225    /// @param fn    Callable taking @c TMediaPin&.
2226    template<typename Fn>
2227    TMediaSocketT&& pinRef(int32_t index, Fn&& fn) && {
2228        TMediaPin pin = pins(index);
2229        std::forward<Fn>(fn)(pin);
2230        return std::move(*this);
2231    }
2232
2233    template<typename Fn>
2234    TMediaSocketT& pinRef(int32_t index, Fn&& fn) & {
2235        TMediaPin pin = pins(index);
2236        std::forward<Fn>(fn)(pin);
2237        return *this;
2238    }
2239    /// @}
2240
2241    /// @{
2242    /// Gives you a reference to the video stream info of the pin at @p index, so you can do something with it
2243    /// (read it or change it) without leaving a fluent chain. Shorter than @c pinRef() when only the stream info
2244    /// is needed.
2245    ///
2246    /// @p fn is called once, immediately, with a @c TVideoStreamInfo& that refers to the stream info inside the pin
2247    /// (it is not a copy), so changes made through it apply to the socket. The socket is returned, so chaining
2248    /// continues. The pin must be a video pin.
2249    /// @code
2250    /// TMediaSocket(mediaInfo).file("out.mp4")
2251    ///     .videoInfoRef(0, [](TVideoStreamInfo& video) { video.frameRate(30.0); })
2252    /// @endcode
2253    template<typename Fn>
2254    TMediaSocketT&& videoInfoRef(int32_t index, Fn&& fn) && {
2255        TVideoStreamInfo info = pins(index).videoStreamInfo();
2256        std::forward<Fn>(fn)(info);
2257        return std::move(*this);
2258    }
2259
2260    template<typename Fn>
2261    TMediaSocketT& videoInfoRef(int32_t index, Fn&& fn) & {
2262        TVideoStreamInfo info = pins(index).videoStreamInfo();
2263        std::forward<Fn>(fn)(info);
2264        return *this;
2265    }
2266    /// @}
2267
2268    /// @{
2269    /// Gives you a reference to the audio stream info of the pin at @p index, so you can do something with it
2270    /// (read it or change it) without leaving a fluent chain. Same as @c videoInfoRef() for an audio pin.
2271    /// @code
2272    /// TMediaSocket(mediaInfo).file("out.mp3")
2273    ///     .audioInfoRef(0, [](TAudioStreamInfo& audio) { audio.sampleRate(48000); })
2274    /// @endcode
2275    template<typename Fn>
2276    TMediaSocketT&& audioInfoRef(int32_t index, Fn&& fn) && {
2277        TAudioStreamInfo info = pins(index).audioStreamInfo();
2278        std::forward<Fn>(fn)(info);
2279        return std::move(*this);
2280    }
2281
2282    template<typename Fn>
2283    TMediaSocketT& audioInfoRef(int32_t index, Fn&& fn) & {
2284        TAudioStreamInfo info = pins(index).audioStreamInfo();
2285        std::forward<Fn>(fn)(info);
2286        return *this;
2287    }
2288    /// @}
2289
2290
2291    /// Returns the stream type of this socket (read from the underlying @c MediaSocket).
2292    primo::codecs::StreamType::Enum streamType() const {
2293        return socket_->streamType();
2294    }
2295
2296    /// Returns the stream sub-type of this socket (read from the underlying @c MediaSocket).
2297    primo::codecs::StreamSubType::Enum streamSubType() const {
2298        return socket_->streamSubType();
2299    }
2300
2301    /// Returns a non-owning view of the pin list.
2302    TMediaPinList pins() const { return TMediaPinList(socket_->pins()); }
2303
2304    /// Returns the pin at @p index, wrapping the existing (non-owned) pointer.
2305    TMediaPin pins(int32_t index) const {
2306        return TMediaPin(socket_->pins()->at(index));
2307    }
2308
2309    /// Returns the metadata associated with this socket as a @c TMetadata wrapper.
2310    /// The returned object is invalid (@c valid() == false) if the socket has no metadata.
2311    TMetadata metadata() const { return TMetadata(socket_->metadata()); }
2312
2313    /// Creates an independent deep copy of this socket (file, stream types and pins). Typical use is to start the
2314    /// output from the input: @c input.clone().file("out.mp4").
2315    TMediaSocketT clone() const {
2316        return TMediaSocketT(primo::ref<primo::avblocks::MediaSocket>(socket_->clone()));
2317    }
2318
2319    /// Returns the raw underlying pointer. For interop with the core API only.
2320    primo::avblocks::MediaSocket* get() const { return socket_.get(); }
2321
2322private:
2323    primo::avblocks::ParameterList* paramList() { return socket_->params(); }
2324};
2325
2326// Type aliases for convenience
2327using TMediaSocket = TMediaSocketT<char>;
2328using TMediaSocketW = TMediaSocketT<wchar_t>;
2329
2330/**
2331 * Non-owning view of a @c MediaSocketList. Returned by @c TMediaInfo::inputs() / @c outputs().
2332 */
2333class TMediaSocketList {
2334    primo::avblocks::MediaSocketList* list_;
2335
2336public:
2337    /// Wraps a non-owned @c MediaSocketList pointer.
2338    explicit TMediaSocketList(primo::avblocks::MediaSocketList* list) : list_(list) {}
2339
2340    /**
2341     * Proxy returned by the non-const lvalue @c operator[](int32_t).
2342     * Supports both read (implicit conversion to @c TMediaSocket) and
2343     * write (@c operator= calls @c setAt on the parent list).
2344     */
2345    struct ItemRef {
2346        /// The list this proxy refers to.
2347        TMediaSocketList& list_;
2348        /// Index of the item the proxy refers to.
2349        int32_t index_;
2350        /// Read — implicit conversion to a non-owning socket wrapper.
2351        operator TMediaSocket() const { return TMediaSocket(list_.list_->at(index_)); }
2352        /// Write — calls @c setAt; returns @c true on success.
2353        bool operator=(TMediaSocket&& s) { return list_.list_->setAt(index_, s.get()) == TRUE; }
2354    };
2355
2356    /// Returns the number of sockets in the collection.
2357    int32_t count() const { return list_ ? list_->count() : 0; }
2358
2359    /// Returns the socket at @p index, wrapping the existing (non-owned) pointer.
2360    TMediaSocket at(int32_t index) const { return TMediaSocket(list_->at(index)); }
2361
2362    /// Non-const lvalue subscript — returns a proxy supporting read and @c setAt write.
2363    ItemRef operator[](int32_t index) & { return {*this, index}; }
2364
2365    /// Const subscript — same as @c at(@p index).
2366    TMediaSocket operator[](int32_t index) const & { return TMediaSocket(list_->at(index)); }
2367
2368    /// Replaces the socket at @p index with @p socket. Returns @c true on success.
2369    bool setAt(int32_t index, TMediaSocket&& socket) {
2370        return list_ && list_->setAt(index, socket.get()) == TRUE;
2371    }
2372
2373    /// Inserts @p socket before @p index, shifting later items. Returns @c true on success.
2374    bool insert(int32_t index, TMediaSocket&& socket) {
2375        return list_ && list_->insert(index, socket.get()) == TRUE;
2376    }
2377
2378    /// Returns @c true when the list (and its parent @c Transcoder) is immutable.
2379    bool immutable() const { return list_ && list_->immutable() == TRUE; }
2380
2381    /// Removes the socket at @p index. Returns @c true on success.
2382    bool remove(int32_t index) { return list_ && list_->remove(index) == TRUE; }
2383
2384    /// Removes all sockets from the list. Returns @c true on success.
2385    bool clear() { return list_ && list_->clear() == TRUE; }
2386};
2387
2388/**
2389 * Fluent wrapper for @c primo::avblocks::MediaInfo, which analyzes a file or a stream and reports its type and
2390 * properties.
2391 *
2392 * Configure the input socket (see @c file() and @c inputs()), call @c open(), then read the detected streams from
2393 * @c outputs() and the metadata from the sockets. In push mode, feed data with @c push() until @c isReady().
2394 * @code
2395 * auto info = TMediaInfo().file("in.mp4").open();
2396 * int32_t pins = info.outputs(0).pins().count();
2397 * @endcode
2398 * @tparam CharT @c char for UTF-8 @c std::string paths, @c wchar_t for @c std::wstring paths.
2399 * Use the aliases @c TMediaInfo and @c TMediaInfoW. Owns the raw object through a @c primo::ref.
2400 * Not copyable, movable.
2401 */
2402template<typename CharT = char>
2403class TMediaInfoT {
2404    primo::ref<primo::avblocks::MediaInfo> info_;
2405
2406public:
2407    /// Creates a media info object with one default input socket.
2408    TMediaInfoT()
2409        : info_(primo::avblocks::Library::createMediaInfo()) {}
2410
2411    // Delete copy operations
2412    TMediaInfoT(const TMediaInfoT&) = delete;
2413    TMediaInfoT& operator=(const TMediaInfoT&) = delete;
2414
2415    // Enable move operations
2416    /// @{
2417    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
2418    TMediaInfoT(TMediaInfoT&&) = default;
2419    TMediaInfoT& operator=(TMediaInfoT&&) = default;
2420    /// @}
2421
2422    /**
2423     * The string type for paths: @c std::string for @c char, @c std::wstring for @c wchar_t.
2424     */
2425    using string_type = typename string_traits<CharT>::string_type;
2426
2427    /// @{
2428    /**
2429     * Sets the file of the first input socket, so a probe can be written as @c TMediaInfo().file("in.mp4").open().
2430     * @param path File name.
2431     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2432     */
2433    TMediaInfoT&& file(const string_type& path) && {
2434        inputs(0).file(path);
2435        return std::move(*this);
2436    }
2437
2438    TMediaInfoT& file(const string_type& path) & {
2439        inputs(0).file(path);
2440        return *this;
2441    }
2442    /// @}
2443
2444    /// @{
2445    /**
2446     * Analyzes the input. If it succeeds, the detected streams and metadata can be read from the object.
2447     * On a temporary, @c auto @c info @c = @c TMediaInfo().file(f).open(); moves the opened object into @c info.
2448     * @throws TAVBlocksException if the input cannot be analyzed.
2449     * @return This object, for chaining.
2450     */
2451    TMediaInfoT& open() & {
2452        if (!info_->open()) {
2453            throw TAVBlocksException("Failed to open MediaInfo", TErrorInfo(info_->error()));
2454        }
2455        return *this;
2456    }
2457
2458    TMediaInfoT&& open() && {
2459        open();
2460        return std::move(*this);
2461    }
2462    /// @}
2463
2464    /// Opens the MediaInfo. Returns @c true on success, @c false on failure.
2465    bool tryOpen() {
2466        return info_->open() == TRUE;
2467    }
2468
2469    /// Returns @c true when stream info and metadata have been fully parsed in push mode.
2470    bool isReady() { return info_->isReady() == TRUE; }
2471
2472    /// Pushes a chunk of input data for the socket at @p inputIndex.
2473    /// Returns @c true on success.
2474    bool push(int32_t inputIndex, TMediaSample& sample) {
2475        return info_->push(inputIndex, sample.get()) == TRUE;
2476    }
2477
2478    /// Signals end-of-stream on the input socket at @p inputIndex.
2479    /// Equivalent to pushing a null (empty) sample.
2480    bool pushEos(int32_t inputIndex) {
2481        return info_->push(inputIndex, nullptr) == TRUE;
2482    }
2483
2484    /// Returns the error information for the last operation.
2485    TErrorInfo error() const {
2486        return TErrorInfo(info_->error());
2487    }
2488
2489    /// Returns a non-owning view of the input socket list.
2490    TMediaSocketList inputs() const { return TMediaSocketList(info_->inputs()); }
2491
2492    /// Returns the input socket at @p index, wrapping the existing (non-owned) pointer.
2493    TMediaSocketT<CharT> inputs(int32_t index) const {
2494        return TMediaSocketT<CharT>(info_->inputs()->at(index));
2495    }
2496
2497    /// Returns a non-owning view of the output socket list.
2498    TMediaSocketList outputs() const { return TMediaSocketList(info_->outputs()); }
2499
2500    /// Returns the output socket at @p index, wrapping the existing (non-owned) pointer.
2501    TMediaSocketT<CharT> outputs(int32_t index) const {
2502        return TMediaSocketT<CharT>(info_->outputs()->at(index));
2503    }
2504
2505    /// Returns the raw underlying pointer. For interop with the core API only.
2506    primo::avblocks::MediaInfo* get() const { return info_.get(); }
2507};
2508
2509// Type aliases for convenience
2510using TMediaInfo  = TMediaInfoT<char>;
2511using TMediaInfoW = TMediaInfoT<wchar_t>;
2512
2513/**
2514 * Internal adapter that bridges @c std::function callbacks to the raw
2515 * @c primo::avblocks::TranscoderCallback interface.
2516 *
2517 * Owned by @c TTranscoderT via @c std::unique_ptr so its address stays
2518 * stable across moves.  Only allocated when at least one lambda is set.
2519 */
2520class TTranscoderCallback : public primo::avblocks::TranscoderCallback {
2521    std::function<void(double, double)>                              onProgress_;
2522    std::function<void(primo::avblocks::TranscoderStatus::Enum)>     onStatus_;
2523    std::function<bool(double)>                                      onContinue_;
2524    std::function<void(int32_t)>                                     onInputChange_;
2525public:
2526    /// Sets the function called periodically to report progress, with the current and total time in seconds.
2527    void setOnProgress(std::function<void(double, double)> f)   { onProgress_    = std::move(f); }
2528    /// Sets the function called to report the status of the current operation.
2529    void setOnStatus(std::function<void(primo::avblocks::TranscoderStatus::Enum)> f) { onStatus_ = std::move(f); }
2530    /**
2531     * Sets the function called to let the user code stop a running transcoder. It returns @c true to continue.
2532     */
2533    void setOnContinue(std::function<bool(double)> f)            { onContinue_    = std::move(f); }
2534    /// Sets the function called when an input format is detected or changes, with the index of the input socket.
2535    void setOnInputChange(std::function<void(int32_t)> f)        { onInputChange_ = std::move(f); }
2536
2537    /// Calls the progress function, if set.
2538    void onProgress(double cur, double total) override {
2539        if (onProgress_) onProgress_(cur, total);
2540    }
2541    /// Calls the status function, if set.
2542    void onStatus(primo::avblocks::TranscoderStatus::Enum s) override {
2543        if (onStatus_) onStatus_(s);
2544    }
2545    /**
2546     * Calls the continue function, if set. Returns @c TRUE when no function is set, so the transcoder keeps running.
2547     */
2548    bool_t onContinue(double cur) override {
2549        return onContinue_ ? (onContinue_(cur) ? TRUE : FALSE) : TRUE;
2550    }
2551    /// Calls the input change function, if set.
2552    void onInputChange(int32_t idx) override {
2553        if (onInputChange_) onInputChange_(idx);
2554    }
2555};
2556
2557/**
2558 * Fluent wrapper for @c primo::avblocks::Transcoder, which encodes, decodes and transforms audio and video.
2559 *
2560 * Add input and output sockets, then either run the whole job with @c transcode() (file to file), or drive it
2561 * step by step: @c open(), @c run() or @c push() / @c pull() / @c flush(), then @c close(). Failures are reported with
2562 * @c TAVBlocksException; the @c try* variants return @c false instead. Progress and status are received through
2563 * lambdas (see @c onProgress()).
2564 * @code
2565 * TTranscoder()
2566 *     .allowDemoMode(true)
2567 *     .addInput(TMediaSocket(mediaInfo))
2568 *     .addOutput(TMediaSocket("out.mp4"))
2569 *     .transcode();
2570 * @endcode
2571 * @tparam CharT @c char or @c wchar_t, matching the sockets used. Use the aliases @c TTranscoder and @c TTranscoderW.
2572 * Owns the raw object through a @c primo::ref. Not copyable, movable.
2573 */
2574template<typename CharT = char>
2575class TTranscoderT {
2576    // callback_ must be declared BEFORE transcoder_ so that it is destroyed
2577    // AFTER the raw Transcoder (which holds a raw pointer to the callback).
2578    std::unique_ptr<TTranscoderCallback>   callback_;
2579    primo::ref<primo::avblocks::Transcoder> transcoder_;
2580    
2581public:
2582    /// Creates a transcoder with no inputs and outputs.
2583    TTranscoderT() 
2584        : transcoder_(primo::avblocks::Library::createTranscoder()) {}
2585    
2586    // Delete copy operations
2587    TTranscoderT(const TTranscoderT&) = delete;
2588    TTranscoderT& operator=(const TTranscoderT&) = delete;
2589    
2590    // Enable move operations
2591    /// @{
2592    /// Move construction/assignment transfers ownership of the underlying object. The moved-from object no longer wraps anything.
2593    TTranscoderT(TTranscoderT&&) = default;
2594    TTranscoderT& operator=(TTranscoderT&&) = default;
2595    /// @}
2596    
2597        /// Returns the callback adapter, creating it and installing it in the transcoder on first use.
2598    TTranscoderCallback& ensureCallback() {
2599        if (!callback_) {
2600            callback_ = std::make_unique<TTranscoderCallback>();
2601            transcoder_->setCallback(callback_.get());
2602        }
2603        return *callback_;
2604    }
2605
2606    /// @{
2607    /**
2608     * Sets whether demo mode (unlicensed transcoding) is allowed. When allowed, a demo overlay is applied if a
2609     * required codec is not licensed; otherwise the transcoder fails with @c AVBlocksError::UnlicensedFeature.
2610     * Demo mode is on by default in the Demo release and off in the OEM release.
2611     * @param allow @c true to allow demo mode.
2612     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2613     */
2614    TTranscoderT& allowDemoMode(bool allow = true) & {
2615        transcoder_->setAllowDemoMode(allow ? TRUE : FALSE);
2616        return *this;
2617    }
2618
2619    TTranscoderT&& allowDemoMode(bool allow = true) && {
2620        transcoder_->setAllowDemoMode(allow ? TRUE : FALSE);
2621        return std::move(*this);
2622    }
2623    /// @}
2624
2625    /// @{
2626    /**
2627     * Sets a function called periodically during @c run() to report progress.
2628     * @param f Function taking the current and total time in seconds.
2629     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2630     */
2631    TTranscoderT& onProgress(std::function<void(double, double)> f) & {
2632        ensureCallback().setOnProgress(std::move(f));
2633        return *this;
2634    }
2635
2636    TTranscoderT&& onProgress(std::function<void(double, double)> f) && {
2637        ensureCallback().setOnProgress(std::move(f));
2638        return std::move(*this);
2639    }
2640    /// @}
2641
2642    /// @{
2643    /**
2644     * Sets a function called during @c run() to report the status of the current operation.
2645     * @param f Function taking the @c TranscoderStatus.
2646     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2647     */
2648    TTranscoderT& onStatus(std::function<void(primo::avblocks::TranscoderStatus::Enum)> f) & {
2649        ensureCallback().setOnStatus(std::move(f));
2650        return *this;
2651    }
2652
2653    TTranscoderT&& onStatus(std::function<void(primo::avblocks::TranscoderStatus::Enum)> f) && {
2654        ensureCallback().setOnStatus(std::move(f));
2655        return std::move(*this);
2656    }
2657    /// @}
2658
2659    /// @{
2660    /**
2661     * Sets a function that controls whether @c run() continues. It is called with the current time in seconds.
2662     * Return @c true to continue or @c false to stop gracefully, leaving valid output.
2663     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2664     */
2665    TTranscoderT& onContinue(std::function<bool(double)> f) & {
2666        ensureCallback().setOnContinue(std::move(f));
2667        return *this;
2668    }
2669
2670    TTranscoderT&& onContinue(std::function<bool(double)> f) && {
2671        ensureCallback().setOnContinue(std::move(f));
2672        return std::move(*this);
2673    }
2674    /// @}
2675
2676    /// @{
2677    /**
2678     * Sets a function called when an input format is detected or changes in push mode, with the index of the input
2679     * socket. It can inspect the detected inputs and reconfigure outputs.
2680     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2681     */
2682    TTranscoderT& onInputChange(std::function<void(int32_t)> f) & {
2683        ensureCallback().setOnInputChange(std::move(f));
2684        return *this;
2685    }
2686
2687    TTranscoderT&& onInputChange(std::function<void(int32_t)> f) && {
2688        ensureCallback().setOnInputChange(std::move(f));
2689        return std::move(*this);
2690    }
2691    /// @}
2692    
2693    /// @{
2694    /**
2695     * Adds an input socket. The transcoder retains the socket.
2696     * @param socket The input socket.
2697     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2698     */
2699    TTranscoderT& addInput(const TMediaSocketT<CharT>& socket) & {
2700        transcoder_->inputs()->add(socket.get());
2701        return *this;
2702    }
2703
2704    TTranscoderT&& addInput(const TMediaSocketT<CharT>& socket) && {
2705        transcoder_->inputs()->add(socket.get());
2706        return std::move(*this);
2707    }
2708    /// @}
2709
2710    /// @{
2711    /**
2712     * Removes the input socket at @p index.
2713     * @param index Zero-based index of the socket.
2714     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2715     */
2716    TTranscoderT& removeInput(int32_t index) & {
2717        transcoder_->inputs()->remove(index);
2718        return *this;
2719    }
2720
2721    TTranscoderT&& removeInput(int32_t index) && {
2722        transcoder_->inputs()->remove(index);
2723        return std::move(*this);
2724    }
2725    /// @}
2726
2727    /// @{
2728    /**
2729     * Adds an output socket. The transcoder retains the socket.
2730     * @param socket The output socket.
2731     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2732     */
2733    TTranscoderT& addOutput(const TMediaSocketT<CharT>& socket) & {
2734        transcoder_->outputs()->add(socket.get());
2735        return *this;
2736    }
2737
2738    TTranscoderT&& addOutput(const TMediaSocketT<CharT>& socket) && {
2739        transcoder_->outputs()->add(socket.get());
2740        return std::move(*this);
2741    }
2742    /// @}
2743
2744    /// @{
2745    /**
2746     * Removes the output socket at @p index.
2747     * @param index Zero-based index of the socket.
2748     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2749     */
2750    TTranscoderT& removeOutput(int32_t index) & {
2751        transcoder_->outputs()->remove(index);
2752        return *this;
2753    }
2754
2755    TTranscoderT&& removeOutput(int32_t index) && {
2756        transcoder_->outputs()->remove(index);
2757        return std::move(*this);
2758    }
2759    /// @}
2760
2761    /// Returns a non-owning view of the input socket list.
2762    TMediaSocketList inputs() const { return TMediaSocketList(transcoder_->inputs()); }
2763
2764    /// Returns the input socket at @p index, wrapping the existing (non-owned) pointer.
2765    TMediaSocketT<CharT> inputs(int32_t index) const {
2766        return TMediaSocketT<CharT>(transcoder_->inputs()->at(index));
2767    }
2768
2769    /// Returns a non-owning view of the output socket list.
2770    TMediaSocketList outputs() const { return TMediaSocketList(transcoder_->outputs()); }
2771
2772    /// Returns the output socket at @p index, wrapping the existing (non-owned) pointer.
2773    TMediaSocketT<CharT> outputs(int32_t index) const {
2774        return TMediaSocketT<CharT>(transcoder_->outputs()->at(index));
2775    }
2776
2777    /// @{
2778    /**
2779     * Initializes the transcoder from the configured inputs and outputs. When it succeeds the transcoder is ready
2780     * to process data with @c run(), or with @c push() and @c pull().
2781     * @throws TAVBlocksException if the transcoder cannot be opened.
2782     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2783     */
2784    TTranscoderT& open() & {
2785        if (!transcoder_->open()) {
2786            throw TAVBlocksException("Failed to open transcoder", TErrorInfo(transcoder_->error()));
2787        }
2788        return *this;
2789    }
2790
2791    TTranscoderT&& open() && {
2792        if (!transcoder_->open()) {
2793            throw TAVBlocksException("Failed to open transcoder", TErrorInfo(transcoder_->error()));
2794        }
2795        return std::move(*this);
2796    }
2797    /// @}
2798
2799    /// Opens the transcoder without throwing. Returns @c true on success.
2800    bool tryOpen() {
2801        return transcoder_->open() == TRUE;
2802    }
2803    
2804    /**
2805     * Pulls output data from the transcoder.
2806     * @param[out] outputIndex Receives the index of the output socket the data belongs to.
2807     * @param[out] sample      Receives the output data.
2808     * @return @c true on success.
2809     */
2810    bool pull(int32_t& outputIndex, TMediaSample& sample) {
2811        return transcoder_->pull(outputIndex, sample.get()) == TRUE;
2812    }
2813
2814    /**
2815     * Pushes input data to the transcoder. The sample is used only within this call and is not retained. The
2816     * transcoder may consume only part of it: on return the buffer's data offset and size show what is left, which
2817     * matters when more data is appended for the next push.
2818     * @param inputIndex Index of the input socket the data is for.
2819     * @param sample     The input data.
2820     * @return @c true on success.
2821     */
2822    bool push(int32_t inputIndex, TMediaSample& sample) {
2823        return transcoder_->push(inputIndex, sample.get()) == TRUE;
2824    }
2825
2826    /// Signals end-of-stream on the input socket at @p inputIndex.
2827    /// Equivalent to pushing a null (empty) sample.
2828    bool pushEos(int32_t inputIndex) {
2829        return transcoder_->push(inputIndex, nullptr) == TRUE;
2830    }
2831
2832    /// @{
2833    /**
2834     * Runs an automatic transcoding. It returns when the transcoding is finished. All inputs and outputs must be
2835     * associated with files or user streams.
2836     * @throws TAVBlocksException if the transcoding fails or run mode cannot be used with these inputs and outputs.
2837     * @return This object, for chaining. On a temporary the rvalue overload returns the object by rvalue reference, so the chain can be returned or moved.
2838     */
2839    TTranscoderT& run() & {
2840        if (!transcoder_->run()) {
2841            throw TAVBlocksException("Failed to run transcoder", TErrorInfo(transcoder_->error()));
2842        }
2843        return *this;
2844    }
2845
2846    TTranscoderT&& run() && {
2847        if (!transcoder_->run()) {
2848            throw TAVBlocksException("Failed to run transcoder", TErrorInfo(transcoder_->error()));
2849        }
2850        return std::move(*this);
2851    }
2852    /// @}
2853    
2854    /// Closes the transcoder. When closed it neither accepts nor delivers data. The inputs and outputs are not modified.
2855    void close() {
2856        transcoder_->close();
2857    }
2858
2859    /// Opens, runs and closes the transcoder (file to file mode): the whole @c open().run().close() sequence in
2860    /// one call. The transcoder is closed even if @c run() throws; @c TAVBlocksException is thrown if it cannot be
2861    /// opened or fails to run.
2862    /// @code
2863    /// TTranscoder().allowDemoMode(true).addInput(in).addOutput(out).transcode();
2864    /// @endcode
2865    void transcode() {
2866        open();
2867        try {
2868            run();
2869        } catch (...) {
2870            close();
2871            throw;
2872        }
2873        close();
2874    }
2875
2876    /**
2877     * Flushes the data buffered in the transcoder to the output and marks the end of the stream for all inputs. Push
2878     * mode only. Do not push data after flushing.
2879     * @return @c true on success.
2880     */
2881    bool flush() {
2882        return transcoder_->flush() == TRUE;
2883    }
2884
2885    /// Returns the error information for the last operation.
2886    TErrorInfo error() const {
2887        return TErrorInfo(transcoder_->error());
2888    }
2889    
2890    /// Returns the raw underlying pointer. For interop with the core API only.
2891    primo::avblocks::Transcoder* get() const { return transcoder_.get(); }
2892};
2893
2894// Type aliases for convenience
2895using TTranscoder = TTranscoderT<char>;
2896using TTranscoderW = TTranscoderT<wchar_t>;
2897
2898} // namespace primo::avblocks::modern