Open Menu
Securing Video Pipelines: Digitally Signed Content (DSC) Support in GStreamer

Securing Video Pipelines: Digitally Signed Content (DSC) Support in GStreamer

User Name

Written by

Diego Nieto

April 22, 2026

Introduction

With recent advances in AI technology, it is becoming increasingly hard to identify whether video content is authentic or fake. As a consequence, users are losing trust in the information that they consume, and media outlets are challenged to fight modified or completely faked versions of their content. While governments are exploring lawful content marking, malicious players may not label their content at all or remove such marking. This can undermine digital platform trust and safety and have an impact on societies worldwide.

To overcome this problem, the Joint Video Experts Team (JVET) has proposed a mechanism for trustworthy signing and authentication of coded video data at an elementary stream level in their JVET-AN1019-v1 contribution. This method is designed so that it is compatible with key functionalities of the underlying video codecs like random access or scalable coding. The proposed DSC signalling is being incorporated into the ITU H.274 (VSEI) standard, which is designed to be reusable across multiple video codecs. The GStreamer implementation work was tracked in GStreamer Issue #4841.

This work was sponsored by Fraunhofer HHI. Fraunhofer implemented DSC in JVET/VTM, and Fluendo brought that implementation into GStreamer for H.266/VVC bitstreams, enabling developers to build verifiable and trustworthy media applications. Our GStreamer integration is available in our DSC GitHub repository. In this blog post, we explain the work completed and provide a brief introduction to DSC and how it works with H.266/VVC.

How Digitally Signed Content Works

The Challenge of Video Authentication

Traditional content authentication methods often rely on container-level signatures or external metadata, which can be easily stripped or manipulated. DSC takes a different approach by embedding cryptographic signatures directly into the video bitstream at the Network Abstraction Layer (NAL) unit level. This ensures that authentication information travels with the video data itself, making it much harder to remove or tamper with.

DSC in H.266/VVC

The mechanism for trustworthy authentication and verification of video content is realized by three new Supplemental Enhancement Information (SEI) messages, as detailed in the Fraunhofer HHI paper. These SEI messages enable attaching cryptographic signatures to flexible chunks of data of a video stream at the NAL unit level.

The proposed DSC signalling is defined in Section 8.37 of the ITU H.274 (VSEI) draft, which is being designed for ITU H.266 with the goal of being reusable by other codecs like H.265/HEVC and H.264/AVC in the future. Our current GStreamer implementation focuses specifically on H.266/VVC bitstreams.

The Three SEI Messages

The DSC workflow relies on three newly introduced SEI messages:

Digitally Signed Content Initialization (DSCI) - SEI Message

The DSCI SEI message indicates the beginning of signed content and provides the initialization parameters for the entire signing process:

  • dsci_id: an identifying number of the verification system.
  • dsci_hash_method_type: indicates the secure hash algorithm used (SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, etc.).
  • dsci_key_retrieval_mode_idc: specifies how to retrieve the public key (trust record or certificate).
  • dsci_use_key_register_idx_flag: indicates whether a key register index is used.
  • dsci_key_register_idx: index specifying the certificate in a certificate register (when applicable).
  • dsci_content_uuid_present_flag: indicates whether a content UUID is present.
  • dsci_content_uuid: unique identifier for the video content (UUID).
  • dsci_num_verification_substreams_minus1: number of verification substreams minus 1.
  • dsci_ref_substream_flag: flags indicating dependencies between substreams.
  • dsci_vss_implicit_association_mode_flag: verification substream implicit association mode.
  • dsci_signed_content_start_flag: indicates the start of signed content.
  • dsci_sei_signing_flag: indicates whether SEI messages are signed.
  • dsci_key_source_uri: URI reference to the content creator’s public key certificate or trust record.

Digitally Signed Content Selection (DSCS) - SEI Message

The DSCS SEI message indicates which verification substream a particular picture belongs to:

  • dscs_id: the verification system ID (matches dsci_id).
  • dscs_verification_substream_id: the ID of the verification substream that this picture is part of.

Digitally Signed Content Verification (DSCV) - SEI Message

The DSCV SEI message is transmitted for each of the verification substreams and contains the actual cryptographic signature:

  • dscv_id: the verification system ID (matches dsci_id).
  • dscv_verification_substream_id: the ID of the verification substream being signed.
  • dscv_signature_length_in_octets_minus1: length of the signature data minus 1.
  • dscv_signature: the digital signature of the substream data.
  • dscv_signed_content_end_flag: indicates the end of signed content.

Protection Against Temporal Attacks

