1 /*****************************************************************************
2 * heif.c : ISO/IEC 23008-12 HEIF still picture demuxer
3 *****************************************************************************
4 * Copyright (C) 2018 Videolabs, VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <vlc_common.h>
28 #include <vlc_demux.h>
29 #include <vlc_input.h>
30 #include <vlc_image.h>
36 #include "../../packetizer/iso_color_tables.h"
43 vlc_tick_t i_end_display_time
;
44 vlc_tick_t i_image_duration
;
45 bool b_seekpoint_changed
;
47 input_title_t
*p_title
;
53 const MP4_Box_t
*p_shared_header
;
57 static MP4_Box_t
* NextAtom( MP4_Box_t
*p_root
,
58 vlc_fourcc_t i_type
, const char *psz_path
,
62 p_infe
= MP4_BoxGet( p_root
, psz_path
);
64 p_infe
= p_infe
->p_next
;
65 for( ; p_infe
; p_infe
= p_infe
->p_next
)
67 if( p_infe
->i_type
== i_type
)
73 static MP4_Box_t
* GetAtom( MP4_Box_t
*p_root
, MP4_Box_t
*p_atom
,
74 vlc_fourcc_t i_type
, const char *psz_path
,
75 bool(*pf_match
)(const MP4_Box_t
*, void *),
78 while( (p_atom
= NextAtom( p_root
, i_type
, psz_path
, p_atom
)) )
80 if( pf_match( p_atom
, priv
) )
86 static bool MatchInfeID( const MP4_Box_t
*p_infe
, void *priv
)
88 return BOXDATA(p_infe
)->i_item_id
== *((uint32_t *) priv
);
91 static bool MatchPureImage( const MP4_Box_t
*p_infe
, void *priv
)
93 MP4_Box_t
*p_root
= priv
;
94 const MP4_Box_t
*p_iref
= MP4_BoxGet( p_root
, "meta/iref" );
97 for( const MP4_Box_t
*p_refbox
= p_iref
->p_first
;
98 p_refbox
; p_refbox
= p_refbox
->p_next
)
100 if( BOXDATA(p_refbox
)->i_from_item_id
== BOXDATA(p_infe
)->i_item_id
)
106 static void SeekToPrevImageEnd( struct heif_private_t
*p_sys
, int i_picture
)
109 MP4_Box_t
*p_infe
= NULL
;
110 while( i
< i_picture
&&
111 (p_infe
= NextAtom( p_sys
->p_root
, ATOM_infe
, "meta/iinf/infe", p_infe
)) )
113 if( (BOXDATA(p_infe
)->i_flags
& 0x01) != 0x00 ||
114 !MatchPureImage( p_infe
, p_sys
->p_root
) )
118 p_sys
->current
.p_infe
= p_infe
;
119 p_sys
->i_end_display_time
= 0;
120 p_sys
->i_pcr
= i
* p_sys
->i_image_duration
;
123 static int ControlHEIF( demux_t
*p_demux
, int i_query
, va_list args
)
125 struct heif_private_t
*p_sys
= (void *) p_demux
->p_sys
;
130 *va_arg(args
, bool *) = true;
132 case DEMUX_GET_TITLE_INFO
:
134 input_title_t
***ppp_title
= va_arg( args
, input_title_t
*** );
135 int *pi_int
= va_arg( args
, int* );
136 int *pi_title_offset
= va_arg( args
, int* );
137 int *pi_seekpoint_offset
= va_arg( args
, int* );
139 if( !p_sys
->p_title
)
143 *ppp_title
= malloc( sizeof( input_title_t
*) );
144 (*ppp_title
)[0] = vlc_input_title_Duplicate( p_sys
->p_title
);
145 *pi_title_offset
= 0;
146 *pi_seekpoint_offset
= 0;
149 case DEMUX_SET_TITLE
:
151 const int i_title
= va_arg( args
, int );
152 if( !p_sys
->p_title
|| i_title
!= 0 )
156 case DEMUX_GET_SEEKPOINT
:
157 *va_arg( args
, int * ) = p_sys
->i_seekpoint
;
159 case DEMUX_SET_SEEKPOINT
:
161 const int i_seekpoint
= va_arg( args
, int );
162 if( !p_sys
->p_title
)
164 SeekToPrevImageEnd( p_sys
, i_seekpoint
);
167 case DEMUX_TEST_AND_CLEAR_FLAGS
:
169 unsigned *restrict flags
= va_arg( args
, unsigned * );
171 if ((*flags
& INPUT_UPDATE_SEEKPOINT
) && p_sys
->b_seekpoint_changed
)
173 *flags
= INPUT_UPDATE_SEEKPOINT
;
174 p_sys
->b_seekpoint_changed
= false;
180 case DEMUX_GET_LENGTH
:
181 *(va_arg( args
, vlc_tick_t
* )) = p_sys
->p_title
->i_seekpoint
*
182 p_sys
->i_image_duration
;
185 *(va_arg(args
, vlc_tick_t
*)) = p_sys
->i_pcr
;
189 SeekToPrevImageEnd( p_sys
, va_arg(args
, vlc_tick_t
) /
190 p_sys
->i_image_duration
);
193 case DEMUX_GET_POSITION
:
194 if( !p_sys
->p_title
->i_seekpoint
)
196 *(va_arg(args
, double *)) = (double) p_sys
->i_pcr
/
197 (p_sys
->p_title
->i_seekpoint
* p_sys
->i_image_duration
);
199 case DEMUX_SET_POSITION
:
201 SeekToPrevImageEnd( p_sys
, va_arg(args
, double) * p_sys
->p_title
->i_seekpoint
);
204 case DEMUX_CAN_PAUSE
:
205 case DEMUX_SET_PAUSE_STATE
:
206 case DEMUX_CAN_CONTROL_PACE
:
207 case DEMUX_GET_PTS_DELAY
:
208 return demux_vaControlHelper( p_demux
->s
, 0, -1, 0, 1, i_query
, args
);
216 //static int DemuxCompositeImage( demux_t *p_demux )
221 static block_t
*ReadItemExtents( demux_t
*p_demux
, uint32_t i_item_id
,
222 const MP4_Box_t
*p_shared_header
)
224 struct heif_private_t
*p_sys
= (void *) p_demux
->p_sys
;
225 block_t
*p_block
= NULL
;
227 MP4_Box_t
*p_iloc
= MP4_BoxGet( p_sys
->p_root
, "meta/iloc" );
231 for( uint32_t i
=0; i
<BOXDATA(p_iloc
)->i_item_count
; i
++ )
233 if( BOXDATA(p_iloc
)->p_items
[i
].i_item_id
!= i_item_id
)
236 block_t
**pp_append
= &p_block
;
238 /* Shared prefix data, ex: JPEG */
239 if( p_shared_header
)
241 *pp_append
= block_Alloc( p_shared_header
->data
.p_binary
->i_blob
);
244 memcpy( (*pp_append
)->p_buffer
,
245 p_shared_header
->data
.p_binary
->p_blob
,
246 p_shared_header
->data
.p_binary
->i_blob
);
247 pp_append
= &((*pp_append
)->p_next
);
251 for( uint16_t j
=0; j
<BOXDATA(p_iloc
)->p_items
[i
].i_extent_count
; j
++ )
253 uint64_t i_offset
= BOXDATA(p_iloc
)->p_items
[i
].i_base_offset
+
254 BOXDATA(p_iloc
)->p_items
[i
].p_extents
[j
].i_extent_offset
;
255 uint64_t i_length
= BOXDATA(p_iloc
)->p_items
[i
].p_extents
[j
].i_extent_length
;
257 if( BOXDATA(p_iloc
)->p_items
[i
].i_construction_method
< 2 )
259 /* Extents are in 1:file, 2:idat */
260 if( BOXDATA(p_iloc
)->p_items
[i
].i_construction_method
== 1 )
262 MP4_Box_t
*idat
= MP4_BoxGet( p_sys
->p_root
, "meta/idat" );
265 i_offset
+= idat
->i_pos
+ mp4_box_headersize(idat
);
268 if( vlc_stream_Seek( p_demux
->s
, i_offset
) != VLC_SUCCESS
)
270 *pp_append
= vlc_stream_Block( p_demux
->s
, i_length
);
272 /* Extents are 3:iloc reference */
273 else if( BOXDATA(p_iloc
)->p_items
[i
].i_construction_method
== 2 )
275 /* FIXME ? That's totally untested and really complicated */
276 uint32_t i_extent_index
= BOXDATA(p_iloc
)->p_items
[i
].p_extents
[j
].i_extent_index
;
277 if(i_extent_index
== 0)
278 i_extent_index
= 1; /* Inferred. Indexes start 1 */
279 const MP4_Box_t
*p_iref
= MP4_BoxGet( p_sys
->p_root
, "meta/iref" );
282 for( const MP4_Box_t
*p_refbox
= p_iref
->p_first
;
283 p_refbox
; p_refbox
= p_refbox
->p_next
)
285 if( p_refbox
->i_type
!= VLC_FOURCC('i','l','o','c') ||
286 BOXDATA(p_refbox
)->i_from_item_id
== i_item_id
)
289 for( uint16_t k
=0; k
< BOXDATA(p_refbox
)->i_reference_count
; k
++ )
291 if( --i_extent_index
> 0 )
293 if( BOXDATA(p_refbox
)->p_references
[k
].i_to_item_id
!= i_item_id
)
295 *pp_append
= ReadItemExtents(p_demux
,
296 BOXDATA(p_refbox
)->p_references
[k
].i_to_item_id
,
306 pp_append
= &((*pp_append
)->p_next
);
312 p_block
= block_ChainGather( p_block
);
317 static int SetPictureProperties( demux_t
*p_demux
, uint32_t i_item_id
,
318 es_format_t
*fmt
, const MP4_Box_t
**p_header
)
320 struct heif_private_t
*p_sys
= (void *) p_demux
->p_sys
;
322 const MP4_Box_t
*p_ipma
= MP4_BoxGet( p_sys
->p_root
, "meta/iprp/ipma" );
326 /* Load properties */
327 for( uint32_t i
=0; i
<BOXDATA(p_ipma
)->i_entry_count
; i
++ )
329 if( BOXDATA(p_ipma
)->p_entries
[i
].i_item_id
!= i_item_id
)
332 for( uint8_t j
=0; j
<BOXDATA(p_ipma
)->p_entries
[i
].i_association_count
; j
++ )
334 if( !BOXDATA(p_ipma
)->p_entries
[i
].p_assocs
[j
].i_property_index
)
337 const MP4_Box_t
*p_prop
= MP4_BoxGet( p_sys
->p_root
, "meta/iprp/ipco/[%u]",
338 BOXDATA(p_ipma
)->p_entries
[i
].p_assocs
[j
].i_property_index
- 1 );
342 switch( p_prop
->i_type
)
348 ((fmt
->i_codec
== VLC_CODEC_HEVC
&& p_prop
->i_type
== ATOM_hvcC
) ||
349 (fmt
->i_codec
== VLC_CODEC_H264
&& p_prop
->i_type
== ATOM_avcC
) ||
350 (fmt
->i_codec
== VLC_CODEC_AV1
&& p_prop
->i_type
== ATOM_av1C
)) )
352 fmt
->p_extra
= malloc( p_prop
->data
.p_binary
->i_blob
);
355 fmt
->i_extra
= p_prop
->data
.p_binary
->i_blob
;
356 memcpy( fmt
->p_extra
, p_prop
->data
.p_binary
->p_blob
, fmt
->i_extra
);
361 if( fmt
->i_codec
== VLC_CODEC_JPEG
)
365 fmt
->video
.i_visible_width
= p_prop
->data
.p_ispe
->i_width
;
366 fmt
->video
.i_visible_height
= p_prop
->data
.p_ispe
->i_height
;
369 if( p_prop
->data
.p_pasp
->i_horizontal_spacing
&&
370 p_prop
->data
.p_pasp
->i_vertical_spacing
)
372 fmt
->video
.i_sar_num
= p_prop
->data
.p_pasp
->i_horizontal_spacing
;
373 fmt
->video
.i_sar_den
= p_prop
->data
.p_pasp
->i_vertical_spacing
;
377 switch( p_prop
->data
.p_irot
->i_ccw_degrees
% 360 )
380 case 0: fmt
->video
.orientation
= ORIENT_NORMAL
; break;
381 case 90: fmt
->video
.orientation
= ORIENT_ROTATED_90
; break;
382 case 180: fmt
->video
.orientation
= ORIENT_ROTATED_180
; break;
383 case 270: fmt
->video
.orientation
= ORIENT_ROTATED_270
; break;
387 fmt
->video
.primaries
= iso_23001_8_cp_to_vlc_primaries(
388 p_prop
->data
.p_colr
->nclc
.i_primary_idx
);
389 fmt
->video
.transfer
= iso_23001_8_tc_to_vlc_xfer(
390 p_prop
->data
.p_colr
->nclc
.i_transfer_function_idx
);
391 fmt
->video
.space
= iso_23001_8_mc_to_vlc_coeffs(
392 p_prop
->data
.p_colr
->nclc
.i_matrix_idx
);
393 fmt
->video
.b_color_range_full
= p_prop
->data
.p_colr
->nclc
.i_full_range
;
396 fmt
->video
.lighting
.MaxCLL
= p_prop
->data
.p_CoLL
->i_maxCLL
;
397 fmt
->video
.lighting
.MaxFALL
= p_prop
->data
.p_CoLL
->i_maxFALL
;
400 memcpy( fmt
->video
.mastering
.primaries
,
401 p_prop
->data
.p_SmDm
->primaries
, sizeof(uint16_t) * 6 );
402 memcpy( fmt
->video
.mastering
.white_point
,
403 p_prop
->data
.p_SmDm
->white_point
, sizeof(uint16_t) * 2 );
404 fmt
->video
.mastering
.max_luminance
= p_prop
->data
.p_SmDm
->i_luminanceMax
;
405 fmt
->video
.mastering
.min_luminance
= p_prop
->data
.p_SmDm
->i_luminanceMin
;
411 fmt
->video
.i_frame_rate
= 1000;
412 fmt
->video
.i_frame_rate_base
= p_sys
->i_image_duration
/ 1000;
417 static int SetupPicture( demux_t
*p_demux
, const MP4_Box_t
*p_infe
,
418 es_format_t
*fmt
, const MP4_Box_t
**p_header
)
423 const uint32_t i_item_id
= BOXDATA(p_infe
)->i_item_id
;
424 const char *psz_mime
= BOXDATA(p_infe
)->psz_content_type
;
425 switch( BOXDATA(p_infe
)->item_type
)
427 case VLC_FOURCC('h','v','c','1'):
428 es_format_Init( fmt
, VIDEO_ES
, VLC_CODEC_HEVC
);
430 case VLC_FOURCC('a','v','c','1'):
431 es_format_Init( fmt
, VIDEO_ES
, VLC_CODEC_H264
);
434 es_format_Init( fmt
, VIDEO_ES
, VLC_CODEC_AV1
);
436 case VLC_FOURCC('j','p','e','g'):
437 es_format_Init( fmt
, VIDEO_ES
, VLC_CODEC_JPEG
);
442 if( !strcasecmp( "image/jpeg", psz_mime
) )
443 es_format_Init( fmt
, VIDEO_ES
, VLC_CODEC_JPEG
);
444 else if( !strcasecmp( "image/avif", psz_mime
) )
445 es_format_Init( fmt
, VIDEO_ES
, VLC_CODEC_AV1
);
450 if( fmt
->i_codec
== 0 )
453 return SetPictureProperties( p_demux
, i_item_id
, fmt
, p_header
);
456 union heif_derivation_data
460 uint8_t rows_minus_one
;
461 uint8_t columns_minus_one
;
462 uint32_t output_width
;
463 uint32_t output_height
;
467 static int ReadDerivationData_Grid( const uint8_t *p_data
, size_t i_data
,
468 union heif_derivation_data
*d
)
470 if( i_data
< 8 || p_data
[0] != 0x00 )
473 uint8_t i_fieldlength
= ((p_data
[1] & 0x01) + 1) << 1;
474 /* length is either 2 or 4 bytes */
475 d
->ImageGrid
.rows_minus_one
= p_data
[2];
476 d
->ImageGrid
.columns_minus_one
= p_data
[3];
477 if(i_fieldlength
== 2)
479 d
->ImageGrid
.output_width
= GetWBE(&p_data
[4]);
480 d
->ImageGrid
.output_height
= GetWBE(&p_data
[6]);
486 d
->ImageGrid
.output_width
= GetDWBE(&p_data
[4]);
487 d
->ImageGrid
.output_height
= GetDWBE(&p_data
[8]);
492 static int ReadDerivationData( demux_t
*p_demux
, vlc_fourcc_t type
,
494 union heif_derivation_data
*d
)
496 int i_ret
= VLC_EGENERIC
;
497 block_t
*p_data
= ReadItemExtents( p_demux
, i_item_id
, NULL
);
502 case VLC_FOURCC('g','r','i','d'):
503 i_ret
= ReadDerivationData_Grid( p_data
->p_buffer
,
504 p_data
->i_buffer
, d
);
509 block_Release( p_data
);
514 static int LoadGridImage( demux_t
*p_demux
, uint32_t i_pic_item_id
,
516 unsigned tile
, unsigned gridcols
,
517 unsigned imagewidth
, unsigned imageheight
)
519 struct heif_private_t
*p_sys
= (void *) p_demux
->p_sys
;
521 MP4_Box_t
*p_infe
= GetAtom( p_sys
->p_root
, NULL
,
522 ATOM_infe
, "meta/iinf/infe",
523 MatchInfeID
, &i_pic_item_id
);
528 es_format_Init(&fmt
, UNKNOWN_ES
, 0);
530 const MP4_Box_t
*p_shared_header
= NULL
;
531 if( SetupPicture( p_demux
, p_infe
, &fmt
, &p_shared_header
) != VLC_SUCCESS
)
533 es_format_Clean( &fmt
);
534 return VLC_EGENERIC
; /* Unsupported picture, goto next */
537 block_t
*p_sample
= ReadItemExtents( p_demux
, i_pic_item_id
,
541 es_format_Clean( &fmt
);
545 image_handler_t
*handler
= image_HandlerCreate( p_demux
);
548 block_Release( p_sample
);
549 es_format_Clean( &fmt
);
553 video_format_t decoded
;
554 video_format_Init( &decoded
, VLC_CODEC_RGBA
);
556 fmt
.video
.i_chroma
= fmt
.i_codec
;
557 picture_t
*p_picture
= image_ReadExt( handler
, p_sample
, &fmt
.video
,
558 fmt
.p_extra
, fmt
.i_extra
, &decoded
);
559 image_HandlerDelete( handler
);
561 es_format_Clean( &fmt
);
566 const unsigned tilewidth
= p_picture
->format
.i_visible_width
;
567 const unsigned tileheight
= p_picture
->format
.i_visible_height
;
568 uint8_t *dstline
= p_buffer
;
569 dstline
+= (tile
/ gridcols
) * (imagewidth
* tileheight
* 4);
572 const unsigned offsetpxw
= (tile
% gridcols
) * tilewidth
;
573 const unsigned offsetpxh
= (tile
/ gridcols
) * tileheight
;
574 if( offsetpxw
> imagewidth
)
576 const uint8_t *srcline
= p_picture
->p
[0].p_pixels
;
577 unsigned tocopylines
= p_picture
->p
[0].i_lines
;
578 if(offsetpxh
+ tocopylines
>= imageheight
)
579 tocopylines
= imageheight
- offsetpxh
;
580 for(unsigned i
=0; i
<tocopylines
; i
++)
582 size_t tocopypx
= tilewidth
;
583 if( offsetpxw
+ tilewidth
> imagewidth
)
584 tocopypx
= imagewidth
- offsetpxw
;
585 memcpy( &dstline
[offsetpxw
* 4], srcline
, tocopypx
* 4 );
586 dstline
+= imagewidth
* 4;
587 srcline
+= p_picture
->p
[0].i_pitch
;
593 picture_Release( p_picture
);
598 static int DerivedImageAssembleGrid( demux_t
*p_demux
, uint32_t i_grid_item_id
,
599 es_format_t
*fmt
, block_t
**pp_block
)
601 struct heif_private_t
*p_sys
= (void *) p_demux
->p_sys
;
603 const MP4_Box_t
*p_iref
= MP4_BoxGet( p_sys
->p_root
, "meta/iref" );
607 const MP4_Box_t
*p_refbox
;
608 for( p_refbox
= p_iref
->p_first
; p_refbox
; p_refbox
= p_refbox
->p_next
)
610 if( p_refbox
->i_type
== VLC_FOURCC('d','i','m','g') &&
611 BOXDATA(p_refbox
)->i_from_item_id
== i_grid_item_id
)
618 union heif_derivation_data derivation_data
;
619 if( ReadDerivationData( p_demux
,
620 p_sys
->current
.BOXDATA(p_infe
)->item_type
,
621 i_grid_item_id
, &derivation_data
) != VLC_SUCCESS
)
624 msg_Dbg(p_demux
,"%ux%upx image %ux%u tiles composition",
625 derivation_data
.ImageGrid
.output_width
,
626 derivation_data
.ImageGrid
.output_height
,
627 derivation_data
.ImageGrid
.columns_minus_one
+ 1,
628 derivation_data
.ImageGrid
.columns_minus_one
+ 1);
630 block_t
*p_block
= block_Alloc( derivation_data
.ImageGrid
.output_width
*
631 derivation_data
.ImageGrid
.output_height
* 4 );
636 es_format_Init( fmt
, VIDEO_ES
, VLC_CODEC_RGBA
);
637 fmt
->video
.i_sar_num
=
639 fmt
->video
.i_visible_width
= derivation_data
.ImageGrid
.output_width
;
640 fmt
->video
.i_sar_den
=
641 fmt
->video
.i_height
=
642 fmt
->video
.i_visible_height
= derivation_data
.ImageGrid
.output_height
;
644 for( uint16_t i
=0; i
<BOXDATA(p_refbox
)->i_reference_count
; i
++ )
646 msg_Dbg( p_demux
, "Loading tile %d/%d", i
,
647 (derivation_data
.ImageGrid
.rows_minus_one
+ 1) *
648 (derivation_data
.ImageGrid
.columns_minus_one
+ 1) );
649 LoadGridImage( p_demux
,
650 BOXDATA(p_refbox
)->p_references
[i
].i_to_item_id
,
651 p_block
->p_buffer
, i
,
652 derivation_data
.ImageGrid
.columns_minus_one
+ 1,
653 derivation_data
.ImageGrid
.output_width
,
654 derivation_data
.ImageGrid
.output_height
);
657 SetPictureProperties( p_demux
, i_grid_item_id
, fmt
, NULL
);
662 static int DemuxHEIF( demux_t
*p_demux
)
664 struct heif_private_t
*p_sys
= (void *) p_demux
->p_sys
;
666 /* Displaying a picture */
667 if( p_sys
->i_end_display_time
> 0 )
670 es_out_Control( p_demux
->out
, ES_OUT_GET_EMPTY
, &b_empty
);
671 if( !b_empty
|| vlc_tick_now() <= p_sys
->i_end_display_time
)
673 vlc_tick_sleep( VLC_TICK_FROM_MS(40) );
674 return VLC_DEMUXER_SUCCESS
;
676 p_sys
->i_end_display_time
= 0;
679 /* Reset prev pic params */
680 p_sys
->current
.p_shared_header
= NULL
;
682 /* First or next picture */
683 if( !p_sys
->current
.p_infe
)
685 MP4_Box_t
*p_pitm
= MP4_BoxGet( p_sys
->p_root
, "meta/pitm" );
687 return VLC_DEMUXER_EOF
;
689 p_sys
->current
.p_infe
= GetAtom( p_sys
->p_root
, NULL
,
690 ATOM_infe
, "meta/iinf/infe",
691 MatchInfeID
, &BOXDATA(p_pitm
)->i_item_id
);
695 p_sys
->current
.p_infe
= GetAtom( p_sys
->p_root
, p_sys
->current
.p_infe
,
696 ATOM_infe
, "meta/iinf/infe",
697 MatchPureImage
, p_sys
->p_root
);
700 if( !p_sys
->current
.p_infe
)
701 return VLC_DEMUXER_EOF
;
703 const uint32_t i_current_item_id
= p_sys
->current
.BOXDATA(p_infe
)->i_item_id
;
704 const MP4_Box_t
*p_ipco
= MP4_BoxGet( p_sys
->p_root
, "meta/iprp/ipco" );
706 return VLC_DEMUXER_EOF
;
709 es_format_Init(&fmt
, UNKNOWN_ES
, 0);
711 block_t
*p_block
= NULL
;
712 if( p_sys
->current
.BOXDATA(p_infe
)->item_type
== VLC_FOURCC('g','r','i','d') )
714 if( DerivedImageAssembleGrid( p_demux
, i_current_item_id
,
715 &fmt
, &p_block
) != VLC_SUCCESS
)
717 es_format_Clean( &fmt
);
718 return VLC_DEMUXER_SUCCESS
;
723 if( SetupPicture( p_demux
, p_sys
->current
.p_infe
,
724 &fmt
, &p_sys
->current
.p_shared_header
) != VLC_SUCCESS
)
726 es_format_Clean( &fmt
);
727 return VLC_DEMUXER_SUCCESS
;
730 p_block
= ReadItemExtents( p_demux
, i_current_item_id
,
731 p_sys
->current
.p_shared_header
);
734 es_format_Clean( &fmt
);
735 return VLC_DEMUXER_SUCCESS
; /* Goto next picture */
739 es_format_Clean( &p_sys
->current
.fmt
);
740 es_format_Copy( &p_sys
->current
.fmt
, &fmt
);
741 es_format_Clean( &fmt
);
743 es_out_Del( p_demux
->out
, p_sys
->id
);
744 p_sys
->id
= es_out_Add( p_demux
->out
, &p_sys
->current
.fmt
);
748 p_sys
->current
.p_infe
= NULL
; /* Goto next picture */
749 return VLC_DEMUXER_SUCCESS
;
752 if( p_sys
->i_pcr
== VLC_TICK_INVALID
)
754 p_sys
->i_pcr
= VLC_TICK_0
;
755 es_out_SetPCR( p_demux
->out
, p_sys
->i_pcr
);
758 p_block
->i_dts
= p_block
->i_pts
= p_sys
->i_pcr
;
759 p_block
->i_length
= p_sys
->i_image_duration
;
761 p_block
->i_flags
|= BLOCK_FLAG_END_OF_SEQUENCE
;
763 p_sys
->i_end_display_time
= vlc_tick_now() + p_block
->i_length
;
764 p_sys
->b_seekpoint_changed
= true;
766 p_sys
->i_pcr
= p_block
->i_dts
+ p_block
->i_length
;
767 es_out_Send( p_demux
->out
, p_sys
->id
, p_block
);
768 es_out_SetPCR( p_demux
->out
, p_sys
->i_pcr
);
770 return VLC_DEMUXER_SUCCESS
;
773 int OpenHEIF( vlc_object_t
* p_this
)
775 demux_t
*p_demux
= (demux_t
*)p_this
;
776 const uint8_t *p_peek
;
778 if( vlc_stream_Peek( p_demux
->s
, &p_peek
, 12 ) < 12 )
781 if( VLC_FOURCC( p_peek
[4], p_peek
[5], p_peek
[6], p_peek
[7] ) != ATOM_ftyp
)
784 switch( VLC_FOURCC( p_peek
[8], p_peek
[9], p_peek
[10], p_peek
[11] ) )
802 MP4_Box_t
*p_root
= MP4_BoxGetRoot( p_demux
->s
);
806 MP4_BoxDumpStructure( p_demux
->s
, p_root
);
808 struct heif_private_t
*p_sys
= calloc( 1, sizeof(*p_sys
) );
809 p_demux
->p_sys
= (void *) p_sys
;
810 p_sys
->p_root
= p_root
;
811 p_sys
->p_title
= vlc_input_title_New();
812 if( !p_sys
->p_title
)
818 p_sys
->i_image_duration
= vlc_tick_from_sec(var_InheritFloat( p_demux
, "heif-image-duration" ));
819 if( p_sys
->i_image_duration
<= 0 )
820 p_sys
->i_image_duration
= VLC_TICK_FROM_SEC(HEIF_DEFAULT_DURATION
);
822 MP4_Box_t
*p_infe
= NULL
;
823 while( (p_infe
= NextAtom( p_root
, ATOM_infe
, "meta/iinf/infe", p_infe
)) )
825 if( (BOXDATA(p_infe
)->i_flags
& 0x01) != 0x00 ||
826 !MatchPureImage( p_infe
, p_root
) )
828 seekpoint_t
*s
= vlc_seekpoint_New();
831 s
->i_time_offset
= p_sys
->p_title
->i_seekpoint
* p_sys
->i_image_duration
;
832 if( BOXDATA(p_infe
)->psz_item_name
)
833 s
->psz_name
= strdup( BOXDATA(p_infe
)->psz_item_name
);
834 TAB_APPEND( p_sys
->p_title
->i_seekpoint
, p_sys
->p_title
->seekpoint
, s
);
838 es_format_Init( &p_sys
->current
.fmt
, UNKNOWN_ES
, 0 );
840 p_demux
->pf_demux
= DemuxHEIF
;
841 p_demux
->pf_control
= ControlHEIF
;
846 void CloseHEIF ( vlc_object_t
* p_this
)
848 demux_t
*p_demux
= (demux_t
*)p_this
;
849 struct heif_private_t
*p_sys
= (void *) p_demux
->p_sys
;
850 MP4_BoxFree( p_sys
->p_root
);
852 es_out_Del( p_demux
->out
, p_sys
->id
);
853 es_format_Clean( &p_sys
->current
.fmt
);
854 vlc_input_title_Delete( p_sys
->p_title
);