Coverage Report

Created: 2026-05-24 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/sequences/track.h
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
#ifndef LIBHEIF_TRACK_H
22
#define LIBHEIF_TRACK_H
23
24
#include "error.h"
25
#include "api_structs.h"
26
#include "libheif/heif_plugin.h"
27
#include "libheif/heif_sequences.h"
28
#include <string>
29
#include <memory>
30
#include <vector>
31
32
class HeifContext;
33
34
class HeifPixelImage;
35
36
class Chunk;
37
38
class Box_trak;
39
40
41
class SampleAuxInfoHelper
42
{
43
public:
44
  SampleAuxInfoHelper(bool interleaved = false);
45
46
  void set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter = 0);
47
48
  Error add_sample_info(const std::vector<uint8_t>& data);
49
50
  void add_nonpresent_sample();
51
52
  void write_interleaved(const std::shared_ptr<HeifFile>& file);
53
54
  void write_all(const std::shared_ptr<Box>& parent, const std::shared_ptr<HeifFile>& file);
55
56
private:
57
  std::shared_ptr<Box_saiz> m_saiz;
58
  std::shared_ptr<Box_saio> m_saio;
59
60
  std::vector<uint8_t> m_data;
61
62
  bool m_interleaved;
63
};
64
65
66
class SampleAuxInfoReader
67
{
68
public:
69
  SampleAuxInfoReader(std::shared_ptr<Box_saiz>,
70
                      std::shared_ptr<Box_saio>,
71
                      const std::vector<std::shared_ptr<Chunk>>& chunks);
72
73
  heif_sample_aux_info_type get_type() const;
74
75
  Result<std::vector<uint8_t>> get_sample_info(const HeifFile* file, uint32_t sample_idx);
76
77
private:
78
  std::shared_ptr<Box_saiz> m_saiz;
79
  std::shared_ptr<Box_saio> m_saio;
80
81
  // If there is only one chunk and the SAI data sizes are constant, we do not need an offset table.
82
  // We just store the base offset and can directly calculate the sample offset from that.
83
  bool m_contiguous_and_constant_size=false;
84
  uint64_t m_singleChunk_offset=0;
85
86
  // For chunked data or non-constant sample sizes, we use a table with the offsets for all SAI samples.
87
  std::vector<uint64_t> m_sample_offsets;
88
};
89
90
91
/**
92
 * This structure specifies what will be written in a track and how it will be laid out in the file.
93
 */
