Coverage Report

Created: 2026-05-16 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/video/gstvideodscmeta.c
Line
Count
Source
1
/* GStreamer
2
 * Copyright (C) 2025 Fluendo S.A.
3
 *   Author: Diego Nieto <dnieto@fluendo.com>
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Library General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Library General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Library General Public
16
 * License along with this library; if not, write to the
17
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18
 * Boston, MA 02110-1301, USA.
19
 */
20
21
#ifdef HAVE_CONFIG_H
22
#include "config.h"
23
#endif
24
25
#include "gstvideodscmeta.h"
26
27
#ifndef GST_DISABLE_GST_DEBUG
28
#define GST_CAT_DEFAULT gst_video_dsc_meta_debug_category_get()
29
static GstDebugCategory *
30
gst_video_dsc_meta_debug_category_get (void)
31
0
{
32
0
  static gsize cat_gonce = 0;
33
34
0
  if (g_once_init_enter (&cat_gonce)) {
35
0
    GstDebugCategory *cat = NULL;
36
37
0
    GST_DEBUG_CATEGORY_INIT (cat,
38
0
        "videodscmeta", 0, "Video Digitally Signed Content Meta");
39
40
0
    g_once_init_leave (&cat_gonce, (gsize) cat);
41
0
  }
42
0
  return (GstDebugCategory *) cat_gonce;
43
0
}
44
#endif /* GST_DISABLE_GST_DEBUG */
45
46
/**
47
 * SECTION:gstvideodscmeta
48
 * @title: GstVideoDSCMeta
49
 * @short_description: GstMeta for Digitally Signed Content SEI messages
50
 *
51
 * This meta carries Digitally Signed Content (DSC) SEI message information
52
 * for video streams. DSC allows the verification of the integrity and authenticity
53
 * of the video content.
54
 * 
55
 * The mechanism is realized through three supplemental enhancement information (SEI)
56
 * messages that enable attaching cryptographic signatures to flexible chunks of data
57
 * within a video stream at the Network Abstraction Layer (NAL) unit level.
58
 * 
59
 * The current implementation follows the specification defined in
60
 * https://www.jvet-experts.org/doc_end_user/documents/40_Geneva/wg11/JVET-AN1019-v1.zip
61
 * 
62
 * The main concepts and mechanisms are also described in the paper:
63
 * https://www.hhi.fraunhofer.de/fileadmin/Events/2025/IBC_2025/IBC2025PaperAuthentication_HHI.pdf
64
 * 
65
 * Three types of metadata are provided:
66
 * - Initialization: Contains hash method, key source, and verification settings
67
 * - Selection: Indicates which verification substream to use
68
 * - Verification: Contains the actual signature data for verification
69
 *
70
 * Since: 1.30
71
 */
72
73
/* ==================== Initialization Meta ==================== */
74
75
typedef struct
76
{
77
  const GstH274DigitallySignedContentInitialization *dsc_initialization;
78
} GstVideoDSCInitializationMetaParams;
79
80
GType
81
gst_video_dsc_initialization_meta_api_get_type (void)
82
0
{
83
0
  static GType type = 0;
84
0
  static const gchar *tags[] = { GST_META_TAG_VIDEO_STR, NULL };
85
86
0
  if (g_once_init_enter (&type)) {
87
0
    GType _type =
88
0
        gst_meta_api_type_register ("GstVideoDSCInitializationMetaAPI", tags);
89
0
    g_once_init_leave (&type, _type);
90
0
  }
91
0
  return type;
92
0
}
93
94
static gboolean
95
gst_video_dsc_initialization_meta_transform (GstBuffer *
96
    dest, GstMeta * meta, GstBuffer * buffer, GQuark type, gpointer data)
97
0
{
98
0
  GstVideoDSCInitializationMeta *dmeta, *smeta;
99
100
0
  smeta = (GstVideoDSCInitializationMeta *) meta;
101
102
0
  if (GST_META_TRANSFORM_IS_COPY (type)) {
103
0
    dmeta =
104
0
        gst_buffer_add_video_dsc_initialization_meta (dest,
105
0
        &smeta->dsc_initialization);
106
107
0
    if (!dmeta)
108
0
      return FALSE;
109
0
  }
110
0
  return TRUE;
111
0
}
112
113
static gboolean
114
gst_video_dsc_initialization_meta_init (GstMeta * meta,
115
    gpointer params, GstBuffer * buffer)
