Coverage Report

Created: 2025-11-14 07:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/sequences/track.cc
Line
Count
Source
1
/*
2
 * HEIF image base codec.
3
 * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libheif.
6
 *
7
 * libheif 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
 * libheif 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 libheif.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include <cstring>
22
#include "track.h"
23
#include "context.h"
24
#include "sequences/seq_boxes.h"
25
#include "sequences/chunk.h"
26
#include "sequences/track_visual.h"
27
#include "sequences/track_metadata.h"
28
#include "api_structs.h"
29
#include <limits>
30
31
32
TrackOptions& TrackOptions::operator=(const TrackOptions& src)
33
0
{
34
0
  if (&src == this) {
35
0
    return *this;
36
0
  }
37
38
0
  this->track_timescale = src.track_timescale;
39
0
  this->write_sample_aux_infos_interleaved = src.write_sample_aux_infos_interleaved;
40
0
  this->with_sample_tai_timestamps = src.with_sample_tai_timestamps;
41
42
0
  if (src.tai_clock_info) {
43
0
    this->tai_clock_info = heif_tai_clock_info_alloc();
44
0
    heif_tai_clock_info_copy(this->tai_clock_info, src.tai_clock_info);
45
0
  }
46
0
  else {
47
0
    this->tai_clock_info = nullptr;
48
0
  }
49
50
0
  this->with_sample_content_ids = src.with_sample_content_ids;
51
0
  this->gimi_track_content_id = src.gimi_track_content_id;
52
53
0
  return *this;
54
0
}
55
56
57
SampleAuxInfoHelper::SampleAuxInfoHelper(bool interleaved)
58
0
    : m_interleaved(interleaved)
59
0
{
60
0
  m_saiz = std::make_shared<Box_saiz>();
61
0
  m_saio = std::make_shared<Box_saio>();
62
0
}
63
64
65
void SampleAuxInfoHelper::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter)
66
0
{
67
0
  m_saiz->set_aux_info_type(aux_info_type, aux_info_type_parameter);
68
0
  m_saio->set_aux_info_type(aux_info_type, aux_info_type_parameter);
69
0
}
70
71
Error SampleAuxInfoHelper::add_sample_info(const std::vector<uint8_t>& data)
72
0
{
73
0
  if (data.size() > 0xFF) {
74
0
    return {heif_error_Encoding_error,
75
0
            heif_suberror_Unspecified,
76
0
            "Encoded sample auxiliary information exceeds maximum size"};
77
0
  }
78
79
0
  m_saiz->add_sample_size(static_cast<uint8_t>(data.size()));
80
81
0
  m_data.insert(m_data.end(), data.begin(), data.end());
82
83
0
  return Error::Ok;
84
0
}
85
86
void SampleAuxInfoHelper::add_nonpresent_sample()
87
0
{
88
0
  m_saiz->add_nonpresent_sample();
89
0
}
90
91
92
void SampleAuxInfoHelper::write_interleaved(const std::shared_ptr<HeifFile>& file)
93
0
{
94
0
  if (m_interleaved && !m_data.empty()) {
95
0
    uint64_t pos = file->append_mdat_data(m_data);
96
0
    m_saio->add_sample_offset(pos);
97
98
0
    m_data.clear();
99
0
  }
100
0
}
101
102
void SampleAuxInfoHelper::write_all(const std::shared_ptr<Box>& parent, const std::shared_ptr<HeifFile>& file)
103
0
{
104
0
  parent->append_child_box(m_saiz);
105
0
  parent->append_child_box(m_saio);
106
107
0
  if (!m_data.empty()) {
108
0
    uint64_t pos = file->append_mdat_data(m_data);
109
0
    m_saio->add_sample_offset(pos);
110
0
  }
111
0
}
112
113
114
SampleAuxInfoReader::SampleAuxInfoReader(std::shared_ptr<Box_saiz> saiz,
115
                                         std::shared_ptr<Box_saio> saio)
116
0
{
117
0
  m_saiz = saiz;
118
0
  m_saio = saio;
119
120
0
  m_contiguous = (saio->get_num_samples() == 1);
121
0
  if (m_contiguous) {
122
0
    uint64_t offset = saio->get_sample_offset(0);
123
0
    auto nSamples = saiz->get_num_samples();
124
125
0
    for (uint32_t i=0;i<nSamples;i++) {
126
0
      m_contiguous_offsets.push_back(offset);
127
0
      offset += saiz->get_sample_size(i);
128
0
    }
129
130
    // TODO: we could add a special case for contiguous data with constant size
131
0
  }
132
0
}
133
134
135
heif_sample_aux_info_type SampleAuxInfoReader::get_type() const
136
0
{
137
0
  heif_sample_aux_info_type type;
138
0
  type.type = m_saiz->get_aux_info_type();
139
0
  type.parameter = m_saiz->get_aux_info_type_parameter();
140
0
  return type;
141
0
}
142
143
144
Result<std::vector<uint8_t>> SampleAuxInfoReader::get_sample_info(const HeifFile* file, uint32_t idx)
145
0
{
146
0
  uint64_t offset;
147
0
  if (m_contiguous) {
148
0
    offset = m_contiguous_offsets[idx];
149
0
  }
150
0
  else {
151
0
    offset = m_saio->get_sample_offset(idx);
152
0
  }
153
154
0
  uint8_t size = m_saiz->get_sample_size(idx);
155
156
0
  std::vector<uint8_t> data;
157
0
  Error err = file->append_data_from_file_range(data, offset, size);
158
0
  if (err) {
159
0
    return err;
160
0
  }
161
162
0
  return data;
163
0
}
164
165
166
std::shared_ptr<HeifFile> Track::get_file() const
167
0
{
168
0
  return m_heif_context->get_heif_file();
169
0
}
170
171
172
Track::Track(HeifContext* ctx)
173
0
{
174
0
  m_heif_context = ctx;
175
0
}
176
177
178
Error Track::load(const std::shared_ptr<Box_trak>& trak_box)
179
0
{
180
0
  m_trak = trak_box;
181
182
0
  auto tkhd = trak_box->get_child_box<Box_tkhd>();
183
0
  if (!tkhd) {
184
0
    return {
185
0
      heif_error_Invalid_input,
186
0
      heif_suberror_Unspecified,
187
0
      "Track has no 'tkhd' box."
188
0
    };
189
0
  }
190
191
0
  m_id = tkhd->get_track_id();
192
193
0
  auto edts = trak_box->get_child_box<Box_edts>();
194
0
  if (edts) {
195
0
    m_elst = edts->get_child_box<Box_elst>();
196
0
  }
197
198
0
  auto mdia = trak_box->get_child_box<Box_mdia>();
199
0
  if (!mdia) {
200
0
    return {
201
0
      heif_error_Invalid_input,
202
0
      heif_suberror_Unspecified,
203
0
      "Track has no 'mdia' box."
204
0
    };
205
0
  }
206
207
0
  m_tref = trak_box->get_child_box<Box_tref>();
208
209
0
  auto hdlr = mdia->get_child_box<Box_hdlr>();
210
0
  if (!hdlr) {
211
0
    return {
212
0
      heif_error_Invalid_input,
213
0
      heif_suberror_Unspecified,
214
0
      "Track has no 'hdlr' box."
215
0
    };
216
0
  }
217
218
0
  m_handler_type = hdlr->get_handler_type();
219
220
0
  m_minf = mdia->get_child_box<Box_minf>();
221
0
  if (!m_minf) {
222
0
    return {
223
0
      heif_error_Invalid_input,
224
0
      heif_suberror_Unspecified,
225
0
      "Track has no 'minf' box."
226
0
    };
227
0
  }
228
229
0
  m_mdhd = mdia->get_child_box<Box_mdhd>();
230
0
  if (!m_mdhd) {
231
0
    return {
232
0
      heif_error_Invalid_input,
233
0
      heif_suberror_Unspecified,
234
0
      "Track has no 'mdhd' box."
235
0
    };
236
0
  }
237
238
0
  auto stbl = m_minf->get_child_box<Box_stbl>();
239
0
  if (!stbl) {
240
0
    return {
241
0
      heif_error_Invalid_input,
242
0
      heif_suberror_Unspecified,
243
0
      "Track has no 'stbl' box."
244
0
    };
245
0
  }
246
247
0
  m_stsd = stbl->get_child_box<Box_stsd>();
248
0
  if (!m_stsd) {
249
0
    return {
250
0
      heif_error_Invalid_input,
251
0
      heif_suberror_Unspecified,
252
0
      "Track has no 'stsd' box."
253
0
    };
254
0
  }
255
256
0
  m_stsc = stbl->get_child_box<Box_stsc>();
257
0
  if (!m_stsc) {
258
0
    return {
259
0
      heif_error_Invalid_input,
260
0
      heif_suberror_Unspecified,
261
0
      "Track has no 'stsc' box."
262
0
    };
263
0
  }
264
265
0
  m_stco = stbl->get_child_box<Box_stco>();
266
0
  if (!m_stco) {
267
0
    return {
268
0
      heif_error_Invalid_input,
269
0
      heif_suberror_Unspecified,
270
0
      "Track has no 'stco' box."
271
0
    };
272
0
  }
273
274
0
  m_stsz = stbl->get_child_box<Box_stsz>();
275
0
  if (!m_stsz) {
276
0
    return {
277
0
      heif_error_Invalid_input,
278
0
      heif_suberror_Unspecified,
279
0
      "Track has no 'stsz' box."
280
0
    };
281
0
  }
282
283
0
  m_stts = stbl->get_child_box<Box_stts>();
284
285
0
  const std::vector<uint32_t>& chunk_offsets = m_stco->get_offsets();
286
0
  assert(chunk_offsets.size() <= (size_t) std::numeric_limits<uint32_t>::max()); // There cannot be more than uint32_t chunks.
287
288
0
  uint32_t current_sample_idx = 0;
289
0
  int32_t previous_sample_description_index = -1;
290
291
0
  for (size_t chunk_idx = 0; chunk_idx < chunk_offsets.size(); chunk_idx++) {
292
0
    auto* s2c = m_stsc->get_chunk(static_cast<uint32_t>(chunk_idx + 1));
293
0
    if (!s2c) {
294
0
      return {
295
0
        heif_error_Invalid_input,
296
0
        heif_suberror_Unspecified,
297
0
        "'stco' box references a non-existing chunk."
298
0
      };
299
0
    }
300
301
0
    Box_stsc::SampleToChunk sampleToChunk = *s2c;
302
303
0
    auto sample_description = m_stsd->get_sample_entry(sampleToChunk.sample_description_index - 1);
304
0
    if (!sample_description) {
305
0
      return {
306
0
        heif_error_Invalid_input,
307
0
        heif_suberror_Unspecified,
308
0
        "Track references a non-existing sample description."
309
0
      };
310
0
    }
311
312
0
    if (auto auxi = sample_description->get_child_box<Box_auxi>()) {
313
0
      m_auxiliary_info_type = auxi->get_aux_track_type_urn();
314
0
    }
315
316
0
    if (m_first_taic == nullptr) {
317
0
      auto taic = sample_description->get_child_box<Box_taic>();
318
0
      if (taic) {
319
0
        m_first_taic = taic;
320
0
      }
321
0
    }
322
323
0
    auto chunk = std::make_shared<Chunk>(m_heif_context, m_id,
324
0
                                         current_sample_idx, sampleToChunk.samples_per_chunk,
325
0
                                         m_stco->get_offsets()[chunk_idx],
326
0
                                         m_stsz);
327
328
0
    if (auto visualSampleDescription = std::dynamic_pointer_cast<const Box_VisualSampleEntry>(sample_description)) {
329
0
      if (chunk_idx > 0 && (int32_t) sampleToChunk.sample_description_index == previous_sample_description_index) {
330
        // reuse decoder from previous chunk if it uses the sample sample_description_index
331
0
        chunk->set_decoder(m_chunks[chunk_idx - 1]->get_decoder());
332
0
      }
333
0
      else {
334
        // use a new decoder
335
0
        chunk->set_decoder(Decoder::alloc_for_sequence_sample_description_box(visualSampleDescription));
336
0
      }
337
0
    }
338
339
0
    m_chunks.push_back(chunk);
340
341
0
    current_sample_idx += sampleToChunk.samples_per_chunk;
342
0
    previous_sample_description_index = sampleToChunk.sample_description_index;
343
0
  }
344
345
  // --- read sample auxiliary information boxes
346
347
0
  std::vector<std::shared_ptr<Box_saiz>> saiz_boxes = stbl->get_child_boxes<Box_saiz>();
348
0
  std::vector<std::shared_ptr<Box_saio>> saio_boxes = stbl->get_child_boxes<Box_saio>();
349
350
0
  for (const auto& saiz : saiz_boxes) {
351
0
    uint32_t aux_info_type = saiz->get_aux_info_type();
352
0
    uint32_t aux_info_type_parameter = saiz->get_aux_info_type_parameter();
353
354
    // find the corresponding saio box
355
356
0
    std::shared_ptr<Box_saio> saio;
357
0
    for (const auto& candidate : saio_boxes) {
358
0
      if (candidate->get_aux_info_type() == aux_info_type &&
359
0
          candidate->get_aux_info_type_parameter() == aux_info_type_parameter) {
360
0
        saio = candidate;
361
0
        break;
362
0
      }
363
0
    }
364
365
0
    if (saio) {
366
0
      if (aux_info_type == fourcc("suid")) {
367
0
        m_aux_reader_content_ids = std::make_unique<SampleAuxInfoReader>(saiz, saio);
368
0
      }
369
370
0
      if (aux_info_type == fourcc("stai")) {
371
0
        m_aux_reader_tai_timestamps = std::make_unique<SampleAuxInfoReader>(saiz, saio);
372
0
      }
373
0
    }
374
0
  }
375
376
  // --- read track properties
377
378
0
  if (auto meta = trak_box->get_child_box<Box_meta>()) {
379
0
    auto iloc = meta->get_child_box<Box_iloc>();
380
0
    auto idat = meta->get_child_box<Box_idat>();
381
382
0
    auto iinf = meta->get_child_box<Box_iinf>();
383
0
    if (iinf) {
384
0
      auto infe_boxes = iinf->get_child_boxes<Box_infe>();
385
0
      for (const auto& box : infe_boxes) {
386
0
        if (box->get_item_type_4cc() == fourcc("uri ") &&
387
0
            box->get_item_uri_type() == "urn:uuid:15beb8e4-944d-5fc6-a3dd-cb5a7e655c73") {
388
0
          heif_item_id id = box->get_item_ID();
389
390
0
          std::vector<uint8_t> data;
391
0
          Error err = iloc->read_data(id, m_heif_context->get_heif_file()->get_reader(), idat, &data, m_heif_context->get_security_limits());
392
0
          if (err) {
393
            // TODO
394
0
          }
395
396
0
          Result contentIdResult = vector_to_string(data);
397
0
          if (!contentIdResult) {
398
            // TODO
399
0
          }
400
401
0
          m_track_info.gimi_track_content_id = *contentIdResult;
402
0
        }
403
0
      }
404
0
    }
405
0
  }
406
407
0
  init_sample_timing_table();
408
409
0
  return {};
410
0
}
411
412
413
Track::Track(HeifContext* ctx, uint32_t track_id, const TrackOptions* options, uint32_t handler_type)
414
0
{
415
0
  m_heif_context = ctx;
416
417
0
  m_moov = ctx->get_heif_file()->get_moov_box();
418
0
  assert(m_moov);
419
420
  // --- find next free track ID
421
422
0
  if (track_id == 0) {
423
0
    track_id = 1; // minimum track ID
424
425
0
    for (const auto& track : m_moov->get_child_boxes<Box_trak>()) {
426
0
      auto tkhd = track->get_child_box<Box_tkhd>();
427
428
0
      if (tkhd->get_track_id() >= track_id) {
429
0
        track_id = tkhd->get_track_id() + 1;
430
0
      }
431
0
    }
432
433
0
    auto mvhd = m_moov->get_child_box<Box_mvhd>();
434
0
    mvhd->set_next_track_id(track_id + 1);
435
436
0
    m_id = track_id;
437
0
  }
438
439
0
  m_trak = std::make_shared<Box_trak>();
440
0
  m_moov->append_child_box(m_trak);
441
442
0
  m_tkhd = std::make_shared<Box_tkhd>();
443
0
  m_trak->append_child_box(m_tkhd);
444
0
  m_tkhd->set_track_id(track_id);
445
446
0
  auto mdia = std::make_shared<Box_mdia>();
447
0
  m_trak->append_child_box(mdia);
448
449
0
  m_mdhd = std::make_shared<Box_mdhd>();
450
0
  m_mdhd->set_timescale(options ? options->track_timescale : 90000);
451
0
  mdia->append_child_box(m_mdhd);
452
453
0
  m_hdlr = std::make_shared<Box_hdlr>();
454
0
  mdia->append_child_box(m_hdlr);
455
0
  m_hdlr->set_handler_type(handler_type);
456
457
0
  m_minf = std::make_shared<Box_minf>();
458
0
  mdia->append_child_box(m_minf);
459
460
  // vmhd is added in Track_Visual
461
462
0
  m_stbl = std::make_shared<Box_stbl>();
463
0
  m_minf->append_child_box(m_stbl);
464
465
0
  m_stsd = std::make_shared<Box_stsd>();
466
0
  m_stbl->append_child_box(m_stsd);
467
468
0
  m_stts = std::make_shared<Box_stts>();
469
0
  m_stbl->append_child_box(m_stts);
470
471
0
  m_stsc = std::make_shared<Box_stsc>();
472
0
  m_stbl->append_child_box(m_stsc);
473
474
0
  m_stsz = std::make_shared<Box_stsz>();
475
0
  m_stbl->append_child_box(m_stsz);
476
477
0
  m_stco = std::make_shared<Box_stco>();
478
0
  m_stbl->append_child_box(m_stco);
479
480
0
  m_stss = std::make_shared<Box_stss>();
481
0
  m_stbl->append_child_box(m_stss);
482
483
0
  if (options) {
484
0
    m_track_info = *options;
485
486
0
    if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) {
487
0
      m_aux_helper_tai_timestamps = std::make_unique<SampleAuxInfoHelper>(m_track_info.write_sample_aux_infos_interleaved);
488
0
      m_aux_helper_tai_timestamps->set_aux_info_type(fourcc("stai"));
489
0
    }
490
491
0
    if (m_track_info.with_sample_content_ids != heif_sample_aux_info_presence_none) {
492
0
      m_aux_helper_content_ids = std::make_unique<SampleAuxInfoHelper>(m_track_info.write_sample_aux_infos_interleaved);
493
0
      m_aux_helper_content_ids->set_aux_info_type(fourcc("suid"));
494
0
    }
495
496
0
    if (!options->gimi_track_content_id.empty()) {
497
0
      auto hdlr_box = std::make_shared<Box_hdlr>();
498
0
      hdlr_box->set_handler_type(fourcc("meta"));
499
500
0
      auto uuid_box = std::make_shared<Box_infe>();
501
0
      uuid_box->set_item_type_4cc(fourcc("uri "));
502
0
      uuid_box->set_item_uri_type("urn:uuid:15beb8e4-944d-5fc6-a3dd-cb5a7e655c73");
503
0
      uuid_box->set_item_ID(1);
504
505
0
      auto iinf_box = std::make_shared<Box_iinf>();
506
0
      iinf_box->append_child_box(uuid_box);
507
508
0
      std::vector<uint8_t> track_uuid_vector;
509
0
      track_uuid_vector.insert(track_uuid_vector.begin(),
510
0
                               options->gimi_track_content_id.c_str(),
511
0
                               options->gimi_track_content_id.c_str() + options->gimi_track_content_id.length() + 1);
512
513
0
      auto iloc_box = std::make_shared<Box_iloc>();
514
0
      iloc_box->append_data(1, track_uuid_vector, 1);
515
516
0
      auto meta_box = std::make_shared<Box_meta>();
517
0
      meta_box->append_child_box(hdlr_box);
518
0
      meta_box->append_child_box(iinf_box);
519
0
      meta_box->append_child_box(iloc_box);
520
521
0
      m_trak->append_child_box(meta_box);
522
0
    }
523
0
  }
524
0
}
525
526
527
Result<std::shared_ptr<Track>> Track::alloc_track(HeifContext* ctx, const std::shared_ptr<Box_trak>& trak)
528
0
{
529
0
  auto mdia = trak->get_child_box<Box_mdia>();
530
0
  if (!mdia) {
531
0
    return Error{
532
0
      heif_error_Invalid_input,
533
0
      heif_suberror_Unspecified,
534
0
      "Track has no 'mdia' box."
535
0
    };
536
0
  }
537
538
0
  auto hdlr = mdia->get_child_box<Box_hdlr>();
539
0
  if (!hdlr) {
540
0
    return Error{
541
0
      heif_error_Invalid_input,
542
0
      heif_suberror_Unspecified,
543
0
      "Track has no 'hdlr' box."
544
0
    };
545
0
  }
546
547
0
  std::shared_ptr<Track> track;
548
549
0
  switch (hdlr->get_handler_type()) {
550
0
    case fourcc("pict"):
551
0
    case fourcc("vide"):
552
0
    case fourcc("auxv"):
553
0
      track = std::make_shared<Track_Visual>(ctx);
554
0
      break;
555
0
    case fourcc("meta"):
556
0
      track = std::make_shared<Track_Metadata>(ctx);
557
0
    default: {
558
0
      std::stringstream sstr;
559
0
      sstr << "Track with unsupported handler type '" << fourcc_to_string(hdlr->get_handler_type()) << "'.";
560
0
      return Error{
561
0
        heif_error_Unsupported_filetype,
562
0
        heif_suberror_Unspecified,
563
0
        sstr.str()
564
0
      };
565
0
    }
566
0
  }
567
568
0
  assert(track);
569
0
  Error loadError = track->load(trak);
570
0
  if (loadError) {
571
0
    return loadError;
572
0
  }
573
574
0
  return {track};
575
0
}
576
577
578
bool Track::is_visual_track() const
579
0
{
580
0
  return (m_handler_type == fourcc("pict") ||
581
0
          m_handler_type == fourcc("vide"));
582
0
}
583
584
585
static const char* cAuxType_alpha_miaf = "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha";
586
static const char* cAuxType_alpha_hevc = "urn:mpeg:hevc:2015:auxid:1";
587
static const char* cAuxType_alpha_avc = "urn:mpeg:avc:2015:auxid:1";
588
589
heif_auxiliary_track_info_type Track::get_auxiliary_info_type() const
590
0
{
591
0
  if (m_auxiliary_info_type == cAuxType_alpha_miaf ||
592
0
      m_auxiliary_info_type == cAuxType_alpha_hevc ||
593
0
      m_auxiliary_info_type == cAuxType_alpha_avc) {
594
0
    return heif_auxiliary_track_info_type_alpha;
595
0
  }
596
0
  else {
597
0
    return heif_auxiliary_track_info_type_unknown;
598
0
  }
599
0
}
600
601
602
void Track::set_auxiliary_info_type(heif_auxiliary_track_info_type type)
603
0
{
604
0
  switch (type) {
605
0
    case heif_auxiliary_track_info_type_alpha:
606
0
      m_auxiliary_info_type = cAuxType_alpha_miaf;
607
0
      break;
608
0
    default:
609
0
      m_auxiliary_info_type.clear();
610
0
      break;
611
0
  }
612
0
}
613
614
615
uint32_t Track::get_first_cluster_sample_entry_type() const
616
0
{
617
0
  if (m_stsd->get_num_sample_entries() == 0) {
618
0
    return 0; // TODO: error ? Or can we assume at this point that there is at least one sample entry?
619
0
  }
620
621
0
  return m_stsd->get_sample_entry(0)->get_short_type();
622
0
}
623
624
625
Result<std::string> Track::get_first_cluster_urim_uri() const
626
0
{
627
0
  if (m_stsd->get_num_sample_entries() == 0) {
628
0
    return Error{heif_error_Invalid_input,
629
0
                 heif_suberror_Unspecified,
630
0
                 "This track has no sample entries."};
631
0
  }
632
633
0
  std::shared_ptr<const Box> sampleEntry = m_stsd->get_sample_entry(0);
634
0
  auto urim = std::dynamic_pointer_cast<const Box_URIMetaSampleEntry>(sampleEntry);
635
0
  if (!urim) {
636
0
    return Error{heif_error_Usage_error,
637
0
                 heif_suberror_Unspecified,
638
0
                 "This cluster is no 'urim' sample entry."};
639
0
  }
640
641
0
  std::shared_ptr<const Box_uri> uri = urim->get_child_box<const Box_uri>();
642
0
  if (!uri) {
643
0
    return Error{heif_error_Invalid_input,
644
0
                 heif_suberror_Unspecified,
645
0
                 "The 'urim' box has no 'uri' child box."};
646
0
  }
647
648
0
  return uri->get_uri();
649
0
}
650
651
652
bool Track::end_of_sequence_reached() const
653
0
{
654
  //return (m_next_sample_to_be_processed > m_chunks.back()->last_sample_number());
655
0
  return m_next_sample_to_be_processed >= m_num_output_samples;
656
0
}
657
658
659
void Track::finalize_track()
660
0
{
661
0
  if (m_aux_helper_tai_timestamps) m_aux_helper_tai_timestamps->write_all(m_stbl, get_file());
662
0
  if (m_aux_helper_content_ids) m_aux_helper_content_ids->write_all(m_stbl, get_file());
663
664
0
  uint64_t duration = m_stts->get_total_duration(true);
665
0
  m_mdhd->set_duration(duration);
666
0
}
667
668
669
uint64_t Track::get_duration_in_media_units() const
670
0
{
671
0
  return m_mdhd->get_duration();
672
0
}
673
674
675
uint32_t Track::get_timescale() const
676
0
{
677
0
  return m_mdhd->get_timescale();
678
0
}
679
680
681
void Track::set_track_duration_in_movie_units(uint64_t total_duration)
682
0
{
683
0
  m_tkhd->set_duration(total_duration);
684
685
0
  if (m_elst) {
686
0
    Box_elst::Entry entry;
687
0
    entry.segment_duration = total_duration;
688
689
0
    m_elst->add_entry(entry);
690
0
  }
691
0
}
692
693
694
void Track::enable_edit_list_repeat_mode(bool enable)
695
0
{
696
0
  if (!m_elst) {
697
0
    if (!enable) {
698
0
      return;
699
0
    }
700
701
0
    auto edts = std::make_shared<Box_edts>();
702
0
    m_trak->append_child_box(edts);
703
704
0
    m_elst = std::make_shared<Box_elst>();
705
0
    edts->append_child_box(m_elst);
706
707
0
    m_elst->enable_repeat_mode(enable);
708
0
  }
709
0
}
710
711
712
void Track::add_chunk(heif_compression_format format)
713
0
{
714
0
  auto chunk = std::make_shared<Chunk>(m_heif_context, m_id, format);
715
0
  m_chunks.push_back(chunk);
716
717
0
  int chunkIdx = (uint32_t) m_chunks.size();
718
0
  m_stsc->add_chunk(chunkIdx);
719
0
}
720
721
void Track::set_sample_description_box(std::shared_ptr<Box> sample_description_box)
722
0
{
723
  // --- add 'taic' when we store timestamps as sample auxiliary information
724
725
0
  if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) {
726
0
    auto taic = std::make_shared<Box_taic>();
727
0
    taic->set_from_tai_clock_info(m_track_info.tai_clock_info);
728
0
    sample_description_box->append_child_box(taic);
729
0
  }
730
731
0
  m_stsd->add_sample_entry(sample_description_box);
732
0
}
733
734
735
Error Track::write_sample_data(const std::vector<uint8_t>& raw_data, uint32_t sample_duration, bool is_sync_sample,
736
                               const heif_tai_timestamp_packet* tai, const std::string& gimi_contentID)
737
0
{
738
0
  size_t data_start = m_heif_context->get_heif_file()->append_mdat_data(raw_data);
739
740
  // first sample in chunk? -> write chunk offset
741
742
0
  if (m_stsc->last_chunk_empty()) {
743
    // if auxiliary data is interleaved, write it between the chunks
744
0
    if (m_aux_helper_tai_timestamps) m_aux_helper_tai_timestamps->write_interleaved(get_file());
745
0
    if (m_aux_helper_content_ids) m_aux_helper_content_ids->write_interleaved(get_file());
746
747
    // TODO
748
0
    assert(data_start < 0xFF000000); // add some headroom for header data
749
0
    m_stco->add_chunk_offset(static_cast<uint32_t>(data_start));
750
0
  }
751
752
0
  m_stsc->increase_samples_in_chunk(1);
753
754
0
  m_stsz->append_sample_size((uint32_t)raw_data.size());
755
756
0
  if (is_sync_sample) {
757
0
    m_stss->add_sync_sample(m_next_sample_to_be_processed + 1);
758
0
  }
759
760
0
  if (sample_duration == 0) {
761
0
    return {heif_error_Usage_error,
762
0
            heif_suberror_Unspecified,
763
0
            "Sample duration may not be 0"};
764
0
  }
765
766
0
  m_stts->append_sample_duration(sample_duration);
767
768
769
  // --- sample timestamp
770
771
0
  if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) {
772
0
    if (tai) {
773
0
      std::vector<uint8_t> tai_data = Box_itai::encode_tai_to_bitstream(tai);
774
0
      auto err = m_aux_helper_tai_timestamps->add_sample_info(tai_data);
775
0
      if (err) {
776
0
        return err;
777
0
      }
778
0
    }
779
0
    else if (m_track_info.with_sample_tai_timestamps == heif_sample_aux_info_presence_optional) {
780
0
      m_aux_helper_tai_timestamps->add_nonpresent_sample();
781
0
    }
782
0
    else {
783
0
      return {heif_error_Encoding_error,
784
0
              heif_suberror_Unspecified,
785
0
              "Mandatory TAI timestamp missing"};
786
0
    }
787
0
  }
788
789
  // --- sample content id
790
791
0
  if (m_track_info.with_sample_content_ids != heif_sample_aux_info_presence_none) {
792
0
    if (!gimi_contentID.empty()) {
793
0
      auto id = gimi_contentID;
794
0
      const char* id_str = id.c_str();
795
0
      std::vector<uint8_t> id_vector;
796
0
      id_vector.insert(id_vector.begin(), id_str, id_str + id.length() + 1);
797
0
      auto err = m_aux_helper_content_ids->add_sample_info(id_vector);
798
0
      if (err) {
799
0
        return err;
800
0
      }
801
0
    } else if (m_track_info.with_sample_content_ids == heif_sample_aux_info_presence_optional) {
802
0
      m_aux_helper_content_ids->add_nonpresent_sample();
803
0
    } else {
804
0
      return {heif_error_Encoding_error,
805
0
              heif_suberror_Unspecified,
806
0
              "Mandatory ContentID missing"};
807
0
    }
808
0
  }
809
810
0
  m_next_sample_to_be_processed++;
811
812
0
  return Error::Ok;
813
0
}
814
815
816
void Track::add_reference_to_track(uint32_t referenceType, uint32_t to_track_id)
817
0
{
818
0
  if (!m_tref) {
819
0
    m_tref = std::make_shared<Box_tref>();
820
0
    m_trak->append_child_box(m_tref);
821
0
  }
822
823
0
  m_tref->add_references(to_track_id, referenceType);
824
0
}
825
826
827
void Track::init_sample_timing_table()
828
0
{
829
0
  m_num_samples = m_stsz->num_samples();
830
831
  // --- build media timeline
832
833
0
  std::vector<SampleTiming> media_timeline;
834
835
0
  uint64_t current_decoding_time = 0;
836
0
  uint32_t current_chunk = 0;
837
838
0
  for (uint32_t i = 0; i<m_num_samples; i++) {
839
0
    SampleTiming timing;
840
0
    timing.sampleIdx = i;
841
0
    timing.media_decoding_time = current_decoding_time;
842
0
    timing.sample_duration_media_time = m_stts->get_sample_duration(i);
843
0
    current_decoding_time += timing.sample_duration_media_time;
844
845
0
    while (i > m_chunks[current_chunk]->last_sample_number()) {
846
0
      current_chunk++;
847
848
0
      if (current_chunk > m_chunks.size()) {
849
0
        timing.chunkIdx = 0; // TODO: error
850
0
      }
851
0
    }
852
853
0
    timing.chunkIdx = current_chunk;
854
855
0
    media_timeline.push_back(timing);
856
0
  }
857
858
  // --- build presentation timeline from editlist
859
860
0
  bool fallback = false;
861
862
0
  if (m_heif_context->get_sequence_timescale() != get_timescale()) {
863
0
    fallback = true;
864
0
  }
865
0
  else if (m_elst &&
866
0
           m_elst->num_entries() == 1 &&
867
0
           m_elst->get_entry(0).media_time == 0 &&
868
0
           m_elst->get_entry(0).segment_duration == m_mdhd->get_duration() &&
869
0
           m_elst->is_repeat_mode()) {
870
0
    m_presentation_timeline = media_timeline;
871
0
    m_num_output_samples = m_heif_context->get_sequence_duration() / get_duration_in_media_units() * media_timeline.size();
872
0
  }
873
0
  else {
874
0
    fallback = true;
875
0
  }
876
877
  // Fallback: just play the media timeline
878
0
  if (fallback) {
879
0
    m_presentation_timeline = media_timeline;
880
0
    m_num_output_samples = media_timeline.size();
881
0
  }
882
0
}
883
884
885
Result<heif_raw_sequence_sample*> Track::get_next_sample_raw_data(const heif_decoding_options* options)
886
0
{
887
0
  uint64_t num_output_samples = m_num_output_samples;
888
0
  if (options && options->ignore_sequence_editlist) {
889
0
    num_output_samples = m_num_samples;
890
0
  }
891
892
0
  if (m_next_sample_to_be_processed >= num_output_samples) {
893
0
    return Error{heif_error_End_of_sequence,
894
0
                 heif_suberror_Unspecified,
895
0
                 "End of sequence"};
896
0
  }
897
898
0
  const auto& sampleTiming = m_presentation_timeline[m_next_sample_to_be_processed % m_presentation_timeline.size()];
899
0
  uint32_t sample_idx = sampleTiming.sampleIdx;
900
0
  uint32_t chunk_idx = sampleTiming.chunkIdx;
901
902
0
  const std::shared_ptr<Chunk>& chunk = m_chunks[chunk_idx];
903
904
0
  DataExtent extent = chunk->get_data_extent_for_sample(sample_idx);
905
0
  auto readResult = extent.read_data();
906
0
  if (!readResult) {
907
0
    return readResult.error();
908
0
  }
909
910
0
  heif_raw_sequence_sample* sample = new heif_raw_sequence_sample();
911
0
  sample->data = **readResult;
912
913
  // read sample duration
914
915
0
  if (m_stts) {
916
0
    sample->duration = m_stts->get_sample_duration(sample_idx);
917
0
  }
918
919
  // --- read sample auxiliary data
920
921
0
  if (m_aux_reader_content_ids) {
922
0
    auto readResult = m_aux_reader_content_ids->get_sample_info(get_file().get(), sample_idx);
923
0
    if (!readResult) {
924
0
      return readResult.error();
925
0
    }
926
927
0
    if (!readResult->empty()) {
928
0
      Result<std::string> convResult = vector_to_string(*readResult);
929
0
      if (!convResult) {
930
0
        return convResult.error();
931
0
      }
932
933
0
      sample->gimi_sample_content_id = *convResult;
934
0
    }
935
0
  }
936
937
0
  if (m_aux_reader_tai_timestamps) {
938
0
    auto readResult = m_aux_reader_tai_timestamps->get_sample_info(get_file().get(), sample_idx);
939
0
    if (!readResult) {
940
0
      return readResult.error();
941
0
    }
942
943
0
    if (!readResult->empty()) {
944
0
      auto resultTai = Box_itai::decode_tai_from_vector(*readResult);
945
0
      if (!resultTai) {
946
0
        return resultTai.error();
947
0
      }
948
949
0
      sample->timestamp = heif_tai_timestamp_packet_alloc();
950
0
      heif_tai_timestamp_packet_copy(sample->timestamp, &*resultTai);
951
0
    }
952
0
  }
953
954
0
  m_next_sample_to_be_processed++;
955
956
0
  return sample;
957
0
}
958
959
960
std::vector<heif_sample_aux_info_type> Track::get_sample_aux_info_types() const
961
0
{
962
0
  std::vector<heif_sample_aux_info_type> types;
963
964
0
  if (m_aux_reader_tai_timestamps) types.emplace_back(m_aux_reader_tai_timestamps->get_type());
965
0
  if (m_aux_reader_content_ids) types.emplace_back(m_aux_reader_content_ids->get_type());
966
967
0
  return types;
968
0
}