A critical feature of the DSC design is protection against attacks by removal, insertion, or shuffling data chunks in the protected data stream. To guarantee that decoders can verify the continuity between different temporal segments of the coded video data, the hash value of a preceding temporal segment is also incorporated in the digital signature of a current temporal segment. Consequently, a joint digital signature of both a current and its previous temporal segment is constructed.

The following diagram illustrates how verification substreams are organized into chunks with dependencies, and how the three DSC SEI messages are structured within the bitstream:

DSC Chunk Stream Dependencies and SEI Messages

Source: Adapted from the Fraunhofer HHI paper: Authentication and Verification of Coded Video Data in Real-time Communications and Media Applications.

Multi-Stream Authentication

The DSC method also supports joint authentication of video and other media data such as coded audio data. A dedicated unique identifier (UUID) can be added to each chunk of signed video data within the context of the developed SEI messages. Whenever a chunk of coded audio data is intended to be coupled to the respective chunk of coded video data, the same UUID should also be part of the respective chunk of coded audio data. To verify the combination, users simply need to check that the values of the UUID present in the respective elementary streams of interest coincide.

Implementing DSC Support in GStreamer

Building on Fraunhofer HHI’s DSC work in JVET/VTM, Fluendo implemented the corresponding support in the GStreamer ecosystem. This sponsored implementation work is tracked in GStreamer Issue #4841, and the complete code is available in our DSC GitHub repository. Currently, the implementation provides comprehensive infrastructure to handle DSC SEI messages for H.266/VVC bitstreams, with support for H.265/HEVC and H.264/AVC planned for future releases.

Architecture Overview

Our implementation consists of several components working together:

  1. H.274: Adds H.274 structuers and parses its messages, including DSC ones.
  2. H.266 parser extension: Implements the parsing and writing of H.274 SEI messages for H.266
  3. DSC Substream Manager: Manages verification substream state and temporal dependencies. Used by dscsigner and dscverifier.
  4. DSC Signer Element (dscsigner): Signs H.266/VVC streams with cryptographic signatures.
  5. DSC Verifier Element (dscverifier): Verifies signed H.266/VVC content using public keys.
  6. H.266 SEI Inserter Element (h266seiinserter): Injects DSC SEI messages into H.266/VVC bitstreams.

Parsing DSC SEI Messages

Since the ITU H.274 specification is designed to be codec-agnostic and reusable across H.266/VVC, H.265/HEVC, and H.264/AVC, we implemented a common H.274 parser infrastructure in GStreamer that can be shared across all H.26x codec parsers.

Introducing gsth274 and gsth274parser

To provide a solid foundation for DSC implementation, we added two new libraries to GStreamer:

  • gsth274 (in gst-plugins-base): Defines core data structures for H.274 SEI messages following the ITU-T H.274 (VSEI) standard. Provides codec-independent type definitions and memory management functions for DSC structures.

  • gsth274parser (in gst-plugins-bad): Implements parsing and writing logic for H.274 SEI messages. Handles bitstream operations using GStreamer’s NAL reader/writer utilities to process DSCI, DSCS, and DSCV messages.

This architectural approach provides several key advantages: codec reusability (the same parser code works for H.266, H.265, and H.264), standardized data structures across all codecs, future-proof design as the H.274 specification evolves, and simplified maintenance with centralized bug fixes and improvements.

The H.266 parser (h266parse) automatically detects DSC SEI messages and uses the common H.274 parser to extract the relevant information for downstream processing. This makes it straightforward to add DSC support to H.265 and H.264 parsers in the future—they can simply call the same gsth274parser functions when processing SEI NAL units.

Parsing DSC SEI Messages

The first step was to add support in GStreamer’s H.266 parser (h266parse) to recognize, extract, and parse the DSCI, DSCS, and DSCV SEI messages. We implemented a dedicated DSC NAL parser that follows the ITU H.274 specification proposal:

typedef struct _GstH274DigitallySignedContentInitialization {
  guint8 id;
  guint8 hash_method_type;
  guint32 key_retrieval_mode_idc;
  guint8 use_key_register_idx_flag;
  guint32 key_register_idx;
  guint8 content_uuid_present_flag;
  guint8 content_uuid[16];
  guint32 num_verification_substreams;
  guint8 *ref_substream_flag;
  gsize ref_substream_flag_len;
  guint8 vss_implicit_association_mode_flag;
  guint8 signed_content_start_flag;
  guint8 sei_signing_flag;
  guint8 *key_source_uri;
} GstH274DigitallySignedContentInitialization;

