1 /*****************************************************************************
2 * mock.c : mock demux module for vlc
3 *****************************************************************************
4 * Copyright (C) 2018 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 *****************************************************************************/
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
31 #include <vlc_picture.h>
32 #include <vlc_demux.h>
33 #include <vlc_input.h>
34 #include <vlc_vector.h>
42 typedef struct VLC_VECTOR(struct mock_track
*) mock_track_vector
;
45 var_InheritSsize(vlc_object_t
*obj
, const char *name
)
47 int64_t value
= var_InheritInteger(obj
, name
);
48 return value
>= 0 ? value
: -1;
52 var_InheritUnsigned(vlc_object_t
*obj
, const char *name
)
54 int64_t value
= var_InheritInteger(obj
, name
);
55 return value
>= 0 && value
< UINT_MAX
? value
: UINT_MAX
;
59 var_InheritFourcc(vlc_object_t
*obj
, const char *name
)
61 char *var_value
= var_InheritString(obj
, name
);
65 size_t var_len
= strlen(var_value
);
72 /* Pad with spaces if the string len is less than 4 */
74 strcpy(value
, var_value
);
80 memcpy(&fourcc
, value
, 4);
84 /* var_name, type, module_header_type, getter, default_value */
85 #define LIST_OPTIONS \
86 X(length, vlc_tick_t, add_integer, var_InheritInteger, VLC_TICK_FROM_MS(5000)) \
87 X(audio_track_count, ssize_t, add_integer, var_InheritSsize, 0) \
88 X(audio_channels, unsigned, add_integer, var_InheritUnsigned, 2) \
89 X(audio_format, vlc_fourcc_t, add_string, var_InheritFourcc, "u8") \
90 X(audio_rate, unsigned, add_integer, var_InheritUnsigned, 44100) \
91 X(audio_packetized, bool, add_bool, var_InheritBool, true) \
92 X(video_track_count, ssize_t, add_integer, var_InheritSsize, 0) \
93 X(video_chroma, vlc_fourcc_t, add_string, var_InheritFourcc, "I420") \
94 X(video_width, unsigned, add_integer, var_InheritUnsigned, 640) \
95 X(video_height, unsigned, add_integer, var_InheritUnsigned, 480) \
96 X(video_frame_rate, unsigned, add_integer, var_InheritUnsigned, 25) \
97 X(video_frame_rate_base, unsigned, add_integer, var_InheritUnsigned, 1) \
98 X(video_packetized, bool, add_bool, var_InheritBool, true) \
99 X(sub_track_count, ssize_t, add_integer, var_InheritSsize, 0) \
100 X(sub_packetized, bool, add_bool, var_InheritBool, true) \
101 X(title_count, ssize_t, add_integer, var_InheritSsize, 0 ) \
102 X(chapter_count, ssize_t, add_integer, var_InheritSsize, 0) \
103 X(null_names, bool, add_bool, var_InheritBool, false) \
104 X(program_count, ssize_t, add_integer, var_InheritSsize, 0) \
105 X(can_seek, bool, add_bool, var_InheritBool, true) \
106 X(can_pause, bool, add_bool, var_InheritBool, true) \
107 X(can_control_pace, bool, add_bool, var_InheritBool, true) \
108 X(can_control_rate, bool, add_bool, var_InheritBool, true) \
109 X(can_record, bool, add_bool, var_InheritBool, true) \
110 X(error, bool, add_bool, var_InheritBool, false) \
111 X(add_video_track_at, vlc_tick_t, add_integer, var_InheritInteger, VLC_TICK_INVALID ) \
112 X(add_audio_track_at, vlc_tick_t, add_integer, var_InheritInteger, VLC_TICK_INVALID ) \
113 X(add_spu_track_at, vlc_tick_t, add_integer, var_InheritInteger, VLC_TICK_INVALID ) \
117 mock_track_vector tracks
;
119 vlc_tick_t step_length
;
124 #define X(var_name, type, module_header_type, getter, default_value) \
130 static input_title_t
*
131 CreateTitle(demux_t
*demux
, size_t idx
)
133 struct demux_sys
*sys
= demux
->p_sys
;
135 input_title_t
*t
= vlc_input_title_New();
139 t
->i_length
= sys
->length
;
141 && asprintf(&t
->psz_name
, "Mock Title %zu", idx
) == -1)
144 vlc_input_title_Delete(t
);
147 t
->seekpoint
= vlc_alloc(sys
->chapter_count
, sizeof(*t
->seekpoint
));
150 vlc_input_title_Delete(t
);
154 for (ssize_t i
= 0; i
< sys
->chapter_count
; ++i
)
156 t
->seekpoint
[i
] = vlc_seekpoint_New();
157 if (!t
->seekpoint
[i
])
159 vlc_input_title_Delete(t
);
164 && asprintf(&t
->seekpoint
[i
]->psz_name
, "Mock Chapter %zu-%zu", idx
, i
)
167 vlc_input_title_Delete(t
);
170 t
->seekpoint
[i
]->i_time_offset
= i
* sys
->chapter_gap
;
176 Control(demux_t
*demux
, int query
, va_list args
)
178 struct demux_sys
*sys
= demux
->p_sys
;
183 *va_arg(args
, bool *) = sys
->can_seek
;
185 case DEMUX_CAN_PAUSE
:
186 *va_arg(args
, bool *) = sys
->can_pause
;
188 case DEMUX_CAN_CONTROL_PACE
:
189 *va_arg(args
, bool *) = sys
->can_control_pace
;
191 case DEMUX_GET_PTS_DELAY
:
192 *va_arg(args
, vlc_tick_t
*) = 0;
196 case DEMUX_GET_SIGNAL
:
198 case DEMUX_SET_PAUSE_STATE
:
199 return sys
->can_pause
? VLC_SUCCESS
: VLC_EGENERIC
;
200 case DEMUX_SET_TITLE
:
201 if (sys
->title_count
> 0)
203 int new_title
= va_arg(args
, int);
204 if (new_title
>= sys
->title_count
)
206 sys
->current_title
= new_title
;
207 sys
->pts
= VLC_TICK_0
;
211 case DEMUX_SET_SEEKPOINT
:
212 if (sys
->chapter_gap
> 0)
214 const int seekpoint_idx
= va_arg(args
, int);
215 if (seekpoint_idx
< sys
->chapter_count
)
217 sys
->pts
= seekpoint_idx
* sys
->chapter_gap
;
222 case DEMUX_TEST_AND_CLEAR_FLAGS
:
224 case DEMUX_GET_TITLE
:
225 if (sys
->title_count
> 0)
227 *va_arg(args
, int *) = sys
->current_title
;
231 case DEMUX_GET_SEEKPOINT
:
232 if (sys
->chapter_gap
> 0)
234 *va_arg(args
, int *) = sys
->pts
/ sys
->chapter_gap
;
238 case DEMUX_GET_POSITION
:
239 *va_arg(args
, double *) = sys
->pts
/ (double) sys
->length
;
241 case DEMUX_SET_POSITION
:
244 sys
->pts
= va_arg(args
, double) * sys
->length
;
246 case DEMUX_GET_LENGTH
:
247 *va_arg(args
, vlc_tick_t
*) = sys
->length
;
250 *va_arg(args
, vlc_tick_t
*) = sys
->pts
;
255 sys
->pts
= va_arg(args
, vlc_tick_t
);
257 case DEMUX_GET_TITLE_INFO
:
258 if (sys
->title_count
> 0)
260 input_title_t
***titles
= va_arg(args
, input_title_t
***);
261 *titles
= vlc_alloc(sys
->title_count
, sizeof(*titles
));
264 for (ssize_t i
= 0; i
< sys
->title_count
; ++i
)
266 (*titles
)[i
] = CreateTitle(demux
, i
);
270 vlc_input_title_Delete((*titles
)[i
- 1]);
276 *va_arg(args
, int *) = sys
->title_count
;
277 *va_arg(args
, int *) = 0;
278 *va_arg(args
, int *) = 0;
282 case DEMUX_SET_GROUP_DEFAULT
:
284 case DEMUX_SET_GROUP_ALL
:
286 case DEMUX_SET_GROUP_LIST
:
290 case DEMUX_SET_NEXT_DEMUX_TIME
:
294 case DEMUX_HAS_UNSUPPORTED_META
:
296 case DEMUX_GET_ATTACHMENTS
:
298 case DEMUX_CAN_RECORD
:
299 *va_arg(args
, bool *) = sys
->can_record
;
301 case DEMUX_SET_RECORD_STATE
:
302 return sys
->can_record
? VLC_SUCCESS
: VLC_EGENERIC
;
303 case DEMUX_CAN_CONTROL_RATE
:
304 *va_arg(args
, bool *) = sys
->can_control_rate
;
307 return sys
->can_control_rate
? VLC_SUCCESS
: VLC_EGENERIC
;
308 case DEMUX_IS_PLAYLIST
:
310 case DEMUX_NAV_ACTIVATE
:
318 case DEMUX_NAV_RIGHT
:
320 case DEMUX_NAV_POPUP
:
330 CreateAudioBlock(demux_t
*demux
, struct mock_track
*track
)
332 struct demux_sys
*sys
= demux
->p_sys
;
333 const int64_t samples
=
334 samples_from_vlc_tick(sys
->step_length
, track
->fmt
.audio
.i_rate
);
335 const int64_t bytes
= samples
/ track
->fmt
.audio
.i_frame_length
336 * track
->fmt
.audio
.i_bytes_per_frame
;
337 block_t
*b
= block_Alloc(bytes
);
340 memset(b
->p_buffer
, 0, b
->i_buffer
);
351 video_block_free_cb(block_t
*b
)
353 struct video_block
*video
= container_of(b
, struct video_block
, b
);
354 picture_Release(video
->pic
);
359 CreateVideoBlock(demux_t
*demux
, struct mock_track
*track
)
361 struct demux_sys
*sys
= demux
->p_sys
;
362 picture_t
*pic
= picture_NewFromFormat(&track
->fmt
.video
);
366 struct video_block
*video
= malloc(sizeof(*video
));
369 picture_Release(pic
);
374 static const struct vlc_block_callbacks cbs
=
376 .free
= video_block_free_cb
379 size_t block_len
= 0;
380 for (int i
= 0; i
< pic
->i_planes
; ++i
)
381 block_len
+= pic
->p
[i
].i_lines
* pic
->p
[i
].i_pitch
;
382 memset(pic
->p
[0].p_pixels
, (sys
->pts
/ VLC_TICK_FROM_MS(10)) % 255,
384 return block_Init(&video
->b
, &cbs
, pic
->p
[0].p_pixels
, block_len
);
389 CreateSubBlock(demux_t
*demux
, struct mock_track
*track
)
391 struct demux_sys
*sys
= demux
->p_sys
;
393 if (asprintf(&text
, "subtitle @ %"PRId64
, sys
->pts
) == -1)
395 size_t len
= strlen(text
) + 1;
397 block_t
*b
= block_Alloc(len
);
404 memcpy(b
->p_buffer
, text
, len
);
413 AppendMockTrack(demux_t
*demux
, const es_format_t
*fmt
, int group
,
416 struct demux_sys
*sys
= demux
->p_sys
;
417 struct mock_track
*mock_track
= malloc(sizeof(*mock_track
));
420 mock_track
->fmt
= *fmt
;
421 mock_track
->fmt
.i_group
= group
;
422 mock_track
->fmt
.b_packetized
= packetized
;
423 mock_track
->id
= es_out_Add(demux
->out
, & mock_track
->fmt
);
429 bool success
= vlc_vector_push(&sys
->tracks
, mock_track
);
430 assert(success
); (void) success
; /* checked by reserve() */
435 InitVideoTracks(demux_t
*demux
, int group
, size_t count
)
437 struct demux_sys
*sys
= demux
->p_sys
;
442 const vlc_chroma_description_t
*desc
=
443 vlc_fourcc_GetChromaDescription(sys
->video_chroma
);
444 if (!desc
|| desc
->plane_count
== 0)
445 sys
->video_chroma
= 0;
447 const bool frame_rate_ok
=
448 sys
->video_frame_rate
!= 0 && sys
->video_frame_rate
!= UINT_MAX
&&
449 sys
->video_frame_rate_base
!= 0 && sys
->video_frame_rate_base
!= UINT_MAX
;
450 const bool chroma_ok
= sys
->video_chroma
!= 0;
451 const bool size_ok
= sys
->video_width
!= UINT_MAX
&&
452 sys
->video_height
!= UINT_MAX
;
454 if (sys
->video_frame_rate
== 0 || sys
->video_frame_rate_base
== 0
455 || sys
->video_chroma
== 0)
456 if (!frame_rate_ok
|| !chroma_ok
|| !size_ok
)
459 msg_Err(demux
, "Invalid video frame rate");
461 msg_Err(demux
, "Invalid video chroma");
463 msg_Err(demux
, "Invalid video size");
467 for (size_t i
= 0; i
< count
; ++i
)
470 es_format_Init(&fmt
, VIDEO_ES
, sys
->video_chroma
);
471 fmt
.video
.i_chroma
= fmt
.i_codec
;
472 fmt
.video
.i_width
= fmt
.video
.i_visible_width
= sys
->video_width
;
473 fmt
.video
.i_height
= fmt
.video
.i_visible_height
= sys
->video_height
;
474 fmt
.video
.i_frame_rate
= sys
->video_frame_rate
;
475 fmt
.video
.i_frame_rate_base
= sys
->video_frame_rate_base
;
477 if (AppendMockTrack(demux
, &fmt
, group
, sys
->video_packetized
))
484 InitAudioTracks(demux_t
*demux
, int group
, size_t count
)
486 struct demux_sys
*sys
= demux
->p_sys
;
491 const bool rate_ok
= sys
->audio_rate
> 0 && sys
->audio_rate
!= UINT_MAX
;
492 const bool format_ok
= aout_BitsPerSample(sys
->audio_format
) != 0;
493 const bool channels_ok
= sys
->audio_channels
> 0 &&
494 sys
->audio_channels
<= AOUT_CHAN_MAX
;
496 if (!rate_ok
|| !format_ok
|| !channels_ok
)
499 msg_Err(demux
, "Invalid audio rate");
501 msg_Err(demux
, "Invalid audio format");
503 msg_Err(demux
, "Invalid audio channels");
507 uint16_t physical_channels
= 0;
508 switch (sys
->audio_channels
)
510 case 1: physical_channels
= AOUT_CHAN_CENTER
; break;
511 case 2: physical_channels
= AOUT_CHANS_2_0
; break;
512 case 3: physical_channels
= AOUT_CHANS_2_1
; break;
513 case 4: physical_channels
= AOUT_CHANS_4_0
; break;
514 case 5: physical_channels
= AOUT_CHANS_4_1
; break;
515 case 6: physical_channels
= AOUT_CHANS_6_0
; break;
516 case 7: physical_channels
= AOUT_CHANS_7_0
; break;
517 case 8: physical_channels
= AOUT_CHANS_7_1
; break;
518 case 9: physical_channels
= AOUT_CHANS_8_1
; break;
519 default: vlc_assert_unreachable();
522 for (size_t i
= 0; i
< count
; ++i
)
525 es_format_Init(&fmt
, AUDIO_ES
, sys
->audio_format
);
526 fmt
.audio
.i_format
= fmt
.i_codec
;
527 fmt
.audio
.i_rate
= sys
->audio_rate
;
528 fmt
.audio
.i_physical_channels
= physical_channels
;
529 aout_FormatPrepare(&fmt
.audio
);
531 if (AppendMockTrack(demux
, &fmt
, group
, sys
->audio_packetized
))
539 InitSubTracks(demux_t
*demux
, int group
, size_t count
)
541 struct demux_sys
*sys
= demux
->p_sys
;
546 for (size_t i
= 0; i
< count
; ++i
)
549 es_format_Init(&fmt
, SPU_ES
, VLC_CODEC_SUBT
);
551 if (AppendMockTrack(demux
, &fmt
, group
, sys
->sub_packetized
))
559 Demux(demux_t
*demux
)
561 struct demux_sys
*sys
= demux
->p_sys
;
564 return VLC_DEMUXER_EGENERIC
;
566 struct mock_track
*track
;
567 vlc_vector_foreach(track
, &sys
->tracks
)
570 switch (track
->fmt
.i_cat
)
573 block
= CreateAudioBlock(demux
, track
);
576 block
= CreateVideoBlock(demux
, track
);
579 block
= CreateSubBlock(demux
, track
);
582 vlc_assert_unreachable();
585 return VLC_DEMUXER_EGENERIC
;
586 block
->i_length
= sys
->step_length
;
587 block
->i_pts
= block
->i_dts
= sys
->pts
;
588 int ret
= es_out_Send(demux
->out
, track
->id
, block
);
589 if (ret
!= VLC_SUCCESS
)
590 return VLC_DEMUXER_EGENERIC
;
592 es_out_SetPCR(demux
->out
, sys
->pts
);
593 sys
->pts
+= sys
->step_length
;
594 if (sys
->pts
> sys
->length
)
595 sys
->pts
= sys
->length
;
597 if (sys
->add_video_track_at
!= VLC_TICK_INVALID
&&
598 sys
->add_video_track_at
<= sys
->pts
)
600 InitVideoTracks(demux
, 0, 1);
601 sys
->add_video_track_at
= VLC_TICK_INVALID
;
603 if (sys
->add_audio_track_at
!= VLC_TICK_INVALID
&&
604 sys
->add_audio_track_at
<= sys
->pts
)
606 InitAudioTracks(demux
, 0, 1);
607 sys
->add_audio_track_at
= VLC_TICK_INVALID
;
609 if (sys
->add_spu_track_at
!= VLC_TICK_INVALID
&&
610 sys
->add_spu_track_at
<= sys
->pts
)
612 InitSubTracks(demux
, 0, 1);
613 sys
->add_spu_track_at
= VLC_TICK_INVALID
;
616 return sys
->pts
== sys
->length
? VLC_DEMUXER_EOF
: VLC_DEMUXER_SUCCESS
;
620 Close(vlc_object_t
*obj
)
622 demux_t
*demux
= (demux_t
*)obj
;
623 struct demux_sys
*sys
= demux
->p_sys
;
625 struct mock_track
*track
;
626 vlc_vector_foreach(track
, &sys
->tracks
)
628 es_out_Del(demux
->out
, track
->id
);
631 vlc_vector_clear(&sys
->tracks
);
635 Open(vlc_object_t
*obj
)
637 demux_t
*demux
= (demux_t
*)obj
;
639 if (demux
->out
== NULL
)
641 struct demux_sys
*sys
= vlc_obj_malloc(obj
, sizeof(*sys
));
647 if (var_LocationParse(obj
, demux
->psz_location
, "mock-") != VLC_SUCCESS
)
650 #define X(var_name, type, module_header_type, getter, default_value) \
651 sys->var_name = getter(obj, "mock-"#var_name);
655 if (sys
->chapter_count
> 0 && sys
->title_count
== 0)
658 const bool length_ok
= sys
->length
>= 0;
659 const bool tracks_ok
= sys
->video_track_count
>= 0 &&
660 sys
->audio_track_count
>= 0 &&
661 sys
->sub_track_count
>= 0;
662 const bool titles_ok
= sys
->title_count
>= 0;
663 const bool chapters_ok
= sys
->chapter_count
>= 0;
664 const bool programs_ok
= sys
->program_count
>= 0;
666 if (!length_ok
|| !tracks_ok
|| !titles_ok
|| !chapters_ok
|| !programs_ok
)
669 msg_Err(demux
, "Invalid length");
671 msg_Err(demux
, "Invalid track count");
673 msg_Err(demux
, "Invalid title count");
675 msg_Err(demux
, "Invalid chapter count");
677 msg_Err(demux
, "Invalid program count");
681 if (sys
->program_count
== 0)
682 sys
->program_count
= 1;
683 size_t track_count
= (sys
->video_track_count
+ sys
->audio_track_count
+
684 sys
->sub_track_count
) * sys
->program_count
;
685 vlc_vector_init(&sys
->tracks
);
688 bool success
= vlc_vector_reserve(&sys
->tracks
, track_count
);
694 for (ssize_t i
= 0; i
< sys
->program_count
; ++i
)
696 ret
= InitVideoTracks(demux
, i
, sys
->video_track_count
);
697 if (ret
!= VLC_SUCCESS
)
700 ret
= InitAudioTracks(demux
, i
, sys
->audio_track_count
);
701 if (ret
!= VLC_SUCCESS
)
704 ret
= InitSubTracks(demux
, i
, sys
->sub_track_count
);
705 if (ret
!= VLC_SUCCESS
)
709 if (sys
->video_track_count
> 0)
710 sys
->step_length
= VLC_TICK_FROM_SEC(1) * sys
->video_frame_rate_base
711 / sys
->video_frame_rate
;
713 sys
->step_length
= VLC_TICK_FROM_MS(100);
715 sys
->pts
= VLC_TICK_0
;
716 sys
->current_title
= 0;
717 sys
->chapter_gap
= sys
->chapter_count
> 0 ?
718 (sys
->length
/ sys
->chapter_count
) : 0;
720 demux
->pf_control
= Control
;
721 demux
->pf_demux
= Demux
;
730 #define X(var_name, type, module_header_type, getter, default_value) \
731 module_header_type("mock-"#var_name, default_value, NULL, NULL, true) \
736 set_description("mock access demux")
737 set_capability("access", 0)
738 set_category(CAT_INPUT
)
739 set_subcategory(SUBCAT_INPUT_ACCESS
)
740 set_callbacks(Open
, Close
)