116
0
{
117
0
  GstVideoDSCInitializationMeta *dsc_meta =
118
0
      (GstVideoDSCInitializationMeta *) meta;
119
0
  GstVideoDSCInitializationMetaParams *p =
120
0
      (GstVideoDSCInitializationMetaParams *) params;
121
122
0
  gst_h274_dsc_initialization_copy (&dsc_meta->dsc_initialization,
123
0
      p->dsc_initialization);
124
125
0
  return TRUE;
126
0
}
127
128
static void
129
gst_video_dsc_initialization_meta_free (GstMeta * meta, GstBuffer * buffer)
130
0
{
131
0
  GstVideoDSCInitializationMeta *dsc_meta =
132
0
      (GstVideoDSCInitializationMeta *) meta;
133
0
  gst_h274_dsc_initialization_free (&dsc_meta->dsc_initialization);
134
0
}
135
136
const GstMetaInfo *
137
gst_video_dsc_initialization_meta_get_info (void)
138
0
{
139
0
  static const GstMetaInfo *info = NULL;
140
141
0
  if (g_once_init_enter ((GstMetaInfo **) & info)) {
142
0
    const GstMetaInfo *meta =
143
0
        gst_meta_register (GST_VIDEO_DSC_INITIALIZATION_META_API_TYPE,
144
0
        "GstVideoDSCInitializationMeta",
145
0
        sizeof (GstVideoDSCInitializationMeta),
146
0
        gst_video_dsc_initialization_meta_init,
147
0
        gst_video_dsc_initialization_meta_free,
148
0
        gst_video_dsc_initialization_meta_transform);
149
0
    g_once_init_leave ((GstMetaInfo **) & info, (GstMetaInfo *) meta);
150
0
  }
151
152
0
  return info;
153
0
}
154
155
GstVideoDSCInitializationMeta *
156
gst_buffer_add_video_dsc_initialization_meta (GstBuffer *
157
    buffer,
158
    const GstH274DigitallySignedContentInitialization * dsc_initialization)
159
0
{
160
0
  GstVideoDSCInitializationMeta *meta;
161
0
  GstVideoDSCInitializationMetaParams params = {
162
0
    .dsc_initialization = dsc_initialization,
163
0
  };
164
165
0
  g_return_val_if_fail (buffer != NULL, NULL);
166
0
  g_return_val_if_fail (dsc_initialization != NULL, NULL);
167
168
0
  GST_DEBUG ("Adding DSC Initialization Meta: id=%u, hash_method_type=%u, "
169
0
      "key_retrieval_mode_idc=%u, use_key_register_idx_flag=%d, "
170
0
      "key_register_idx=%u, content_uuid_present_flag=%d, "
171
0
      "num_verification_substreams=%u, ref_substream_flag_len=%" G_GSIZE_FORMAT
172
0
      ", vss_implicit_association_mode_flag=%d, "
173
0
      "signed_content_start_flag=%d, sei_signing_flag=%d, key_source_uri=%s",
174
0
      dsc_initialization->id, dsc_initialization->hash_method_type,
175
0
      dsc_initialization->key_retrieval_mode_idc,
176
0
      dsc_initialization->use_key_register_idx_flag,
177
0
      dsc_initialization->key_register_idx,
178
0
      dsc_initialization->content_uuid_present_flag,
179
0
      dsc_initialization->num_verification_substreams,
180
0
      dsc_initialization->ref_substream_flag_len,
181
0
      dsc_initialization->vss_implicit_association_mode_flag,
182
0
      dsc_initialization->signed_content_start_flag,
183
0
      dsc_initialization->sei_signing_flag,
184
0
      GST_STR_NULL (dsc_initialization->key_source_uri));
185
186
0
  if (dsc_initialization->content_uuid_present_flag) {
187
0
    GST_MEMDUMP ("Content UUID", dsc_initialization->content_uuid, 16);
188
0
  }
189
190
0
  params.dsc_initialization = dsc_initialization;
191
192
0
  meta = (GstVideoDSCInitializationMeta *)
193
0
      gst_buffer_add_meta (buffer,
194
0
      GST_VIDEO_DSC_INITIALIZATION_META_INFO, &params);
195
196
0
  return meta;
197
0
}
198
199
/* ==================== Selection Meta ==================== */
200
201
typedef struct
202
{
203
  const GstH274DigitallySignedContentSelection *dsc_selection;
204
} GstVideoDSCSelectionMetaParams;
205
206
GType
207
gst_video_dsc_selection_meta_api_get_type (void)
208
0
{
209
0
  static GType type = 0;
210
0
  static const gchar *tags[] = { GST_META_TAG_VIDEO_STR, NULL };
211
212
0
  if (g_once_init_enter (&type)) {
213
0
    GType _type =
214
0
        gst_meta_api_type_register ("GstVideoDSCSelectionMetaAPI", tags);
215
0
    g_once_init_leave (&type, _type);
216
0
  }
217
0
  return type;
218
0
}
219
220
static gboolean
221
gst_video_dsc_selection_meta_transform (GstBuffer * dest,
222
    GstMeta * meta, GstBuffer * buffer, GQuark type, gpointer data)