typedef struct _GstH274DigitallySignedContentSelection {
  guint8 id;
  guint8 verification_substream_id;
} GstH274DigitallySignedContentSelection;

typedef struct _GstH274DigitallySignedContentVerification {
  guint8 id;
  guint8 verification_substream_id;
  guint32 signature_length_in_octets_minus1;
  guint8 *signature;
  guint8 signed_content_end_flag;
} GstH274DigitallySignedContentVerification;

The H.266 parser automatically detects DSC SEI messages and extracts the relevant information for downstream processing.

Handling Metadata with GstMeta

Once parsed, the cryptographic signatures, substream dependencies, and UUIDs need to travel through the GStreamer pipeline alongside the video data. We created three custom GstMeta structures to attach DSC information to GstBuffer objects, one for each SEI message type:

/**
 * GstVideoDSCInitializationMeta:
 * Metadata for DSCI SEI message
 */
struct _GstVideoDSCInitializationMeta {
  GstMeta meta;
  GstH274DigitallySignedContentInitialization dsc_initialization;
};

/**
 * GstVideoDSCSelectionMeta:
 * Metadata for DSCS SEI message
 */
struct _GstVideoDSCSelectionMeta {
  GstMeta meta;
  GstH274DigitallySignedContentSelection dsc_selection;
};

/**
 * GstVideoDSCVerificationMeta:
 * Metadata for DSCV SEI message
 */
struct _GstVideoDSCVerificationMeta {
  GstMeta meta;
  GstH274DigitallySignedContentVerification dsc_verification;
};

These metadata structures preserve all DSC-related information as buffers flow through the GStreamer pipeline. Elements can retrieve the metadata using helper functions, allowing them to access signing and verification data without modifying the actual video payload.

The DSC Signer Element

We developed the dscsigner element to sign video streams with DSC. This element:

  1. Accepts raw or encoded video input
  2. Computes cryptographic hashes of NAL units using OpenSSL
  3. Signs data using RSA or ECDSA private keys
  4. Attaches DSC metadata to buffers
  5. Injects DSCI, DSCS, and DSCV SEI messages into the bitstream

Key properties of dscsigner:

  • private-key-path: Path to the RSA/ECDSA private key file
  • public-key-uri: URI of the corresponding public key certificate
  • substream-length: Number of frames per substream
  • hash-method: Hash algorithm (SHA-256, SHA-384, SHA-512)

The DSC Verifier Element

The dscverifier element is responsible for verifying signed content:

  1. Extracts DSC metadata from incoming buffers
  2. Loads public key certificates from the specified key store
  3. Verifies signatures using OpenSSL’s crypto library
  4. Checks temporal continuity using previous hash values
  5. Validates UUID consistency across streams

Key properties of dscverifier:

  • key-store-path: Directory containing trusted public key certificates
  • fail-on-verification-error: Whether to stop the pipeline on verification failure (default: true)

The verifier posts element messages on the bus when verification succeeds or fails, allowing applications to react accordingly. Applications can listen for dsc-verification-result messages containing verification status, data packet size, signature size, and error details if applicable.

SEI Insertion Element

To support encoders that don’t natively support DSC, we created the h266seiinserter element that can inject DSC SEI messages into existing H.266/VVC bitstreams:

gst-launch-1.0 \
  filesrc location=video.266 ! \
  h266parse ! \
  dscsigner ! \
  h266seiinserter ! \
  filesink location=video-signed.266

Cryptographic Library Integration

Our implementation leverages OpenSSL for all cryptographic operations:

  • Hashing: SHA-256, SHA-384, and SHA-512 support
  • Signing: RSA (2048-bit, 4096-bit) and ECDSA (P-256, P-384, P-521)
  • Verification: X.509 certificate parsing and public key extraction

We also ensured proper memory management and error handling for all cryptographic operations to prevent vulnerabilities.

DSC Pipeline Architecture

