@@ -35,7 +35,19 @@ defmodule Membrane.MP4.Demuxer.ISOM do
35
35
% Membrane.Opus { self_delimiting?: false }
36
36
) ,
37
37
availability: :on_request ,
38
- flow_control: :auto
38
+ options: [
39
+ codec: [
40
+ spec: Membrane.H264 | Membrane.H265 | Membrane.Opus | Membrane.AAC | nil ,
41
+ default: nil ,
42
+ description: """
43
+ Specifies, what kind of codec can be handled by a pad.
44
+
45
+ Pad with `:codec` option set to `:all` can handle all codecs.
46
+
47
+ Defaults to `:all`
48
+ """
49
+ ]
50
+ ]
39
51
40
52
def_options optimize_for_non_fast_start?: [
41
53
default: false ,
@@ -82,7 +94,9 @@ defmodule Membrane.MP4.Demuxer.ISOM do
82
94
boxes_size: 0 ,
83
95
mdat_beginning: nil ,
84
96
mdat_size: nil ,
85
- mdat_header_size: nil
97
+ mdat_header_size: nil ,
98
+ track_to_pad_id: % { } ,
99
+ track_notifications_sent?: false
86
100
}
87
101
88
102
{ [ ] , state }
@@ -147,7 +161,7 @@ defmodule Membrane.MP4.Demuxer.ISOM do
147
161
state . partial <> buffer . payload
148
162
)
149
163
150
- buffers = get_buffer_actions ( samples )
164
+ buffers = get_buffer_actions ( samples , state )
151
165
152
166
{ buffers , % { state | samples_info: samples_info , partial: rest } }
153
167
end
@@ -356,22 +370,26 @@ defmodule Membrane.MP4.Demuxer.ISOM do
356
370
357
371
state = % { state | samples_info: samples_info , partial: rest }
358
372
373
+ state = match_tracks_with_pads ( ctx , state )
374
+
359
375
all_pads_connected? = all_pads_connected? ( ctx , state )
360
376
361
377
{ buffers , state } =
362
378
if all_pads_connected? do
363
- { get_buffer_actions ( samples ) , state }
379
+ { get_buffer_actions ( samples , state ) , state }
364
380
else
365
381
{ [ ] , store_samples ( state , samples ) }
366
382
end
367
383
368
384
notifications = get_track_notifications ( state )
385
+
369
386
stream_format = if all_pads_connected? , do: get_stream_format ( state ) , else: [ ]
370
387
371
388
state =
372
389
% {
373
390
state
374
- | all_pads_connected?: all_pads_connected?
391
+ | all_pads_connected?: all_pads_connected? ,
392
+ track_notifications_sent?: true
375
393
}
376
394
|> update_fsm_state ( )
377
395
@@ -385,9 +403,10 @@ defmodule Membrane.MP4.Demuxer.ISOM do
385
403
end )
386
404
end
387
405
388
- defp get_buffer_actions ( samples ) do
406
+ defp get_buffer_actions ( samples , state ) do
389
407
Enum . map ( samples , fn { buffer , track_id } ->
390
- { :buffer , { Pad . ref ( :output , track_id ) , buffer } }
408
+ pad_id = state . track_to_pad_id [ track_id ]
409
+ { :buffer , { Pad . ref ( :output , pad_id ) , buffer } }
391
410
end )
392
411
end
393
412
@@ -398,12 +417,59 @@ defmodule Membrane.MP4.Demuxer.ISOM do
398
417
end
399
418
end
400
419
420
+ defp match_tracks_with_pads ( ctx , state ) do
421
+ codec_to_pads_data =
422
+ ctx . pads
423
+ |> Map . values ( )
424
+ |> Enum . reject ( fn % { ref: pad_ref } -> pad_ref == :input end )
425
+ |> Enum . group_by ( & & 1 . options . codec )
426
+
427
+ { track_to_pad_id , empty_codec_to_pads_data } =
428
+ state . samples_info . sample_tables
429
+ |> Enum . map_reduce ( codec_to_pads_data , fn { track_id , table } , codec_to_pads_data ->
430
+ % track_codec { } = table . sample_description
431
+
432
+ case codec_to_pads_data [ track_codec ] do
433
+ [ pad_data | tail ] ->
434
+ codec_to_pads_data = Map . put ( codec_to_pads_data , track_codec , tail )
435
+ Pad . ref ( :output , pad_id ) = pad_data . ref
436
+ { { track_id , pad_id } , codec_to_pads_data }
437
+
438
+ _no_pad ->
439
+ { { track_id , nil } , codec_to_pads_data }
440
+ end
441
+ end )
442
+
443
+ max_pad_id =
444
+ track_to_pad_id
445
+ |> Enum . flat_map ( fn { _track , pad_id } -> if pad_id != nil , do: [ pad_id ] , else: [ ] end )
446
+ |> Enum . max ( & >= / 2 , fn -> 0 end )
447
+
448
+ { track_to_pad_id , _max_pad_id } =
449
+ track_to_pad_id
450
+ |> Enum . map_reduce ( max_pad_id , fn
451
+ { track_id , nil } , max_pad_id -> { { track_id , max_pad_id + 1 } , max_pad_id + 1 }
452
+ track_pad_pair , max_pad_id -> { track_pad_pair , max_pad_id }
453
+ end )
454
+
455
+ raise? =
456
+ empty_codec_to_pads_data
457
+ |> Map . values ( )
458
+ |> Enum . any? ( & ( & 1 != [ ] ) )
459
+
460
+ if raise? do
461
+ "dupaaaa"
462
+ end
463
+
464
+ % { state | track_to_pad_id: Map . new ( track_to_pad_id ) }
465
+ end
466
+
401
467
defp get_track_notifications ( state ) do
402
468
new_tracks =
403
469
state . samples_info . sample_tables
404
470
|> Enum . map ( fn { track_id , table } ->
405
- content = table . sample_description
406
- { track_id , content }
471
+ pad_id = state . track_to_pad_id [ track_id ]
472
+ { pad_id , table . sample_description }
407
473
end )
408
474
409
475
[ { :notify_parent , { :new_tracks , new_tracks } } ]
@@ -412,7 +478,8 @@ defmodule Membrane.MP4.Demuxer.ISOM do
412
478
defp get_stream_format ( state ) do
413
479
state . samples_info . sample_tables
414
480
|> Enum . map ( fn { track_id , table } ->
415
- { :stream_format , { Pad . ref ( :output , track_id ) , table . sample_description } }
481
+ pad_id = state . track_to_pad_id [ track_id ]
482
+ { :stream_format , { Pad . ref ( :output , pad_id ) , table . sample_description } }
416
483
end )
417
484
end
418
485
@@ -425,7 +492,8 @@ defmodule Membrane.MP4.Demuxer.ISOM do
425
492
raise "All tracks have corresponding pad already connected"
426
493
end
427
494
428
- def handle_pad_added ( Pad . ref ( :output , _track_id ) , ctx , state ) do
495
+ def handle_pad_added ( Pad . ref ( :output , _track_id ) = pad_ref , ctx , state ) do
496
+ :ok = validate_pad_codec! ( pad_ref , ctx . pad_options . codec , ctx , state )
429
497
all_pads_connected? = all_pads_connected? ( ctx , state )
430
498
431
499
{ actions , state } =
@@ -444,6 +512,53 @@ defmodule Membrane.MP4.Demuxer.ISOM do
444
512
{ actions , state }
445
513
end
446
514
515
+ defp validate_pad_codec! ( pad_ref , pad_codec , ctx , state ) do
516
+ allowed_codecs = [ nil , Membrane.H264 , Membrane.H265 , Membrane.Opus , Membrane.AAC ]
517
+
518
+ if pad_codec not in allowed_codecs do
519
+ raise """
520
+ Pad #{ inspect ( pad_ref ) } has :codec option set to #{ inspect ( pad_codec ) } , while it has te be one of \
521
+ #{ List . delete ( allowed_codecs , nil ) |> inspect ( ) } or be unspecified.
522
+ """
523
+ end
524
+
525
+ if not state . track_notifications_sent? and
526
+ Enum . count ( ctx . pads , & match? ( { Pad . ref ( :output , _id ) , % { options: % { codec: nil } } } , & 1 ) ) > 1 do
527
+ raise """
528
+ If pads are linked before :new_tracks notifications and there are more then one of them, pad option \
529
+ :codec has to be specyfied.
530
+ """
531
+ end
532
+
533
+ if state . track_notifications_sent? do
534
+ Pad . ref ( :output , pad_id ) = pad_ref
535
+
536
+ related_track =
537
+ state . track_to_pad_id
538
+ |> Map . keys ( )
539
+ |> Enum . find ( & ( state . track_to_pad_id [ & 1 ] == pad_id ) )
540
+
541
+ if related_track == nil do
542
+ raise """
543
+ Pad #{ inspect ( pad_ref ) } doesn't have a related track. If you link pads after #{ inspect ( __MODULE__ ) } \
544
+ sent the track notification, you have to restrict yourself to the pad occuring in this notification. \
545
+ Tracks, that occured in this notification are: #{ Map . keys ( state . track_to_pad_id ) |> inspect ( ) }
546
+ """
547
+ end
548
+
549
+ track_codec = state . samples_info . sample_tables [ related_track ] . sample_description
550
+
551
+ if pad_codec != nil and track_codec != pad_codec do
552
+ raise """
553
+ Pad option :codec must point on the related track codec or be equal nil, but pad #{ inspect ( pad_ref ) } \
554
+ codec is #{ inspect ( pad_codec ) } , while the related track codec is #{ inspect ( track_codec ) }
555
+ """
556
+ end
557
+ end
558
+
559
+ :ok
560
+ end
561
+
447
562
@ impl true
448
563
def handle_end_of_stream ( :input , _ctx , % { all_pads_connected?: false } = state ) do
449
564
{ [ ] , % { state | end_of_stream?: true } }
@@ -465,11 +580,11 @@ defmodule Membrane.MP4.Demuxer.ISOM do
465
580
_pad -> [ ]
466
581
end )
467
582
468
- Enum . each ( pads , fn pad ->
469
- if pad not in tracks do
470
- raise "An output pad connected with #{ pad } id, however no matching track exists"
471
- end
472
- end )
583
+ # Enum.each(pads, fn pad ->
584
+ # if pad not in tracks do
585
+ # raise "An output pad connected with #{pad} id, however no matching track exists"
586
+ # end
587
+ # end)
473
588
474
589
Range . size ( tracks ) == length ( pads )
475
590
end
@@ -482,7 +597,8 @@ defmodule Membrane.MP4.Demuxer.ISOM do
482
597
|> Enum . reverse ( )
483
598
|> Enum . map ( fn { buffer , ^ track_id } -> buffer end )
484
599
485
- { :buffer , { Pad . ref ( :output , track_id ) , buffers } }
600
+ pad_id = state . track_to_pad_id [ track_id ]
601
+ { :buffer , { Pad . ref ( :output , pad_id ) , buffers } }
486
602
end )
487
603
488
604
state = % { state | buffered_samples: % { } }
0 commit comments