223
0
{
224
0
  GstVideoDSCSelectionMeta *dmeta, *smeta;
225
226
0
  smeta = (GstVideoDSCSelectionMeta *) meta;
227
228
0
  if (GST_META_TRANSFORM_IS_COPY (type)) {
229
0
    dmeta = gst_buffer_add_video_dsc_selection_meta (dest,
230
0
        &smeta->dsc_selection);
231
232
0
    if (!dmeta)
233
0
      return FALSE;
234
0
  }
235
0
  return TRUE;
236
0
}
237
238
static gboolean
239
gst_video_dsc_selection_meta_init (GstMeta * meta,
240
    gpointer params, GstBuffer * buffer)
241
0
{
242
0
  GstVideoDSCSelectionMeta *dsc_meta = (GstVideoDSCSelectionMeta *) meta;
243
0
  GstVideoDSCSelectionMetaParams *p = (GstVideoDSCSelectionMetaParams *) params;
244
245
0
  gst_h274_dsc_selection_copy (&dsc_meta->dsc_selection, p->dsc_selection);
246
247
0
  return TRUE;
248
0
}
249
250
static void
251
gst_video_dsc_selection_meta_free (GstMeta * meta, GstBuffer * buffer)
252
0
{
253
0
  GstVideoDSCSelectionMeta *dsc_meta = (GstVideoDSCSelectionMeta *) meta;
254
0
  gst_h274_dsc_selection_free (&dsc_meta->dsc_selection);
255
0
}
256
257
const GstMetaInfo *
258
gst_video_dsc_selection_meta_get_info (void)
259
0
{
260
0
  static const GstMetaInfo *info = NULL;
261
262
0
  if (g_once_init_enter ((GstMetaInfo **) & info)) {
263
0
    const GstMetaInfo *meta =
264
0
        gst_meta_register (GST_VIDEO_DSC_SELECTION_META_API_TYPE,
265
0
        "GstVideoDSCSelectionMeta",
266
0
        sizeof (GstVideoDSCSelectionMeta),
267
0
        gst_video_dsc_selection_meta_init,
268
0
        gst_video_dsc_selection_meta_free,
269
0
        gst_video_dsc_selection_meta_transform);
270
0
    g_once_init_leave ((GstMetaInfo **) & info, (GstMetaInfo *) meta);
271
0
  }
272
273
0
  return info;
274
0
}
275
276
GstVideoDSCSelectionMeta *
277
gst_buffer_add_video_dsc_selection_meta (GstBuffer * buffer,
278
    const GstH274DigitallySignedContentSelection * dsc_selection)
279
0
{
280
0
  GstVideoDSCSelectionMeta *meta;
281
0
  GstVideoDSCSelectionMetaParams params = {
282
0
    .dsc_selection = dsc_selection,
283
0
  };
284
285
0
  g_return_val_if_fail (buffer != NULL, NULL);
286
0
  g_return_val_if_fail (dsc_selection != NULL, NULL);
287
288
0
  GST_DEBUG ("Adding DSC Selection Meta: id=%u, verification_substream_id=%u",
289
0
      dsc_selection->id, dsc_selection->verification_substream_id);
290
291
0
  params.dsc_selection = dsc_selection;
292
293
0
  meta = (GstVideoDSCSelectionMeta *)
294
0
      gst_buffer_add_meta (buffer, GST_VIDEO_DSC_SELECTION_META_INFO, &params);
295
296
0
  return meta;
297
0
}
298
299
/* ==================== Verification Meta ==================== */
300
301
typedef struct
302
{
303
  const GstH274DigitallySignedContentVerification *dsc_verification;
304
} GstVideoDSCVerificationMetaParams;
305
306
GType
307
gst_video_dsc_verification_meta_api_get_type (void)
308
0
{
309
0
  static GType type = 0;
310
0
  static const gchar *tags[] = { GST_META_TAG_VIDEO_STR, NULL };
311
312
0
  if (g_once_init_enter (&type)) {
313
0
    GType _type =
314
0
        gst_meta_api_type_register ("GstVideoDSCVerificationMetaAPI", tags);
315
0
    g_once_init_leave (&type, _type);
316
0
  }
317
0
  return type;
318
0
}
319
320
static gboolean
321
gst_video_dsc_verification_meta_transform (GstBuffer * dest,
322
    GstMeta * meta, GstBuffer * buffer, GQuark type, gpointer data)
