/src/libheif/libheif/sequences/track.cc
Line | Count | Source (jump to first uncovered line) |
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<class 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<class Box>& parent, const std::shared_ptr<class 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<class 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, const std::shared_ptr<Box_trak>& trak_box) |
173 | 0 | { |
174 | 0 | m_heif_context = ctx; |
175 | |
|
176 | 0 | m_trak = trak_box; |
177 | |
|
178 | 0 | auto tkhd = trak_box->get_child_box<Box_tkhd>(); |
179 | 0 | if (!tkhd) { |
180 | 0 | return; // TODO: error or dummy error track ? |
181 | 0 | } |
182 | | |
183 | 0 | m_id = tkhd->get_track_id(); |
184 | |
|
185 | 0 | auto mdia = trak_box->get_child_box<Box_mdia>(); |
186 | 0 | if (!mdia) { |
187 | 0 | return; |
188 | 0 | } |
189 | | |
190 | 0 | m_tref = trak_box->get_child_box<Box_tref>(); |
191 | |
|
192 | 0 | auto hdlr = mdia->get_child_box<Box_hdlr>(); |
193 | 0 | if (!hdlr) { |
194 | 0 | return; |
195 | 0 | } |
196 | | |
197 | 0 | m_handler_type = hdlr->get_handler_type(); |
198 | |
|
199 | 0 | m_minf = mdia->get_child_box<Box_minf>(); |
200 | 0 | if (!m_minf) { |
201 | 0 | return; |
202 | 0 | } |
203 | | |
204 | 0 | m_mdhd = mdia->get_child_box<Box_mdhd>(); |
205 | 0 | if (!m_mdhd) { |
206 | 0 | return; |
207 | 0 | } |
208 | | |
209 | 0 | auto stbl = m_minf->get_child_box<Box_stbl>(); |
210 | 0 | if (!stbl) { |
211 | 0 | return; |
212 | 0 | } |
213 | | |
214 | 0 | m_stsd = stbl->get_child_box<Box_stsd>(); |
215 | 0 | if (!m_stsd) { |
216 | 0 | return; |
217 | 0 | } |
218 | | |
219 | 0 | m_stsc = stbl->get_child_box<Box_stsc>(); |
220 | 0 | if (!m_stsc) { |
221 | 0 | return; |
222 | 0 | } |
223 | | |
224 | 0 | m_stco = stbl->get_child_box<Box_stco>(); |
225 | 0 | if (!m_stco) { |
226 | 0 | return; |
227 | 0 | } |
228 | | |
229 | 0 | m_stsz = stbl->get_child_box<Box_stsz>(); |
230 | 0 | if (!m_stsz) { |
231 | 0 | return; |
232 | 0 | } |
233 | | |
234 | 0 | m_stts = stbl->get_child_box<Box_stts>(); |
235 | |
|
236 | 0 | const std::vector<uint32_t>& chunk_offsets = m_stco->get_offsets(); |
237 | 0 | assert(chunk_offsets.size() <= (size_t) std::numeric_limits<uint32_t>::max()); // There cannot be more than uint32_t chunks. |
238 | | |
239 | 0 | uint32_t current_sample_idx = 0; |
240 | |
|
241 | 0 | for (size_t chunk_idx = 0; chunk_idx < chunk_offsets.size(); chunk_idx++) { |
242 | 0 | auto* s2c = m_stsc->get_chunk(static_cast<uint32_t>(chunk_idx + 1)); |
243 | 0 | if (!s2c) { |
244 | 0 | return; |
245 | 0 | } |
246 | | |
247 | 0 | Box_stsc::SampleToChunk sampleToChunk = *s2c; |
248 | |
|
249 | 0 | auto sample_description = m_stsd->get_sample_entry(sampleToChunk.sample_description_index - 1); |
250 | 0 | if (!sample_description) { |
251 | 0 | return; |
252 | 0 | } |
253 | | |
254 | 0 | if (m_first_taic == nullptr) { |
255 | 0 | auto taic = sample_description->get_child_box<Box_taic>(); |
256 | 0 | if (taic) { |
257 | 0 | m_first_taic = taic; |
258 | 0 | } |
259 | 0 | } |
260 | |
|
261 | 0 | auto chunk = std::make_shared<Chunk>(ctx, m_id, sample_description, |
262 | 0 | current_sample_idx, sampleToChunk.samples_per_chunk, |
263 | 0 | m_stco->get_offsets()[chunk_idx], |
264 | 0 | m_stsz); |
265 | |
|
266 | 0 | m_chunks.push_back(chunk); |
267 | |
|
268 | 0 | current_sample_idx += sampleToChunk.samples_per_chunk; |
269 | 0 | } |
270 | | |
271 | | // --- read sample auxiliary information boxes |
272 | | |
273 | 0 | std::vector<std::shared_ptr<Box_saiz>> saiz_boxes = stbl->get_child_boxes<Box_saiz>(); |
274 | 0 | std::vector<std::shared_ptr<Box_saio>> saio_boxes = stbl->get_child_boxes<Box_saio>(); |
275 | |
|
276 | 0 | for (const auto& saiz : saiz_boxes) { |
277 | 0 | uint32_t aux_info_type = saiz->get_aux_info_type(); |
278 | 0 | uint32_t aux_info_type_parameter = saiz->get_aux_info_type_parameter(); |
279 | | |
280 | | // find the corresponding saio box |
281 | |
|
282 | 0 | std::shared_ptr<Box_saio> saio; |
283 | 0 | for (const auto& candidate : saio_boxes) { |
284 | 0 | if (candidate->get_aux_info_type() == aux_info_type && |
285 | 0 | candidate->get_aux_info_type_parameter() == aux_info_type_parameter) { |
286 | 0 | saio = candidate; |
287 | 0 | break; |
288 | 0 | } |
289 | 0 | } |
290 | |
|
291 | 0 | if (saio) { |
292 | 0 | if (aux_info_type == fourcc("suid")) { |
293 | 0 | m_aux_reader_content_ids = std::make_unique<SampleAuxInfoReader>(saiz, saio); |
294 | 0 | } |
295 | |
|
296 | 0 | if (aux_info_type == fourcc("stai")) { |
297 | 0 | m_aux_reader_tai_timestamps = std::make_unique<SampleAuxInfoReader>(saiz, saio); |
298 | 0 | } |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | | // --- read track properties |
303 | |
|
304 | 0 | if (auto meta = trak_box->get_child_box<Box_meta>()) { |
305 | 0 | auto iloc = meta->get_child_box<Box_iloc>(); |
306 | 0 | auto idat = meta->get_child_box<Box_idat>(); |
307 | |
|
308 | 0 | auto iinf = meta->get_child_box<Box_iinf>(); |
309 | 0 | if (iinf) { |
310 | 0 | auto infe_boxes = iinf->get_child_boxes<Box_infe>(); |
311 | 0 | for (const auto& box : infe_boxes) { |
312 | 0 | if (box->get_item_type_4cc() == fourcc("uri ") && |
313 | 0 | box->get_item_uri_type() == "urn:uuid:15beb8e4-944d-5fc6-a3dd-cb5a7e655c73") { |
314 | 0 | heif_item_id id = box->get_item_ID(); |
315 | |
|
316 | 0 | std::vector<uint8_t> data; |
317 | 0 | Error err = iloc->read_data(id, ctx->get_heif_file()->get_reader(), idat, &data, ctx->get_security_limits()); |
318 | 0 | if (err) { |
319 | | // TODO |
320 | 0 | } |
321 | |
|
322 | 0 | Result contentIdResult = vector_to_string(data); |
323 | 0 | if (contentIdResult.error) { |
324 | | // TODO |
325 | 0 | } |
326 | |
|
327 | 0 | m_track_info.gimi_track_content_id = contentIdResult.value; |
328 | 0 | } |
329 | 0 | } |
330 | 0 | } |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | |
335 | | Track::Track(HeifContext* ctx, uint32_t track_id, const TrackOptions* options, uint32_t handler_type) |
336 | 0 | { |
337 | 0 | m_heif_context = ctx; |
338 | |
|
339 | 0 | m_moov = ctx->get_heif_file()->get_moov_box(); |
340 | 0 | assert(m_moov); |
341 | | |
342 | | // --- find next free track ID |
343 | | |
344 | 0 | if (track_id == 0) { |
345 | 0 | track_id = 1; // minimum track ID |
346 | |
|
347 | 0 | for (const auto& track : m_moov->get_child_boxes<Box_trak>()) { |
348 | 0 | auto tkhd = track->get_child_box<Box_tkhd>(); |
349 | |
|
350 | 0 | if (tkhd->get_track_id() >= track_id) { |
351 | 0 | track_id = tkhd->get_track_id() + 1; |
352 | 0 | } |
353 | 0 | } |
354 | |
|
355 | 0 | auto mvhd = m_moov->get_child_box<Box_mvhd>(); |
356 | 0 | mvhd->set_next_track_id(track_id + 1); |
357 | |
|
358 | 0 | m_id = track_id; |
359 | 0 | } |
360 | |
|
361 | 0 | m_trak = std::make_shared<Box_trak>(); |
362 | 0 | m_moov->append_child_box(m_trak); |
363 | |
|
364 | 0 | m_tkhd = std::make_shared<Box_tkhd>(); |
365 | 0 | m_trak->append_child_box(m_tkhd); |
366 | 0 | m_tkhd->set_track_id(track_id); |
367 | |
|
368 | 0 | auto mdia = std::make_shared<Box_mdia>(); |
369 | 0 | m_trak->append_child_box(mdia); |
370 | |
|
371 | 0 | m_mdhd = std::make_shared<Box_mdhd>(); |
372 | 0 | m_mdhd->set_timescale(options ? options->track_timescale : 90000); |
373 | 0 | mdia->append_child_box(m_mdhd); |
374 | |
|
375 | 0 | m_hdlr = std::make_shared<Box_hdlr>(); |
376 | 0 | mdia->append_child_box(m_hdlr); |
377 | 0 | m_hdlr->set_handler_type(handler_type); |
378 | |
|
379 | 0 | m_minf = std::make_shared<Box_minf>(); |
380 | 0 | mdia->append_child_box(m_minf); |
381 | | |
382 | | // vmhd is added in Track_Visual |
383 | |
|
384 | 0 | m_stbl = std::make_shared<Box_stbl>(); |
385 | 0 | m_minf->append_child_box(m_stbl); |
386 | |
|
387 | 0 | m_stsd = std::make_shared<Box_stsd>(); |
388 | 0 | m_stbl->append_child_box(m_stsd); |
389 | |
|
390 | 0 | m_stts = std::make_shared<Box_stts>(); |
391 | 0 | m_stbl->append_child_box(m_stts); |
392 | |
|
393 | 0 | m_stsc = std::make_shared<Box_stsc>(); |
394 | 0 | m_stbl->append_child_box(m_stsc); |
395 | |
|
396 | 0 | m_stsz = std::make_shared<Box_stsz>(); |
397 | 0 | m_stbl->append_child_box(m_stsz); |
398 | |
|
399 | 0 | m_stco = std::make_shared<Box_stco>(); |
400 | 0 | m_stbl->append_child_box(m_stco); |
401 | |
|
402 | 0 | m_stss = std::make_shared<Box_stss>(); |
403 | 0 | m_stbl->append_child_box(m_stss); |
404 | |
|
405 | 0 | if (options) { |
406 | 0 | m_track_info = *options; |
407 | |
|
408 | 0 | if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) { |
409 | 0 | m_aux_helper_tai_timestamps = std::make_unique<SampleAuxInfoHelper>(m_track_info.write_sample_aux_infos_interleaved); |
410 | 0 | m_aux_helper_tai_timestamps->set_aux_info_type(fourcc("stai")); |
411 | 0 | } |
412 | |
|
413 | 0 | if (m_track_info.with_sample_content_ids != heif_sample_aux_info_presence_none) { |
414 | 0 | m_aux_helper_content_ids = std::make_unique<SampleAuxInfoHelper>(m_track_info.write_sample_aux_infos_interleaved); |
415 | 0 | m_aux_helper_content_ids->set_aux_info_type(fourcc("suid")); |
416 | 0 | } |
417 | |
|
418 | 0 | if (!options->gimi_track_content_id.empty()) { |
419 | 0 | auto hdlr_box = std::make_shared<Box_hdlr>(); |
420 | 0 | hdlr_box->set_handler_type(fourcc("meta")); |
421 | |
|
422 | 0 | auto uuid_box = std::make_shared<Box_infe>(); |
423 | 0 | uuid_box->set_item_type_4cc(fourcc("uri ")); |
424 | 0 | uuid_box->set_item_uri_type("urn:uuid:15beb8e4-944d-5fc6-a3dd-cb5a7e655c73"); |
425 | 0 | uuid_box->set_item_ID(1); |
426 | |
|
427 | 0 | auto iinf_box = std::make_shared<Box_iinf>(); |
428 | 0 | iinf_box->append_child_box(uuid_box); |
429 | |
|
430 | 0 | std::vector<uint8_t> track_uuid_vector; |
431 | 0 | track_uuid_vector.insert(track_uuid_vector.begin(), |
432 | 0 | options->gimi_track_content_id.c_str(), |
433 | 0 | options->gimi_track_content_id.c_str() + options->gimi_track_content_id.length() + 1); |
434 | |
|
435 | 0 | auto iloc_box = std::make_shared<Box_iloc>(); |
436 | 0 | iloc_box->append_data(1, track_uuid_vector, 1); |
437 | |
|
438 | 0 | auto meta_box = std::make_shared<Box_meta>(); |
439 | 0 | meta_box->append_child_box(hdlr_box); |
440 | 0 | meta_box->append_child_box(iinf_box); |
441 | 0 | meta_box->append_child_box(iloc_box); |
442 | |
|
443 | 0 | m_trak->append_child_box(meta_box); |
444 | 0 | } |
445 | 0 | } |
446 | 0 | } |
447 | | |
448 | | |
449 | | std::shared_ptr<Track> Track::alloc_track(HeifContext* ctx, const std::shared_ptr<Box_trak>& trak) |
450 | 0 | { |
451 | 0 | auto mdia = trak->get_child_box<Box_mdia>(); |
452 | 0 | if (!mdia) { |
453 | 0 | return nullptr; |
454 | 0 | } |
455 | | |
456 | 0 | auto hdlr = mdia->get_child_box<Box_hdlr>(); |
457 | 0 | if (!mdia) { |
458 | 0 | return nullptr; |
459 | 0 | } |
460 | | |
461 | 0 | switch (hdlr->get_handler_type()) { |
462 | 0 | case fourcc("pict"): |
463 | 0 | case fourcc("vide"): |
464 | 0 | return std::make_shared<Track_Visual>(ctx, trak); |
465 | 0 | case fourcc("meta"): |
466 | 0 | return std::make_shared<Track_Metadata>(ctx, trak); |
467 | 0 | default: |
468 | 0 | return nullptr; |
469 | 0 | } |
470 | 0 | } |
471 | | |
472 | | |
473 | | bool Track::is_visual_track() const |
474 | 0 | { |
475 | 0 | return m_handler_type == fourcc("pict"); |
476 | 0 | } |
477 | | |
478 | | |
479 | | uint32_t Track::get_first_cluster_sample_entry_type() const |
480 | 0 | { |
481 | 0 | if (m_stsd->get_num_sample_entries() == 0) { |
482 | 0 | return 0; // TODO: error ? Or can we assume at this point that there is at least one sample entry? |
483 | 0 | } |
484 | | |
485 | 0 | return m_stsd->get_sample_entry(0)->get_short_type(); |
486 | 0 | } |
487 | | |
488 | | |
489 | | Result<std::string> Track::get_first_cluster_urim_uri() const |
490 | 0 | { |
491 | 0 | if (m_stsd->get_num_sample_entries() == 0) { |
492 | 0 | return Error{heif_error_Invalid_input, |
493 | 0 | heif_suberror_Unspecified, |
494 | 0 | "This track has no sample entries."}; |
495 | 0 | } |
496 | | |
497 | 0 | std::shared_ptr<const Box> sampleEntry = m_stsd->get_sample_entry(0); |
498 | 0 | auto urim = std::dynamic_pointer_cast<const Box_URIMetaSampleEntry>(sampleEntry); |
499 | 0 | if (!urim) { |
500 | 0 | return Error{heif_error_Usage_error, |
501 | 0 | heif_suberror_Unspecified, |
502 | 0 | "This cluster is no 'urim' sample entry."}; |
503 | 0 | } |
504 | | |
505 | 0 | std::shared_ptr<const Box_uri> uri = urim->get_child_box<const Box_uri>(); |
506 | 0 | if (!uri) { |
507 | 0 | return Error{heif_error_Invalid_input, |
508 | 0 | heif_suberror_Unspecified, |
509 | 0 | "The 'urim' box has no 'uri' child box."}; |
510 | 0 | } |
511 | | |
512 | 0 | return uri->get_uri(); |
513 | 0 | } |
514 | | |
515 | | |
516 | | bool Track::end_of_sequence_reached() const |
517 | 0 | { |
518 | 0 | return (m_next_sample_to_be_processed > m_chunks.back()->last_sample_number()); |
519 | 0 | } |
520 | | |
521 | | |
522 | | void Track::finalize_track() |
523 | 0 | { |
524 | 0 | if (m_aux_helper_tai_timestamps) m_aux_helper_tai_timestamps->write_all(m_stbl, get_file()); |
525 | 0 | if (m_aux_helper_content_ids) m_aux_helper_content_ids->write_all(m_stbl, get_file()); |
526 | |
|
527 | 0 | uint64_t duration = m_stts->get_total_duration(false); |
528 | 0 | m_mdhd->set_duration(duration); |
529 | 0 | } |
530 | | |
531 | | |
532 | | uint64_t Track::get_duration_in_media_units() const |
533 | 0 | { |
534 | 0 | return m_mdhd->get_duration(); |
535 | 0 | } |
536 | | |
537 | | |
538 | | uint32_t Track::get_timescale() const |
539 | 0 | { |
540 | 0 | return m_mdhd->get_timescale(); |
541 | 0 | } |
542 | | |
543 | | |
544 | | void Track::set_track_duration_in_movie_units(uint64_t total_duration) |
545 | 0 | { |
546 | 0 | m_tkhd->set_duration(total_duration); |
547 | 0 | } |
548 | | |
549 | | |
550 | | void Track::add_chunk(heif_compression_format format) |
551 | 0 | { |
552 | 0 | auto chunk = std::make_shared<Chunk>(m_heif_context, m_id, format); |
553 | 0 | m_chunks.push_back(chunk); |
554 | |
|
555 | 0 | int chunkIdx = (uint32_t) m_chunks.size(); |
556 | 0 | m_stsc->add_chunk(chunkIdx); |
557 | 0 | } |
558 | | |
559 | | void Track::set_sample_description_box(std::shared_ptr<Box> sample_description_box) |
560 | 0 | { |
561 | | // --- add 'taic' when we store timestamps as sample auxiliary information |
562 | |
|
563 | 0 | if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) { |
564 | 0 | auto taic = std::make_shared<Box_taic>(); |
565 | 0 | taic->set_from_tai_clock_info(m_track_info.tai_clock_info); |
566 | 0 | sample_description_box->append_child_box(taic); |
567 | 0 | } |
568 | |
|
569 | 0 | m_stsd->add_sample_entry(sample_description_box); |
570 | 0 | } |
571 | | |
572 | | |
573 | | Error Track::write_sample_data(const std::vector<uint8_t>& raw_data, uint32_t sample_duration, bool is_sync_sample, |
574 | | const heif_tai_timestamp_packet* tai, const std::string& gimi_contentID) |
575 | 0 | { |
576 | 0 | size_t data_start = m_heif_context->get_heif_file()->append_mdat_data(raw_data); |
577 | | |
578 | | // first sample in chunk? -> write chunk offset |
579 | |
|
580 | 0 | if (m_stsc->last_chunk_empty()) { |
581 | | // if auxiliary data is interleaved, write it between the chunks |
582 | 0 | if (m_aux_helper_tai_timestamps) m_aux_helper_tai_timestamps->write_interleaved(get_file()); |
583 | 0 | if (m_aux_helper_content_ids) m_aux_helper_content_ids->write_interleaved(get_file()); |
584 | | |
585 | | // TODO |
586 | 0 | assert(data_start < 0xFF000000); // add some headroom for header data |
587 | 0 | m_stco->add_chunk_offset(static_cast<uint32_t>(data_start)); |
588 | 0 | } |
589 | | |
590 | 0 | m_stsc->increase_samples_in_chunk(1); |
591 | |
|
592 | 0 | m_stsz->append_sample_size((uint32_t)raw_data.size()); |
593 | |
|
594 | 0 | if (is_sync_sample) { |
595 | 0 | m_stss->add_sync_sample(m_next_sample_to_be_processed + 1); |
596 | 0 | } |
597 | |
|
598 | 0 | if (sample_duration == 0) { |
599 | 0 | return {heif_error_Usage_error, |
600 | 0 | heif_suberror_Unspecified, |
601 | 0 | "Sample duration may not be 0"}; |
602 | 0 | } |
603 | | |
604 | 0 | m_stts->append_sample_duration(sample_duration); |
605 | | |
606 | | |
607 | | // --- sample timestamp |
608 | |
|
609 | 0 | if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) { |
610 | 0 | if (tai) { |
611 | 0 | std::vector<uint8_t> tai_data = Box_itai::encode_tai_to_bitstream(tai); |
612 | 0 | auto err = m_aux_helper_tai_timestamps->add_sample_info(tai_data); |
613 | 0 | if (err) { |
614 | 0 | return err; |
615 | 0 | } |
616 | 0 | } |
617 | 0 | else if (m_track_info.with_sample_tai_timestamps == heif_sample_aux_info_presence_optional) { |
618 | 0 | m_aux_helper_tai_timestamps->add_nonpresent_sample(); |
619 | 0 | } |
620 | 0 | else { |
621 | 0 | return {heif_error_Encoding_error, |
622 | 0 | heif_suberror_Unspecified, |
623 | 0 | "Mandatory TAI timestamp missing"}; |
624 | 0 | } |
625 | 0 | } |
626 | | |
627 | | // --- sample content id |
628 | | |
629 | 0 | if (m_track_info.with_sample_content_ids != heif_sample_aux_info_presence_none) { |
630 | 0 | if (!gimi_contentID.empty()) { |
631 | 0 | auto id = gimi_contentID; |
632 | 0 | const char* id_str = id.c_str(); |
633 | 0 | std::vector<uint8_t> id_vector; |
634 | 0 | id_vector.insert(id_vector.begin(), id_str, id_str + id.length() + 1); |
635 | 0 | auto err = m_aux_helper_content_ids->add_sample_info(id_vector); |
636 | 0 | if (err) { |
637 | 0 | return err; |
638 | 0 | } |
639 | 0 | } else if (m_track_info.with_sample_content_ids == heif_sample_aux_info_presence_optional) { |
640 | 0 | m_aux_helper_content_ids->add_nonpresent_sample(); |
641 | 0 | } else { |
642 | 0 | return {heif_error_Encoding_error, |
643 | 0 | heif_suberror_Unspecified, |
644 | 0 | "Mandatory ContentID missing"}; |
645 | 0 | } |
646 | 0 | } |
647 | | |
648 | 0 | m_next_sample_to_be_processed++; |
649 | |
|
650 | 0 | return Error::Ok; |
651 | 0 | } |
652 | | |
653 | | |
654 | | void Track::add_reference_to_track(uint32_t referenceType, uint32_t to_track_id) |
655 | 0 | { |
656 | 0 | if (!m_tref) { |
657 | 0 | m_tref = std::make_shared<Box_tref>(); |
658 | 0 | m_trak->append_child_box(m_tref); |
659 | 0 | } |
660 | |
|
661 | 0 | m_tref->add_references(to_track_id, referenceType); |
662 | 0 | } |
663 | | |
664 | | |
665 | | Result<heif_raw_sequence_sample*> Track::get_next_sample_raw_data() |
666 | 0 | { |
667 | 0 | if (m_current_chunk > m_chunks.size()) { |
668 | 0 | return Error{heif_error_End_of_sequence, |
669 | 0 | heif_suberror_Unspecified, |
670 | 0 | "End of sequence"}; |
671 | 0 | } |
672 | | |
673 | 0 | while (m_next_sample_to_be_processed > m_chunks[m_current_chunk]->last_sample_number()) { |
674 | 0 | m_current_chunk++; |
675 | |
|
676 | 0 | if (m_current_chunk > m_chunks.size()) { |
677 | 0 | return Error{heif_error_End_of_sequence, |
678 | 0 | heif_suberror_Unspecified, |
679 | 0 | "End of sequence"}; |
680 | 0 | } |
681 | 0 | } |
682 | | |
683 | 0 | const std::shared_ptr<Chunk>& chunk = m_chunks[m_current_chunk]; |
684 | |
|
685 | 0 | DataExtent extent = chunk->get_data_extent_for_sample(m_next_sample_to_be_processed); |
686 | 0 | auto readResult = extent.read_data(); |
687 | 0 | if (readResult.error) { |
688 | 0 | return readResult.error; |
689 | 0 | } |
690 | | |
691 | 0 | heif_raw_sequence_sample* sample = new heif_raw_sequence_sample(); |
692 | 0 | sample->data = *readResult.value; |
693 | | |
694 | | // read sample duration |
695 | |
|
696 | 0 | if (m_stts) { |
697 | 0 | sample->duration = m_stts->get_sample_duration(m_next_sample_to_be_processed); |
698 | 0 | } |
699 | | |
700 | | // --- read sample auxiliary data |
701 | |
|
702 | 0 | if (m_aux_reader_content_ids) { |
703 | 0 | auto readResult = m_aux_reader_content_ids->get_sample_info(get_file().get(), m_next_sample_to_be_processed); |
704 | 0 | if (readResult.error) { |
705 | 0 | return readResult.error; |
706 | 0 | } |
707 | | |
708 | 0 | if (!readResult.value.empty()) { |
709 | 0 | Result<std::string> convResult = vector_to_string(readResult.value); |
710 | 0 | if (convResult.error) { |
711 | 0 | return convResult.error; |
712 | 0 | } |
713 | | |
714 | 0 | sample->gimi_sample_content_id = convResult.value; |
715 | 0 | } |
716 | 0 | } |
717 | | |
718 | 0 | if (m_aux_reader_tai_timestamps) { |
719 | 0 | auto readResult = m_aux_reader_tai_timestamps->get_sample_info(get_file().get(), m_next_sample_to_be_processed); |
720 | 0 | if (readResult.error) { |
721 | 0 | return readResult.error; |
722 | 0 | } |
723 | | |
724 | 0 | if (!readResult.value.empty()) { |
725 | 0 | auto resultTai = Box_itai::decode_tai_from_vector(readResult.value); |
726 | 0 | if (resultTai.error) { |
727 | 0 | return resultTai.error; |
728 | 0 | } |
729 | | |
730 | 0 | sample->timestamp = heif_tai_timestamp_packet_alloc(); |
731 | 0 | heif_tai_timestamp_packet_copy(sample->timestamp, &resultTai.value); |
732 | 0 | } |
733 | 0 | } |
734 | | |
735 | 0 | m_next_sample_to_be_processed++; |
736 | |
|
737 | 0 | return sample; |
738 | 0 | } |
739 | | |
740 | | |
741 | | std::vector<heif_sample_aux_info_type> Track::get_sample_aux_info_types() const |
742 | 0 | { |
743 | 0 | std::vector<heif_sample_aux_info_type> types; |
744 | |
|
745 | 0 | if (m_aux_reader_tai_timestamps) types.emplace_back(m_aux_reader_tai_timestamps->get_type()); |
746 | 0 | if (m_aux_reader_content_ids) types.emplace_back(m_aux_reader_content_ids->get_type()); |
747 | |
|
748 | 0 | return types; |
749 | 0 | } |