94
struct TrackOptions
95
{
96
  ~TrackOptions()
97
324
  {
98
324
    heif_tai_clock_info_release(tai_clock_info);
99
324
  }
100
101
  // Timescale (clock ticks per second) for this track.
102
  uint32_t track_timescale = 90000;
103
104
  // If 'true', the aux_info data blocks will be interleaved with the compressed image.
105
  // This has the advantage that the aux_info is localized near the image data.
106
  //
107
  // If 'false', all aux_info will be written as one block after the compressed image data.
108
  // This has the advantage that no aux_info offsets have to be written.
109
  bool write_sample_aux_infos_interleaved = false;
110
111
112
  // --- TAI timestamps for samples
113
  heif_sample_aux_info_presence with_sample_tai_timestamps = heif_sample_aux_info_presence_none;
114
  heif_tai_clock_info* tai_clock_info = nullptr;
115
116
  // --- GIMI content IDs for samples
117
118
  heif_sample_aux_info_presence with_sample_content_ids = heif_sample_aux_info_presence_none;
119
120
  // --- GIMI content ID for the track
121
122
  std::string gimi_track_content_id;
123
124
  TrackOptions& operator=(const TrackOptions&);
125
};
126
127
128
const char* get_track_auxiliary_info_type(heif_compression_format format);
129
130
131
class Track : public ErrorBuffer {
132
public:
133
  //Track(HeifContext* ctx);
134
135
  Track(HeifContext* ctx, uint32_t track_id, const TrackOptions* info, uint32_t handler_type);
136
137
  Track(HeifContext* ctx);
138
139
324
  virtual ~Track() = default;
140
141
  // Allocate a Track of the correct sub-class (visual or metadata).
142
  // For tracks with an unsupported handler type, heif_error_Unsupported_feature/heif_suberror_Unsupported_track_type is returned.
143
  static Result<std::shared_ptr<Track>> alloc_track(HeifContext*, const std::shared_ptr<Box_trak>&);
144
145
  // load track from file
146
  virtual Error load(const std::shared_ptr<Box_trak>&);
147
148
  // This is called after creating all Track objects when reading a HEIF file.
149
  // We can now do initializations that require access to all tracks.
150
3
  [[nodiscard]] virtual Error initialize_after_parsing(HeifContext*, const std::vector<std::shared_ptr<Track>>& all_tracks) { return {}; }
151
152
105
  heif_item_id get_id() const { return m_id; }
153
154
  std::shared_ptr<HeifFile> get_file() const;
155
156
46
  uint32_t get_handler() const { return m_handler_type; }
157
158
  heif_auxiliary_track_info_type get_auxiliary_info_type() const;
159
160
0
  std::string get_auxiliary_info_type_urn() const { return m_auxiliary_info_type; }
161
162
  void set_auxiliary_info_type(heif_auxiliary_track_info_type);
163
164
0
  void set_auxiliary_info_type_urn(std::string t) { m_auxiliary_info_type = t; }
165
166
  bool is_visual_track() const;
167
168
0
  virtual bool has_alpha_channel() const { return false; }
169
170
  uint32_t get_first_cluster_sample_entry_type() const;
171
172
  Result<std::string> get_first_cluster_urim_uri() const;
173
174
  uint64_t get_duration_in_media_units() const;
175
176
  uint32_t get_timescale() const;
177
178
  // The context will compute the duration in global movie units and set this.
179
  void set_track_duration_in_movie_units(uint64_t total_duration, uint64_t segment_duration);
180
181
  void enable_edit_list_repeat_mode(bool enable);
182
183
0
  std::shared_ptr<Box_taic> get_first_cluster_taic() { return m_first_taic; }
184
185
  bool end_of_sequence_reached() const;
186
187
  // See m_num_repetitions for the meaning of the return value.
188
0
  uint32_t get_number_of_repetitions() const { return m_num_repetitions; }
189
190
  // Compute some parameters after all frames have been encoded (for example: track duration).
191
  virtual Error finalize_track();
192
193
0
  const TrackOptions& get_track_info() const { return m_track_info; }
194
195
  void add_reference_to_track(uint32_t referenceType, uint32_t to_track_id);
196
197
0
  std::shared_ptr<const Box_tref> get_tref_box() const { return m_tref; }
198
199
  Result<heif_raw_sequence_sample*> get_next_sample_raw_data(const heif_decoding_options* options);
200
201
  std::vector<heif_sample_aux_info_type> get_sample_aux_info_types() const;
202
203
protected:
204
  HeifContext* m_heif_context = nullptr;
205
  uint32_t m_id = 0;
206
  uint32_t m_handler_type = 0;
207
208
  TrackOptions m_track_info;
209
210
  uint32_t m_num_samples = 0;
211
212
  struct SampleTiming {
213
    uint32_t sampleIdx = 0;
214
    uint32_t sampleInChunkIdx = 0;
215
    uint32_t chunkIdx = 0;
216
    uint64_t presentation_time = 0; // TODO
217
    uint64_t media_composition_time = 0; // TODO
218
    uint64_t media_decoding_time = 0;
219
    uint32_t sample_duration_media_time = 0;
220
    uint32_t sample_duration_presentation_time = 0; // TODO
221
  };
222
  std::vector<SampleTiming> m_presentation_timeline;
223
  uint64_t m_num_output_samples = 0; // Can be larger than the vector. It then repeats the playback.
224
225
  // How many times the media timeline is repeated.
226
  // 0  = editlist is present but its pattern is not understood (caller should assume a single playback).
227
  // 1  = no editlist: media plays exactly once.
228
  // UINT32_MAX = infinite (mvhd duration is the indefinite-sentinel and the editlist is in repeat mode).
229
  // N  = the media segment is played N times.
230
  uint32_t m_num_repetitions = 1;
231
232
  // Continuous counting through all repetitions. You have to take the modulo operation to get the
233
  // index into m_presentation_timeline SampleTiming table.
234
  // (At 30 fps, this 32 bit integer will overflow in >4 years. I think this is acceptable.)
235
  uint32_t m_next_sample_to_be_decoded = 0;
236
237
  // Total sequence output index.
238
  uint32_t m_next_sample_to_be_output = 0;
239
  bool     m_decoder_is_flushed = false;
240
241
  Error init_sample_timing_table();
242
243
  std::vector<std::shared_ptr<Chunk>> m_chunks;
244
  std::vector<uint8_t> m_chunk_data;
245
246
  std::shared_ptr<Box_moov> m_moov;
247
  std::shared_ptr<Box_trak> m_trak;
248
  std::shared_ptr<Box_tkhd> m_tkhd;
249
  std::shared_ptr<Box_minf> m_minf;
250
  std::shared_ptr<Box_mdhd> m_mdhd;
251
  std::shared_ptr<Box_hdlr> m_hdlr;
252
  std::shared_ptr<Box_stbl> m_stbl;
253
  std::shared_ptr<Box_stsd> m_stsd;
254
  std::shared_ptr<Box_stsc> m_stsc;
255
  std::shared_ptr<Box_stco> m_stco;
256
  std::shared_ptr<Box_stts> m_stts;
257
  std::shared_ptr<Box_ctts> m_ctts; // optional box, TODO: add only if needed
258
  std::shared_ptr<Box_stss> m_stss;
259
  std::shared_ptr<Box_stsz> m_stsz;
260
  std::shared_ptr<Box_elst> m_elst;
261
262
  std::shared_ptr<class Box_tref> m_tref; // optional
263
264
  std::string m_auxiliary_info_type; // only for auxiliary tracks
265
266
  // --- sample auxiliary information
267
268
  std::unique_ptr<SampleAuxInfoHelper> m_aux_helper_tai_timestamps;
269
  std::unique_ptr<SampleAuxInfoHelper> m_aux_helper_content_ids;
270
271
  std::unique_ptr<SampleAuxInfoReader> m_aux_reader_tai_timestamps;
272
  std::unique_ptr<SampleAuxInfoReader> m_aux_reader_content_ids;
273
274
  std::shared_ptr<class Box_taic> m_first_taic; // the TAIC of the first chunk
275
276
277
  // --- Helper functions for writing samples.
278
279
  // Call when we begin a new chunk of samples, e.g. because the compression format changed
280
  void add_chunk(heif_compression_format format);
281
282
  // Call to set the sample_description_box for the last added chunk.
283
  // Has to be called when we call add_chunk().
284
  // It is not merged with add_chunk() because the sample_description_box may need information from the
285
  // first encoded frame.
286
  void set_sample_description_box(std::shared_ptr<Box> sample_description_box);
287
288
  // Write the actual sample data. `tai` may be null and `gimi_contentID` may be empty.
289
  // In these cases, no timestamp or no contentID will be written, respectively.
290
  Error write_sample_data(const std::vector<uint8_t>& raw_data,
291
                          uint32_t sample_duration,
292
                          int32_t composition_time_offset,
293
                          bool is_sync_sample,
294
                          const heif_tai_timestamp_packet* tai,
295
                          const std::optional<std::string>& gimi_contentID);
296
};
297
298
299
#endif //LIBHEIF_TRACK_H