2 * APV helper functions for muxers
3 * Copyright (c) 2025 Dawid Kozinski <d.kozinski@samsung.com>
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg 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 GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "libavutil/avassert.h"
23 #include "libavutil/intreadwrite.h"
24 #include "libavutil/mem.h"
30 #include "avio_internal.h"
31 #include "libavcodec/cbs_apv.h"
32 #include "libavcodec/packet.h"
34 typedef struct APVDecoderFrameInfo
{
35 uint8_t color_description_present_flag
; // 1 bit
37 // The variable indicates whether the capture_time_distance value in the APV bitstream's frame header should be ignored during playback.
38 // If capture_time_distance_ignored is set to true, the capture_time_distance information will not be utilized,
39 // and timing information for playback should be calculated using an alternative method.
40 // If set to false, the capture_time_distance value will be used as is from the frame header.
41 // It is recommended to set this variable to true, allowing the use of MP4 timestamps for playback and recording,
42 // which enables the conventional compression and playback methods based on the timestamp table defined by the ISO-based file format.
43 uint8_t capture_time_distance_ignored
; // 1-bit
45 uint8_t profile_idc
; // 8 bits
46 uint8_t level_idc
; // 8 bits
47 uint8_t band_idc
; // 8 bits
48 uint32_t frame_width
; // 32 bits
49 uint32_t frame_height
; // 32 bits
50 uint8_t chroma_format_idc
; // 4 bits
51 uint8_t bit_depth_minus8
; // 4 bits
52 uint8_t capture_time_distance
; // 8 bits
54 // if (color_description_present_flag)
55 uint8_t color_primaries
; // 8 bits
56 uint8_t transfer_characteristics
; // 8 bits
57 uint8_t matrix_coefficients
; // 8 bits
58 uint8_t full_range_flag
; // 1 bit
59 } APVDecoderFrameInfo
;
61 typedef struct APVDecoderConfigurationEntry
{
62 uint8_t pbu_type
; // 8 bits
63 uint8_t number_of_frame_info
; // 8 bits
65 APVDecoderFrameInfo
*frame_info
; // An array of size number_of_frame_info storing elements of type APVDecoderFrameInfo*
66 } APVDecoderConfigurationEntry
;
68 // ISOBMFF binding for APV
69 // @see https://github.com/openapv/openapv/blob/main/readme/apv_isobmff.md
70 typedef struct APVDecoderConfigurationRecord
{
71 uint8_t configurationVersion
; // 8 bits
72 uint8_t number_of_configuration_entry
; // 8 bits
74 APVDecoderConfigurationEntry
*configuration_entry
; // table of size number_of_configuration_entry
76 CodedBitstreamContext
*cbc
;
77 CodedBitstreamFragment frag
;
78 } APVDecoderConfigurationRecord
;
80 void ff_isom_write_apvc(AVIOContext
*pb
, const APVDecoderConfigurationRecord
*apvc
, void *logctx
)
82 av_log(logctx
, AV_LOG_TRACE
, "configurationVersion: %"PRIu8
"\n",
83 apvc
->configurationVersion
);
85 av_log(logctx
, AV_LOG_TRACE
, "number_of_configuration_entry: %"PRIu8
"\n",
86 apvc
->number_of_configuration_entry
);
88 for (int i
= 0; i
< apvc
->number_of_configuration_entry
; i
++) {
89 const APVDecoderConfigurationEntry
*configuration_entry
= &apvc
->configuration_entry
[i
];
91 av_log(logctx
, AV_LOG_TRACE
, "pbu_type: %"PRIu8
"\n",
92 configuration_entry
->pbu_type
);
94 av_log(logctx
, AV_LOG_TRACE
, "number_of_frame_info: %"PRIu8
"\n",
95 configuration_entry
->number_of_frame_info
);
97 for (int j
= 0; j
< configuration_entry
->number_of_frame_info
; j
++) {
98 const APVDecoderFrameInfo
*frame_info
= &configuration_entry
->frame_info
[j
];
100 av_log(logctx
, AV_LOG_TRACE
, "color_description_present_flag: %"PRIu8
"\n",
101 frame_info
->color_description_present_flag
);
103 av_log(logctx
, AV_LOG_TRACE
, "capture_time_distance_ignored: %"PRIu8
"\n",
104 frame_info
->capture_time_distance_ignored
);
106 av_log(logctx
, AV_LOG_TRACE
, "profile_idc: %"PRIu8
"\n",
107 frame_info
->profile_idc
);
109 av_log(logctx
, AV_LOG_TRACE
, "level_idc: %"PRIu8
"\n",
110 frame_info
->level_idc
);
112 av_log(logctx
, AV_LOG_TRACE
, "band_idc: %"PRIu8
"\n",
113 frame_info
->band_idc
);
115 av_log(logctx
, AV_LOG_TRACE
, "frame_width: %"PRIu32
"\n",
116 frame_info
->frame_width
);
118 av_log(logctx
, AV_LOG_TRACE
, "frame_height: %"PRIu32
"\n",
119 frame_info
->frame_height
);
121 av_log(logctx
, AV_LOG_TRACE
, "chroma_format_idc: %"PRIu8
"\n",
122 frame_info
->chroma_format_idc
);
124 av_log(logctx
, AV_LOG_TRACE
, "bit_depth_minus8: %"PRIu8
"\n",
125 frame_info
->bit_depth_minus8
);
127 av_log(logctx
, AV_LOG_TRACE
, "capture_time_distance: %"PRIu8
"\n",
128 frame_info
->capture_time_distance
);
130 if (frame_info
->color_description_present_flag
) {
131 av_log(logctx
, AV_LOG_TRACE
, "color_primaries: %"PRIu8
"\n",
132 frame_info
->color_primaries
);
134 av_log(logctx
, AV_LOG_TRACE
, "transfer_characteristics: %"PRIu8
"\n",
135 frame_info
->transfer_characteristics
);
137 av_log(logctx
, AV_LOG_TRACE
, "matrix_coefficients: %"PRIu8
"\n",
138 frame_info
->matrix_coefficients
);
140 av_log(logctx
, AV_LOG_TRACE
, "full_range_flag: %"PRIu8
"\n",
141 frame_info
->full_range_flag
);
146 /* unsigned int(8) configurationVersion = 1; */
147 avio_w8(pb
, apvc
->configurationVersion
);
149 avio_w8(pb
, apvc
->number_of_configuration_entry
);
151 for (int i
= 0; i
< apvc
->number_of_configuration_entry
; i
++) {
152 const APVDecoderConfigurationEntry
*configuration_entry
= &apvc
->configuration_entry
[i
];
154 avio_w8(pb
, configuration_entry
->pbu_type
);
155 avio_w8(pb
, configuration_entry
->number_of_frame_info
);
157 for (int j
= 0; j
< configuration_entry
->number_of_frame_info
; j
++) {
158 const APVDecoderFrameInfo
*frame_info
= &configuration_entry
->frame_info
[j
];
160 /* reserved_zero_6bits
161 * unsigned int(1) color_description_present_flag
162 * unsigned int(1) capture_time_distance_ignored */
163 avio_w8(pb
, frame_info
->color_description_present_flag
<< 1 |
164 frame_info
->capture_time_distance_ignored
);
166 /* unsigned int(8) profile_idc */
167 avio_w8(pb
, frame_info
->profile_idc
);
169 /* unsigned int(8) level_idc */
170 avio_w8(pb
, frame_info
->level_idc
);
172 /* unsigned int(8) band_idc */
173 avio_w8(pb
, frame_info
->band_idc
);
175 /* unsigned int(32) frame_width_minus1 */
176 avio_wb32(pb
, frame_info
->frame_width
);
178 /* unsigned int(32) frame_height_minus1 */
179 avio_wb32(pb
, frame_info
->frame_height
);
181 /* unsigned int(4) chroma_format_idc */
182 /* unsigned int(4) bit_depth_minus8 */
183 avio_w8(pb
, (frame_info
->chroma_format_idc
<< 4) |
184 frame_info
->bit_depth_minus8
);
186 /* unsigned int(8) capture_time_distance */
187 avio_w8(pb
, frame_info
->capture_time_distance
);
189 if (frame_info
->color_description_present_flag
) {
190 /* unsigned int(8) color_primaries */
191 avio_w8(pb
, frame_info
->color_primaries
);
193 /* unsigned int(8) transfer_characteristics */
194 avio_w8(pb
, frame_info
->transfer_characteristics
);
196 /* unsigned int(8) matrix_coefficients */
197 avio_w8(pb
, frame_info
->matrix_coefficients
);
199 /* unsigned int(1) full_range_flag
200 * reserved_zero_7bits */
201 avio_w8(pb
, frame_info
->full_range_flag
<< 7);
207 static const CodedBitstreamUnitType decompose_unit_types
[] = {
208 APV_PBU_PRIMARY_FRAME
, APV_PBU_NON_PRIMARY_FRAME
,
209 APV_PBU_PREVIEW_FRAME
, APV_PBU_DEPTH_FRAME
, APV_PBU_ALPHA_FRAME
212 static int apv_add_configuration_entry(APVDecoderConfigurationRecord
*apvc
, int pbu_type
)
214 APVDecoderConfigurationEntry
*temp
;
216 av_assert0(apvc
->number_of_configuration_entry
< FF_ARRAY_ELEMS(decompose_unit_types
));
217 temp
= av_realloc_array(apvc
->configuration_entry
,
218 apvc
->number_of_configuration_entry
+ 1, sizeof(*apvc
->configuration_entry
));
221 return AVERROR(ENOMEM
);
223 apvc
->configuration_entry
= temp
;
224 memset(&apvc
->configuration_entry
[apvc
->number_of_configuration_entry
], 0, sizeof(*apvc
->configuration_entry
));
225 apvc
->configuration_entry
[apvc
->number_of_configuration_entry
].pbu_type
= pbu_type
;
226 apvc
->number_of_configuration_entry
++;
231 static int apv_add_frameinfo(APVDecoderConfigurationEntry
*configuration_entry
,
232 const APVDecoderFrameInfo
*frame_info
)
234 APVDecoderFrameInfo
*temp
;
236 if (configuration_entry
->number_of_frame_info
>= UINT8_MAX
)
237 return AVERROR(EINVAL
);
239 temp
= av_realloc_array(configuration_entry
->frame_info
,
240 configuration_entry
->number_of_frame_info
+ 1, sizeof(*configuration_entry
->frame_info
));
243 return AVERROR(ENOMEM
);
245 configuration_entry
->frame_info
= temp
;
246 memcpy(&configuration_entry
->frame_info
[configuration_entry
->number_of_frame_info
], frame_info
, sizeof(*frame_info
));
247 configuration_entry
->number_of_frame_info
++;
252 int ff_isom_parse_apvc(APVDecoderConfigurationRecord
*apvc
,
253 const AVPacket
*pkt
, void *logctx
)
255 APVDecoderFrameInfo frame_info
;
258 if (pkt
->size
< 8 || AV_RB32(pkt
->data
) != APV_SIGNATURE
)
259 /* We can't write a valid apvC from the provided data */
260 return AVERROR_INVALIDDATA
;
262 ret
= ff_lavf_cbs_read(apvc
->cbc
, &apvc
->frag
, pkt
->buf
, pkt
->data
, pkt
->size
);
264 av_log(logctx
, AV_LOG_ERROR
, "Failed to parse access unit.\n");
268 memset(&frame_info
, 0, sizeof(frame_info
));
269 frame_info
.capture_time_distance_ignored
= 1;
271 for (int i
= 0; i
< apvc
->frag
.nb_units
; i
++) {
272 const CodedBitstreamUnit
*pbu
= &apvc
->frag
.units
[i
];
276 case APV_PBU_PRIMARY_FRAME
:
277 case APV_PBU_NON_PRIMARY_FRAME
:
278 case APV_PBU_PREVIEW_FRAME
:
279 case APV_PBU_DEPTH_FRAME
:
280 case APV_PBU_ALPHA_FRAME
:
286 const APVRawFrame
*frame
= pbu
->content
;
287 const APVRawFrameHeader
*header
= &frame
->frame_header
;
288 const APVRawFrameInfo
*info
= &header
->frame_info
;
289 int bit_depth
= info
->bit_depth_minus8
+ 8;
291 if (bit_depth
< 8 || bit_depth
> 16 || bit_depth
% 2)
294 frame_info
.profile_idc
= info
->profile_idc
;
295 frame_info
.level_idc
= info
->level_idc
;
296 frame_info
.band_idc
= info
->band_idc
;
298 frame_info
.frame_width
= info
->frame_width
;
299 frame_info
.frame_height
=info
->frame_height
;
300 frame_info
.chroma_format_idc
= info
->chroma_format_idc
;
301 frame_info
.bit_depth_minus8
= info
->bit_depth_minus8
;
302 frame_info
.capture_time_distance
= info
->capture_time_distance
;
304 frame_info
.color_description_present_flag
= header
->color_description_present_flag
;
305 if (frame_info
.color_description_present_flag
) {
306 frame_info
.color_primaries
= header
->color_primaries
;
307 frame_info
.transfer_characteristics
= header
->transfer_characteristics
;
308 frame_info
.matrix_coefficients
= header
->matrix_coefficients
;
309 frame_info
.full_range_flag
= header
->full_range_flag
;
311 frame_info
.color_primaries
=
312 frame_info
.transfer_characteristics
=
313 frame_info
.matrix_coefficients
=
314 frame_info
.full_range_flag
= 0;
317 for (j
= 0; j
< apvc
->number_of_configuration_entry
; j
++) {
320 if (apvc
->configuration_entry
[j
].pbu_type
!= pbu
->type
)
323 for (k
= 0; k
< apvc
->configuration_entry
[j
].number_of_frame_info
; k
++) {
324 if (!memcmp(&apvc
->configuration_entry
[j
].frame_info
[k
], &frame_info
, sizeof(frame_info
)))
327 if (k
== apvc
->configuration_entry
[j
].number_of_frame_info
) {
328 ret
= apv_add_frameinfo(&apvc
->configuration_entry
[j
], &frame_info
);
335 if (j
== apvc
->number_of_configuration_entry
) {
336 ret
= apv_add_configuration_entry(apvc
, pbu
->type
);
339 ret
= apv_add_frameinfo(&apvc
->configuration_entry
[j
], &frame_info
);
347 ff_lavf_cbs_fragment_reset(&apvc
->frag
);
352 int ff_isom_init_apvc(APVDecoderConfigurationRecord
**papvc
, void *logctx
)
354 APVDecoderConfigurationRecord
*apvc
= av_mallocz(sizeof(*apvc
));
357 return AVERROR(ENOMEM
);
359 int ret
= ff_lavf_cbs_init(&apvc
->cbc
, AV_CODEC_ID_APV
, logctx
);
365 apvc
->cbc
->decompose_unit_types
= decompose_unit_types
;
366 apvc
->cbc
->nb_decompose_unit_types
= FF_ARRAY_ELEMS(decompose_unit_types
);
368 apvc
->configurationVersion
= 1;
375 void ff_isom_close_apvc(APVDecoderConfigurationRecord
**papvc
)
377 APVDecoderConfigurationRecord
*apvc
= *papvc
;
382 for (int i
= 0; i
< apvc
->number_of_configuration_entry
; i
++)
383 av_freep(&apvc
->configuration_entry
[i
].frame_info
);
384 av_freep(&apvc
->configuration_entry
);
386 ff_lavf_cbs_fragment_free(&apvc
->frag
);
387 ff_lavf_cbs_close(&apvc
->cbc
);