The following diagram illustrates how the DSC GStreamer elements work together in both signing and verification workflows:

  graph TD
  subgraph "Signing Pipeline"
    A[filesrc<br/>unsigned video]
    A --> B[h266parse<br/>parse NAL units]
    B --> C[dscsigner<br/>compute hashes<br/>sign with private key<br/>attach DSC metadata]
    C --> D[h266seiinserter<br/>inject DSCI/DSCS/DSCV<br/>SEI messages]
    D --> E[filesink<br/>signed video]
  end
  
  subgraph "Verification Pipeline"
    F[filesrc<br/>signed video]
    F --> G[h266parse<br/>parse NAL units<br/>extract DSC SEI]
    G --> H[dscverifier<br/>extract signatures<br/>load public keys<br/>verify hashes]
    H --> I[avdec_h266<br/>decode video]
    I --> J[autovideosink<br/>display]
  end
  
  subgraph "DSC Metadata Flow"
    K[GstVideoDSCInitializationMeta<br/>DSCI SEI data]
    L[GstVideoDSCSelectionMeta<br/>DSCS SEI data]
    M[GstVideoDSCVerificationMeta<br/>DSCV SEI data]
  end
  
  C -.attach metadata.-> K
  C -.attach metadata.-> L
  C -.attach metadata.-> M
  D -.read metadata.-> K
  D -.read metadata.-> L
  D -.read metadata.-> M
  G -.extract metadata.-> K
  G -.extract metadata.-> L
  G -.extract metadata.-> M
  H -.read metadata.-> K
  H -.read metadata.-> L
  H -.read metadata.-> M
  
  style C fill:#2d5016,stroke:#333,color:#fff
  style D fill:#1e4d7a,stroke:#333,color:#fff
  style H fill:#8b2252,stroke:#333,color:#fff
  style K fill:#8b6914,stroke:#333,color:#fff
  style L fill:#8b6914,stroke:#333,color:#fff
  style M fill:#8b6914,stroke:#333,color:#fff

Demo: Signing and Verifying Video Content

Let’s now see working examples demonstrating how to sign and verify video content using GStreamer’s DSC support.

Prerequisites: Using the Docker Environment

For the following demos, we’ll use the pre-configured Docker environment available in our DSC GitHub repository. This container includes all necessary tools (GStreamer with DSC support, VTM encoder/decoder) and pre-generated certificates for testing purposes, allowing you to run the examples immediately without manual certificate generation.

The certificates are located in /root/VVCSoftware_VTM/cfg/keystore/ within the container, with separate directories for CA certificates (ca/), public keys (public/), and private keys (private/). For production use, refer to the repository documentation for generating your own certificates.

Demo 1: Encode and Sign H.266/VVC Stream with VTM

This example shows encoding a raw video stream and signing it using the VVC VTM reference software with DSC support:

cd /root/VVCSoftware_VTM/bin
./EncoderAppStaticd \
    -i /app/AUD_MW_E.raw \
    -wdt 176 \
    -hgt 144 \
    -fr 30 \
    -f 5 \
    -c ../cfg/encoder_randomaccess_vtm.cfg \
    -c ../cfg/sei_vui/digitally_signed_content.cfg

The encoder generates str.bin containing the signed H.266/VVC bitstream with embedded DSCI, DSCS, and DSCV SEI messages.

Demo 2: Verify Signed Content with GStreamer

To verify the VTM-signed H.266/VVC video file using GStreamer’s DSC verifier:

GST_DEBUG="dscverifier:4" \
    gst-launch-1.0 filesrc location=/root/VVCSoftware_VTM/bin/str.bin ! \
    h266parse ! \
    dscverifier key-store-path=/root/VVCSoftware_VTM/cfg/keystore/public/ ! \
    fakesink

The debug output will show verification results for each verification substream:

VERIFIER: Substream 0 verified successfully
VERIFIER: Substream 1 verified successfully
VERIFIER: Substream 2 verified successfully

You can also decode and verify using the VTM decoder:

cd /root/VVCSoftware_VTM/bin
./DecoderAppStaticd \
    -b ./str.bin \
    -o ./decoded_str.yuv \
    --KeyStoreDir=../cfg/keystore/public \
    --TrustStoreDir=../cfg/keystore/ca \
    --TraceFile=/tmp/sei_dsc_trace_decode.txt \
    --TraceRule="D_HEADER:poc>=0"

Here’s what the complete verification pipeline looks like:

GStreamer DSC Verification Pipeline

Demo 3: Detecting and Preventing Tampered Content

DSC’s cryptographic design provides robust protection against various tampering attacks:

Understanding DSC’s Anti-Tampering Mechanisms

Temporal Protection: Each verification substream includes the hash of the previous substream in its signature. This creates a cryptographic chain that makes it impossible to remove, reorder, or insert frames without detection.

NAL-Level Signing: Signatures are computed over the actual video data at the NAL unit level, not just container metadata. Any modification to the video content breaks the signature.

Certificate-Based Trust: Public keys are delivered via X.509 certificates that can be validated against a trust store, preventing key substitution attacks.

Demonstrating Tampering Detection

Let’s corrupt some bytes in a signed stream to see DSC’s detection capabilities:

