@@ -4,16 +4,15 @@ defmodule Membrane.MP4.Muxer.ISOM do
44 """
55 use Membrane.Filter
66
7- alias Membrane . { Buffer , File , MP4 , RemoteStream , Time }
7+ alias Membrane . { Buffer , File , MP4 , RemoteStream , Time , TimestampQueue }
88 alias Membrane.MP4 . { Container , FileTypeBox , MediaDataBox , MovieBox , Track }
99
1010 @ ftyp FileTypeBox . assemble ( "isom" , [ "isom" , "iso2" , "avc1" , "mp41" ] )
1111 @ ftyp_size @ ftyp |> Container . serialize! ( ) |> byte_size ( )
1212 @ mdat_header_size 8
1313
1414 def_input_pad :input ,
15- flow_control: :manual ,
16- demand_unit: :buffers ,
15+ flow_control: :auto ,
1716 accepted_format:
1817 any_of (
1918 % Membrane.AAC { config: { :esds , _esds } } ,
@@ -31,7 +30,7 @@ defmodule Membrane.MP4.Muxer.ISOM do
3130
3231 def_output_pad :output ,
3332 accepted_format: % RemoteStream { type: :bytestream , content_format: MP4 } ,
34- flow_control: :manual
33+ flow_control: :auto
3534
3635 def_options fast_start: [
3736 spec: boolean ( ) ,
@@ -61,27 +60,36 @@ defmodule Membrane.MP4.Muxer.ISOM do
6160
6261 @ impl true
6362 def handle_init ( _ctx , options ) do
63+ queue =
64+ TimestampQueue . new (
65+ chunk_duration: options . chunk_duration ,
66+ pause_demand_boundary: 3 * options . chunk_duration ,
67+ pause_demand_boundary_unit: :time
68+ )
69+
6470 state =
6571 options
6672 |> Map . from_struct ( )
6773 |> Map . merge ( % {
6874 mdat_size: 0 ,
6975 next_track_id: 1 ,
7076 pad_order: [ ] ,
71- pad_to_track: % { }
77+ pad_to_track: % { } ,
78+ queue: queue
7279 } )
7380
7481 { [ ] , state }
7582 end
7683
7784 @ impl true
78- def handle_pad_added ( Pad . ref ( :input , pad_ref ) , _ctx , state ) do
85+ def handle_pad_added ( Pad . ref ( :input , pad_ref ) = pad , _ctx , state ) do
7986 { track_id , state } = Map . get_and_update! ( state , :next_track_id , & { & 1 , & 1 + 1 } )
8087
8188 state =
8289 state
8390 |> Map . update! ( :pad_order , & [ pad_ref | & 1 ] )
8491 |> put_in ( [ :pad_to_track , pad_ref ] , track_id )
92+ |> Map . update! ( :queue , & TimestampQueue . register_pad ( & 1 , pad ) )
8593
8694 { [ ] , state }
8795 end
@@ -126,34 +134,59 @@ defmodule Membrane.MP4.Muxer.ISOM do
126134 end
127135
128136 @ impl true
129- def handle_demand ( :output , _size , :buffers , _ctx , state ) do
130- next_ref = hd ( state . pad_order )
137+ def handle_event ( Pad . ref ( :input , _pad_ref ) = pad , event , _ctx , state ) do
138+ state . queue
139+ |> TimestampQueue . push_event ( pad , event )
140+ |> TimestampQueue . pop_available_items ( )
141+ |> handle_queue_output ( state )
142+ end
143+
144+ @ impl true
145+ def handle_event ( :output , event , _ctx , state ) do
146+ { [ forward: event ] , state }
147+ end
131148
132- { [ demand: { Pad . ref ( :input , next_ref ) , 1 } ] , state }
149+ @ impl true
150+ def handle_buffer ( Pad . ref ( :input , _pad_ref ) = pad , buffer , _ctx , state ) do
151+ state . queue
152+ |> TimestampQueue . push_buffer_and_pop_available_items ( pad , buffer )
153+ |> handle_queue_output ( state )
133154 end
134155
135156 @ impl true
136- def handle_buffer ( Pad . ref ( :input , pad_ref ) , buffer , _ctx , state ) do
157+ def handle_end_of_stream ( Pad . ref ( :input , _pad_ref ) = pad , _ctx , state ) do
158+ state . queue
159+ |> TimestampQueue . push_end_of_stream ( pad )
160+ |> TimestampQueue . pop_available_items ( )
161+ |> handle_queue_output ( state )
162+ end
163+
164+ defp handle_queue_output ( { suggested_actions , batch , queue } , state ) do
165+ { actions , state } = Enum . flat_map_reduce ( batch , state , & handle_queue_item / 2 )
166+ { suggested_actions ++ actions , % { state | queue: queue } }
167+ end
168+
169+ defp handle_queue_item ( { _pad_ref , { :event , event } } , state ) do
170+ { [ event: { :output , event } ] , state }
171+ end
172+
173+ defp handle_queue_item ( { Pad . ref ( :input , pad_ref ) , { :buffer , buffer } } , state ) do
137174 # In case DTS is not set, use PTS. This is the case for audio tracks or H264 originated
138175 # from an RTP stream. ISO base media file format specification uses DTS for calculating
139176 # decoding deltas, and so is the implementation of sample table in this plugin.
140177 buffer = % Buffer { buffer | dts: Buffer . get_dts_or_pts ( buffer ) }
141178
142- { maybe_buffer , state } =
143- state
144- |> update_in ( [ :pad_to_track , pad_ref ] , & Track . store_sample ( & 1 , buffer ) )
145- |> maybe_flush_chunk ( pad_ref )
146-
147- { maybe_buffer ++ [ redemand: :output ] , state }
179+ state
180+ |> update_in ( [ :pad_to_track , pad_ref ] , & Track . store_sample ( & 1 , buffer ) )
181+ |> maybe_flush_chunk ( pad_ref )
148182 end
149183
150- @ impl true
151- def handle_end_of_stream ( Pad . ref ( :input , pad_ref ) , _ctx , state ) do
184+ defp handle_queue_item ( { Pad . ref ( :input , pad_ref ) , :end_of_stream } , state ) do
152185 { buffer , state } = do_flush_chunk ( state , pad_ref )
153186 state = Map . update! ( state , :pad_order , & List . delete ( & 1 , pad_ref ) )
154187
155188 if state . pad_order != [ ] do
156- { buffer ++ [ redemand: :output ] , state }
189+ { buffer , state }
157190 else
158191 actions = finalize_mp4 ( state )
159192 { buffer ++ actions ++ [ end_of_stream: :output ] , state }
0 commit comments