Coverage Report

Created: 2026-03-31 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libde265/libde265/sei.cc
Line
Count
Source
1
/*
2
 * H.265 video codec.
3
 * Copyright (c) 2013-2014 struktur AG, Dirk Farin <farin@struktur.de>
4
 *
5
 * This file is part of libde265.
6
 *
7
 * libde265 is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libde265 is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libde265.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "sei.h"
22
#include "util.h"
23
#include "md5.h"
24
25
#include "libde265/sps.h"
26
#include "libde265/image.h"
27
#include "libde265/decctx.h"
28
29
#include <assert.h>
30
31
32
static de265_error read_sei_decoded_picture_hash(bitreader* reader, sei_message* sei,
33
                                                 const seq_parameter_set* sps)
34
0
{
35
0
  sei_decoded_picture_hash* seihash = &sei->data.decoded_picture_hash;
36
37
0
  seihash->hash_type = (enum sei_decoded_picture_hash_type)reader->get_bits(8);
38
39
0
  if (sps==nullptr) {
40
0
    return DE265_WARNING_SPS_MISSING_CANNOT_DECODE_SEI;
41
0
  }
42
43
0
  int nHashes = sps->chroma_format_idc==0 ? 1 : 3;
44
0
  for (int i=0;i<nHashes;i++) {
45
0
    switch (seihash->hash_type) {
46
0
    case sei_decoded_picture_hash_type_MD5:
47
0
      for (int b=0;b<16;b++) { seihash->md5[i][b] = reader->get_bits(8); }
48
0
      break;
49
50
0
    case sei_decoded_picture_hash_type_CRC:
51
0
      seihash->crc[i] = reader->get_bits(16);
52
0
      break;
53
54
0
    case sei_decoded_picture_hash_type_checksum:
55
0
      seihash->checksum[i]  = reader->get_bits(32);
56
0
      break;
57
0
    }
58
0
  }
59
60
0
  return DE265_OK;
61
0
}
62
63
64
static void dump_sei_decoded_picture_hash(const sei_message* sei,
65
                                          const seq_parameter_set* sps)
66
0
{
67
0
  const sei_decoded_picture_hash* seihash = &sei->data.decoded_picture_hash;
68
69
0
  loginfo(LogSEI,"  hash_type: ");
70
0
  switch (seihash->hash_type) {
71
0
  case sei_decoded_picture_hash_type_MD5: loginfo(LogSEI,"MD5\n"); break;
72
0
  case sei_decoded_picture_hash_type_CRC: loginfo(LogSEI,"CRC\n"); break;
73
0
  case sei_decoded_picture_hash_type_checksum: loginfo(LogSEI,"checksum\n"); break;
74
0
  }
75
76
0
  int nHashes = sps->chroma_format_idc==0 ? 1 : 3;
77
0
  for (int i=0;i<nHashes;i++) {
78
0
    switch (seihash->hash_type) {
79
0
    case sei_decoded_picture_hash_type_MD5:
80
0
      loginfo(LogSEI,"  MD5[%d]: %02x", i,seihash->md5[i][0]);
81
0
      for (int b=1;b<16;b++) {
82
0
        loginfo(LogSEI,"*:%02x", seihash->md5[i][b]);
83
0
      }
84
0
      loginfo(LogSEI,"*\n");
85
0
      break;
86
87
0
    case sei_decoded_picture_hash_type_CRC:
88
0
      loginfo(LogSEI,"  CRC[%d]: %02x\n", i,seihash->crc[i]);
89
0
      break;
90
91
0
    case sei_decoded_picture_hash_type_checksum:
92
0
      loginfo(LogSEI,"  checksum[%d]: %04x\n", i,seihash->checksum[i]);
93
0
      break;
94
0
    }
95
0
  }
96
0
}
97
98
99
class raw_hash_data
100
{
101
public:
102
  raw_hash_data(int w, int stride);
103
  ~raw_hash_data();
104
105
  struct data_chunk {
106
    const uint8_t* data;
107
    int            len;
108
  };
109
110
  data_chunk prepare_8bit(const uint8_t* data,int y);
111
  data_chunk prepare_16bit(const uint8_t* data,int y);
112
113
private:
114
  int mWidth, mStride;
115
116
  uint8_t* mMem;
117
};
118
119
120
raw_hash_data::raw_hash_data(int w, int stride)
121
0
{
122
0
  mWidth=w;
123
0
  mStride=stride;
124
0
  mMem = nullptr;
125
0
}
126
127
raw_hash_data::~raw_hash_data()
128
0
{
129
0
  delete[] mMem;
130
0
}
131
132
raw_hash_data::data_chunk raw_hash_data::prepare_8bit(const uint8_t* data,int y)
133
0
{
134
0
  data_chunk chunk;
135
0
  chunk.data = data+y*mStride;
136
0
  chunk.len  = mWidth;
137
0
  return chunk;
138
0
}
139
140
raw_hash_data::data_chunk raw_hash_data::prepare_16bit(const uint8_t* data,int y)
141
0
{
142
0
  if (mMem == nullptr) {
143
0
    mMem = new uint8_t[2*mWidth];
144
0
  }
145
146
0
  const uint16_t* data16 = (uint16_t*)data;
147
148
0
  for (int x=0; x<mWidth; x++) {
149
0
    mMem[2*x+0] = data16[y*mStride+x] & 0xFF;
150
0
    mMem[2*x+1] = data16[y*mStride+x] >> 8;
151
0
  }
152
153
0
  data_chunk chunk;
154
0
  chunk.data = mMem;
155
0
  chunk.len  = 2*mWidth;
156
0
  return chunk;
157
0
}
158
159
160
static uint32_t compute_checksum(uint8_t* data,int w,int h,int stride, int bit_depth)
161
0
{
162
0
  uint32_t sum = 0;
163
164
0
  if (bit_depth<=8) {
165
0
    for (int y=0; y<h; y++)
166
0
      for(int x=0; x<w; x++) {
167
0
        uint8_t xorMask = ( x & 0xFF ) ^ ( y & 0xFF ) ^ ( x  >>  8 ) ^ ( y  >>  8 );
168
0
        sum += data[y*stride + x] ^ xorMask;
169
0
      }
170
0
  }
171
0
  else {
172
0
    auto* data16 = reinterpret_cast<uint16_t*>(data);
173
0
    int stride16 = stride / 2;
174
0
    for (int y=0; y<h; y++)
175
0
      for(int x=0; x<w; x++) {
176
0
        uint8_t xorMask = ( x & 0xFF ) ^ ( y & 0xFF ) ^ ( x  >>  8 ) ^ ( y  >>  8 );
177
0
        sum += (data16[y*stride16 + x] & 0xFF) ^ xorMask;
178
0
        sum += (data16[y*stride16 + x] >> 8)   ^ xorMask;
179
0
      }
180
0
  }
181
182
0
  return sum & 0xFFFFFFFF;
183
0
}
184
185
/*
186
static inline uint16_t crc_process_byte(uint16_t crc, uint8_t byte)
187
{
188
  for (int bit=0;bit<8;bit++) {
189
    int bitVal = (byte >> (7-bit)) & 1;
190
191
    int crcMsb = (crc>>15) & 1;
192
    crc = (((crc<<1) + bitVal) & 0xFFFF);
193
194
    if (crcMsb) { crc ^=  0x1021; }
195
  }
196
197
  return crc;
198
}
199
200
static uint16_t compute_CRC_8bit_old(const uint8_t* data,int w,int h,int stride)
201
{
202
  uint16_t crc = 0xFFFF;
203
204
  for (int y=0; y<h; y++)
205
    for(int x=0; x<w; x++) {
206
      crc = crc_process_byte(crc, data[y*stride+x]);
207
    }
208
209
  crc = crc_process_byte(crc, 0);
210
  crc = crc_process_byte(crc, 0);
211
212
  return crc;
213
}
214
*/
215
216
static inline uint16_t crc_process_byte_parallel(uint16_t crc, uint8_t byte)
217
0
{
218
0
  uint16_t s = byte ^ (crc >> 8);
219
0
  uint16_t t = s ^ (s >> 4);
220
221
0
  return  ((crc << 8) ^
222
0
     t ^
223
0
     (t <<  5) ^
224
0
     (t << 12)) & 0xFFFF;
225
0
}
226
227
static uint32_t compute_CRC_8bit_fast(const uint8_t* data,int w,int h,int stride, int bit_depth)
228
0
{
229
0
  raw_hash_data raw_data(w,stride);
230
231
0
  uint16_t crc = 0xFFFF;
232
233
0
  crc = crc_process_byte_parallel(crc, 0);
234
0
  crc = crc_process_byte_parallel(crc, 0);
235
236
0
  for (int y=0; y<h; y++) {
237
0
    raw_hash_data::data_chunk chunk;
238
239
0
    if (bit_depth>8)
240
0
      chunk = raw_data.prepare_16bit(data, y);
241
0
    else
242
0
      chunk = raw_data.prepare_8bit(data, y);
243
244
0
    for(int x=0; x<chunk.len; x++) {
245
0
      crc = crc_process_byte_parallel(crc, chunk.data[x]);
246
0
    }
247
0
  }
248
249
0
  return crc;
250
0
}
251
252
253
static void compute_MD5(uint8_t* data,int w,int h,int stride, uint8_t* result, int bit_depth)
254
0
{
255
0
  MD5_CTX md5;
256
0
  MD5_Init(&md5);
257
258
0
  raw_hash_data raw_data(w,stride);
259
260
0
  for (int y=0; y<h; y++) {
261
0
    raw_hash_data::data_chunk chunk;
262
263
0
    if (bit_depth>8)
264
0
      chunk = raw_data.prepare_16bit(data, y);
265
0
    else
266
0
      chunk = raw_data.prepare_8bit(data, y);
267
268
0
    MD5_Update(&md5, (void*)chunk.data, chunk.len);
269
0
  }
270
271
0
  MD5_Final(result, &md5);
272
0
}
273
274
275
static de265_error process_sei_decoded_picture_hash(const sei_message* sei, de265_image* img)
276
0
{
277
0
  const sei_decoded_picture_hash* seihash = &sei->data.decoded_picture_hash;
278
279
  /* Do not check SEI on pictures that are not output.
280
     Hash may be wrong, because of a broken link (BLA).
281
     This happens, for example in conformance stream RAP_B, where a EOS-NAL
282
     appears before a CRA (POC=32). */
283
0
  if (img->PicOutputFlag == false) {
284
0
    return DE265_OK;
285
0
  }
286
287
  //write_picture(img);
288
289
0
  int nHashes = img->get_sps().chroma_format_idc==0 ? 1 : 3;
290
0
  for (int i=0;i<nHashes;i++) {
291
0
    uint8_t* data;
292
0
    int w,h,stride;
293
294
0
    w = img->get_width(i);
295
0
    h = img->get_height(i);
296
297
0
    data = img->get_image_plane(i);
298
0
    stride = img->get_image_stride(i);
299
300
0
    switch (seihash->hash_type) {
301
0
    case sei_decoded_picture_hash_type_MD5:
302
0
      {
303
0
        uint8_t md5[16];
304
0
        compute_MD5(data,w,h,stride,md5, img->get_bit_depth(i));
305
306
/*
307
        fprintf(stderr,"computed MD5: ");
308
        for (int b=0;b<16;b++) {
309
          fprintf(stderr,"%02x", md5[b]);
310
        }
311
        fprintf(stderr,"\n");
312
*/
313
314
0
        for (int b=0;b<16;b++) {
315
0
          if (md5[b] != seihash->md5[i][b]) {
316
/*
317
            fprintf(stderr,"SEI decoded picture MD5 mismatch (POC=%d)\n", img->PicOrderCntVal);
318
*/
319
0
            return DE265_ERROR_CHECKSUM_MISMATCH;
320
0
          }
321
0
        }
322
0
      }
323
0
      break;
324
325
0
    case sei_decoded_picture_hash_type_CRC:
326
0
      {
327
0
        uint16_t crc = compute_CRC_8bit_fast(data,w,h,stride, img->get_bit_depth(i));
328
329
0
        logtrace(LogSEI,"SEI decoded picture hash: %04x <-[%d]-> decoded picture: %04x\n",
330
0
                 seihash->crc[i], i, crc);
331
332
0
        if (crc != seihash->crc[i]) {
333
/*
334
          fprintf(stderr,"SEI decoded picture hash: %04x, decoded picture: %04x (POC=%d)\n",
335
                  seihash->crc[i], crc, img->PicOrderCntVal);
336
*/
337
0
          return DE265_ERROR_CHECKSUM_MISMATCH;
338
0
        }
339
0
      }
340
0
      break;
341
342
0
    case sei_decoded_picture_hash_type_checksum:
343
0
      {
344
0
        uint32_t chksum = compute_checksum(data,w,h,stride, img->get_bit_depth(i));
345
346
0
        if (chksum != seihash->checksum[i]) {
347
/*
348
          fprintf(stderr,"SEI decoded picture hash: %04x, decoded picture: %04x (POC=%d)\n",
349
                  seihash->checksum[i], chksum, img->PicOrderCntVal);
350
*/
351
0
          return DE265_ERROR_CHECKSUM_MISMATCH;
352
0
        }
353
0
      }
354
0
      break;
355
0
    }
356
0
  }
357
358
0
  loginfo(LogSEI,"decoded picture hash checked: OK\n");
359
  //printf("checked picture %d SEI: OK\n", img->PicOrderCntVal);
360
361
0
  return DE265_OK;
362
0
}
363
364
365
141
#define MAX_SEI_SIZE UINT32_C(0xFFFFFFFF)
366
367
de265_error read_sei(bitreader* reader, sei_message* sei, bool suffix, const seq_parameter_set* sps)
368
60
{
369
60
  uint16_t payload_type = 0;
370
60
  for (;;)
371
175
    {
372
175
      uint8_t byte = static_cast<uint8_t>(reader->get_bits(8));
373
374
175
      if (std::numeric_limits<uint16_t>::max() - byte < payload_type) {
375
0
        return DE265_ERROR_CANNOT_PROCESS_SEI;
376
0
      }
377
378
175
      payload_type += byte;
379
175
      if (byte != 0xFF) { break; }
380
175
    }
381
382
  //printf("SEI payload: %d\n",payload_type);
383
384
60
  uint32_t payload_size = 0;
385
60
  for (;;)
386
141
    {
387
141
      uint32_t byte = reader->get_bits(8);
388
389
141
      if (MAX_SEI_SIZE - byte < payload_type) {
390
0
        return DE265_ERROR_CANNOT_PROCESS_SEI;
391
0
      }
392
393
141
      payload_size += byte;
394
141
      if (byte != 0xFF) { break; }
395
141
    }
396
397
60
  sei->payload_type = payload_type;
398
60
  sei->payload_size = payload_size;
399
400
401
  // --- sei message dispatch
402
403
60
  de265_error err = DE265_OK;
404
405
60
  switch (sei->payload_type) {
406
0
  case sei_payload_type_decoded_picture_hash:
407
0
    err = read_sei_decoded_picture_hash(reader,sei,sps);
408
0
    break;
409
410
60
  default:
411
    // TODO: unknown SEI messages are ignored
412
60
    break;
413
60
  }
414
415
60
  return err;
416
60
}
417
418
void dump_sei(const sei_message* sei, const seq_parameter_set* sps)
419
60
{
420
60
  loginfo(LogHeaders,"SEI message: %s\n", sei_type_name(sei->payload_type));
421
422
60
  switch (sei->payload_type) {
423
0
  case sei_payload_type_decoded_picture_hash:
424
0
    dump_sei_decoded_picture_hash(sei, sps);
425
0
    break;
426
427
60
  default:
428
    // TODO: unknown SEI messages are ignored
429
60
    break;
430
60
  }
431
60
}
432
433
434
de265_error process_sei(const sei_message* sei, de265_image* img)
435
0
{
436
0
  de265_error err = DE265_OK;
437
438
0
  switch (sei->payload_type) {
439
0
  case sei_payload_type_decoded_picture_hash:
440
0
    if (img->decctx->param_sei_check_hash) {
441
0
      err = process_sei_decoded_picture_hash(sei, img);
442
0
      if (err==DE265_OK) {
443
        //printf("SEI check ok\n");
444
0
      }
445
0
    }
446
447
0
    break;
448
449
0
  default:
450
    // TODO: unknown SEI messages are ignored
451
0
    break;
452
0
  }
453
454
0
  return err;
455
0
}
456
457
458
const char* sei_type_name(uint16_t type)
459
0
{
460
0
  switch (type) {
461
0
  case sei_payload_type_buffering_period:
462
0
    return "buffering_period";
463
0
  case sei_payload_type_pic_timing:
464
0
    return "pic_timing";
465
0
  case sei_payload_type_pan_scan_rect:
466
0
    return "pan_scan_rect";
467
0
  case sei_payload_type_filler_payload:
468
0
    return "filler_payload";
469
0
  case sei_payload_type_user_data_registered_itu_t_t35:
470
0
    return "user_data_registered_itu_t_t35";
471
0
  case sei_payload_type_user_data_unregistered:
472
0
    return "user_data_unregistered";
473
0
  case sei_payload_type_recovery_point:
474
0
    return "recovery_point";
475
0
  case sei_payload_type_scene_info:
476
0
    return "scene_info";
477
0
  case sei_payload_type_picture_snapshot:
478
0
    return "picture_snapshot";
479
0
  case sei_payload_type_progressive_refinement_segment_start:
480
0
    return "progressive_refinement_segment_start";
481
0
  case sei_payload_type_progressive_refinement_segment_end:
482
0
    return "progressive_refinement_segment_end";
483
0
  case sei_payload_type_film_grain_characteristics:
484
0
    return "film_grain_characteristics";
485
0
  case sei_payload_type_post_filter_hint:
486
0
    return "post_filter_hint";
487
0
  case sei_payload_type_tone_mapping_info:
488
0
    return "tone_mapping_info";
489
0
  case sei_payload_type_frame_packing_arrangement:
490
0
    return "frame_packing_arrangement";
491
0
  case sei_payload_type_display_orientation:
492
0
    return "display_orientation";
493
0
  case sei_payload_type_structure_of_pictures_info:
494
0
    return "structure_of_pictures_info";
495
0
  case sei_payload_type_active_parameter_sets:
496
0
    return "active_parameter_sets";
497
0
  case sei_payload_type_decoding_unit_info:
498
0
    return "decoding_unit_info";
499
0
  case sei_payload_type_temporal_sub_layer_zero_index:
500
0
    return "temporal_sub_layer_zero_index";
501
0
  case sei_payload_type_decoded_picture_hash:
502
0
    return "decoded_picture_hash";
503
0
  case sei_payload_type_scalable_nesting:
504
0
    return "scalable_nesting";
505
0
  case sei_payload_type_region_refresh_info:
506
0
    return "region_refresh_info";
507
0
  case sei_payload_type_no_display:
508
0
    return "no_display";
509
0
  case sei_payload_type_motion_constrained_tile_sets:
510
0
    return "motion_constrained_tile_sets";
511
512
0
  default:
513
0
    return "unknown SEI message";
514
0
  }
515
0
}