323
0
{
324
0
  GstVideoDSCVerificationMeta *dmeta, *smeta;
325
326
0
  smeta = (GstVideoDSCVerificationMeta *) meta;
327
328
0
  if (GST_META_TRANSFORM_IS_COPY (type)) {
329
0
    dmeta = gst_buffer_add_video_dsc_verification_meta (dest,
330
0
        &smeta->dsc_verification);
331
332
0
    if (!dmeta)
333
0
      return FALSE;
334
0
  }
335
0
  return TRUE;
336
0
}
337
338
static gboolean
339
gst_video_dsc_verification_meta_init (GstMeta * meta,
340
    gpointer params, GstBuffer * buffer)
341
0
{
342
0
  GstVideoDSCVerificationMeta *dsc_meta = (GstVideoDSCVerificationMeta *) meta;
343
0
  GstVideoDSCVerificationMetaParams *p =
344
0
      (GstVideoDSCVerificationMetaParams *) params;
345
346
0
  gst_h274_dsc_verification_copy (&dsc_meta->dsc_verification,
347
0
      p->dsc_verification);
348
349
0
  return TRUE;
350
0
}
351
352
static void
353
gst_video_dsc_verification_meta_free (GstMeta * meta, GstBuffer * buffer)
354
0
{
355
0
  GstVideoDSCVerificationMeta *dsc_meta = (GstVideoDSCVerificationMeta *) meta;
356
0
  gst_h274_dsc_verification_free (&dsc_meta->dsc_verification);
357
0
}
358
359
const GstMetaInfo *
360
gst_video_dsc_verification_meta_get_info (void)
361
0
{
362
0
  static const GstMetaInfo *info = NULL;
363
364
0
  if (g_once_init_enter ((GstMetaInfo **) & info)) {
365
0
    const GstMetaInfo *meta =
366
0
        gst_meta_register (GST_VIDEO_DSC_VERIFICATION_META_API_TYPE,
367
0
        "GstVideoDSCVerificationMeta",
368
0
        sizeof (GstVideoDSCVerificationMeta),
369
0
        gst_video_dsc_verification_meta_init,
370
0
        gst_video_dsc_verification_meta_free,
371
0
        gst_video_dsc_verification_meta_transform);
372
0
    g_once_init_leave ((GstMetaInfo **) & info, (GstMetaInfo *) meta);
373
0
  }
374
375
0
  return info;
376
0
}
377
378
GstVideoDSCVerificationMeta *
379
gst_buffer_add_video_dsc_verification_meta (GstBuffer *
380
    buffer, const GstH274DigitallySignedContentVerification * dsc_verification)
381
0
{
382
0
  GstVideoDSCVerificationMeta *meta;
383
0
  GstVideoDSCVerificationMetaParams params = {
384
0
    .dsc_verification = dsc_verification,
385
0
  };
386
387
0
  g_return_val_if_fail (buffer != NULL, NULL);
388
0
  g_return_val_if_fail (dsc_verification != NULL, NULL);
389
390
0
  GST_DEBUG
391
0
      ("Adding DSC Verification Meta: id=%u, verification_substream_id=%u, "
392
0
      "signature_length_in_octets_minus1=%u",
393
0
      dsc_verification->id, dsc_verification->verification_substream_id,
394
0
      dsc_verification->signature_length_in_octets_minus1);
395
396
0
  if (dsc_verification->signature
397
0
      && dsc_verification->signature_length_in_octets_minus1 + 1 > 0) {
398
0
    for (guint i = 0;
399
0
        i < dsc_verification->signature_length_in_octets_minus1 + 1; i++) {
400
0
      guint8 byte = dsc_verification->signature[i];
401
0
      GST_DEBUG ("signature[%u] = %u (0x%02x)", i, byte, byte);
402
0
    }
403
0
  }
404
405
0
  params.dsc_verification = dsc_verification;
406
407
0
  meta = (GstVideoDSCVerificationMeta *)
408
0
      gst_buffer_add_meta (buffer,
409
0
      GST_VIDEO_DSC_VERIFICATION_META_INFO, &params);
410
411
0
  return meta;
412
0
}