# Encode a stream without DSC first
cd /root/VVCSoftware_VTM/bin
./EncoderAppStaticd \
    -i /app/AUD_MW_E.raw \
    -wdt 176 \
    -hgt 144 \
    -fr 30 \
    -f 5 \
    -c ../cfg/encoder_randomaccess_vtm.cfg \
    -b str-no-dsc.bin

# Sign it with GStreamer
gst-launch-1.0 filesrc location=./str-no-dsc.bin ! \
    h266parse ! \
    "video/x-h266,stream-format=byte-stream" ! \
    dscsigner private-key-path=/root/VVCSoftware_VTM/cfg/keystore/private/jvet_example_provider.key \
            public-key-uri=file:///root/VVCSoftware_VTM/cfg/keystore/public/jvet_example_provider.crt \
            substream-length=5 ! \
    h266parse ! \
    "video/x-h266,stream-format=byte-stream,alignment=au" ! \
    h266seiinserter ! \
    h266parse ! \
    "video/x-h266,stream-format=byte-stream,alignment=nal" ! \
    filesink location=./str-gst-dsc.bin

# Verify the just signed content
GST_DEBUG="dscverifier:5" \
    gst-launch-1.0 filesrc location=./str-gst-dsc.bin ! \
    h266parse ! \
    dscverifier key-store-path=/root/VVCSoftware_VTM/cfg/keystore/public/ ! \
    fakesink

# Find a VCL NAL unit and flip some bits in its payload
python3 << 'EOF'
with open('./str-gst-dsc.bin', 'rb') as f:
    data = bytearray(f.read())

# Find first VCL NAL (after start code + 2-byte header)
# Flip bits in the middle of the NAL payload
for i in range(len(data) - 21):
    if data[i:i+4] == b'\x00\x00\x00\x01':
        nal_type = (data[i+5] >> 3) & 0x1F
        if 0 <= nal_type <= 9:  # VCL NAL
            # Flip bits in payload (20 bytes after start)
            data[i + 20] ^= 0xFF
            data[i + 21] ^= 0xAA
            print(f"Modified VCL NAL type {nal_type} at byte {i+20}")
            break

with open('./str-gst-dsc-tampered.bin', 'wb') as f:
    f.write(data)
EOF

# Try to verify the tampered content
GST_DEBUG="dscverifier:5" \
    gst-launch-1.0 filesrc location=./str-gst-dsc-tampered.bin ! \
    h266parse ! \
    dscverifier key-store-path=/root/VVCSoftware_VTM/cfg/keystore/public/ ! \
    fakesink

Protection Against Common Attacks

Frame Removal: Removing frames breaks the temporal chain since the next substream’s signature includes the hash of removed data.

Frame Reordering: Changing frame order invalidates signatures since hashes are computed in sequence.

Frame Injection: Injecting frames creates a mismatch between signed and actual content, causing verification failure.

Partial Modification: Even single-byte changes to video data alter the hash, immediately detected during verification.

Re-encoding Attacks: Re-encoding the video changes the bitstream completely, invalidating all signatures.

The combination of cryptographic hashing, digital signatures, and temporal chaining makes DSC highly resistant to tampering while maintaining compatibility with video codec features like random access and scalable coding.

Conclusion

By bringing Fraunhofer HHI’s JVET Digitally Signed Content implementation into GStreamer, we are helping equip developers with the tools needed to build verifiable, trustworthy media applications capable of combating deepfakes and content manipulation. This implementation provides:

  • Native DSC support in GStreamer’s H.266/VVC parser
  • Flexible signing and verification elements that work with H.266/VVC encoders
  • Industry-standard cryptography using OpenSSL (SHA-256/384/512, RSA, ECDSA)
  • Multi-stream authentication support via content UUIDs
  • Easy integration through GStreamer’s familiar plugin architecture
  • Open source implementation available at github.com/fluendo/dsc

As AI-generated content becomes more sophisticated, technologies like DSC will be essential for maintaining trust in digital media. While the JVET proposal is still being standardized, this GStreamer integration provides a working foundation that can evolve with the standard. We plan to extend support to H.265/HEVC and H.264/AVC in future releases.

All of our DSC implementation work is available in our GitHub repository and tracked in GStreamer Issue #4841. We look forward to seeing how the community leverages this technology to combat misinformation and protect content authenticity.

Get in Touch

At Fluendo, we specialize in developing secure video streaming solutions tailored to your specific needs. Whether you’re looking for content authentication, low-latency streaming, or custom codec integration, our expert team is ready to help.

Want to learn more or discuss your project? Contact us today, and let’s build the future of trustworthy video streaming together!