1
1
use std:: {
2
+ collections:: VecDeque ,
2
3
fs:: File ,
3
4
io:: { BufWriter , Seek , SeekFrom , Write } ,
4
5
num:: NonZeroU32 ,
5
6
path:: Path ,
7
+ sync:: Arc ,
6
8
time:: { Duration , SystemTime } ,
7
9
} ;
8
10
@@ -17,11 +19,13 @@ use shiguredo_mp4::{
17
19
} ;
18
20
19
21
use crate :: {
20
- audio:: { AudioData , AudioDataReceiver } ,
22
+ audio:: AudioData ,
21
23
layout:: { Layout , Resolution } ,
24
+ media:: { MediaSample , MediaStreamId } ,
22
25
mixer_audio:: MIXED_AUDIO_DATA_DURATION ,
23
- stats:: { Mp4WriterStats , Seconds } ,
24
- video:: { VideoFrame , VideoFrameReceiver } ,
26
+ processor:: { MediaProcessor , MediaProcessorInput , MediaProcessorOutput , MediaProcessorSpec } ,
27
+ stats:: { Mp4WriterStats , ProcessorStats , Seconds } ,
28
+ video:: VideoFrame ,
25
29
} ;
26
30
27
31
// Hisui では出力 MP4 のタイムスケールはマイクロ秒固定にする
@@ -42,8 +46,10 @@ pub struct Mp4Writer {
42
46
video_chunks : Vec < Chunk > ,
43
47
audio_sample_entry : Option < SampleEntry > ,
44
48
video_sample_entry : Option < SampleEntry > ,
45
- input_audio_rx : Option < AudioDataReceiver > ,
46
- input_video_rx : Option < VideoFrameReceiver > ,
49
+ input_audio_stream_id : Option < MediaStreamId > ,
50
+ input_video_stream_id : Option < MediaStreamId > ,
51
+ input_audio_queue : VecDeque < Arc < AudioData > > ,
52
+ input_video_queue : VecDeque < Arc < VideoFrame > > ,
47
53
finalize_time : Mp4FileTime ,
48
54
appending_video_chunk : bool ,
49
55
stats : Mp4WriterStats ,
@@ -54,8 +60,8 @@ impl Mp4Writer {
54
60
pub fn new < P : AsRef < Path > > (
55
61
path : P ,
56
62
layout : & Layout ,
57
- input_audio_rx : AudioDataReceiver ,
58
- input_video_rx : VideoFrameReceiver ,
63
+ input_audio_stream_id : Option < MediaStreamId > ,
64
+ input_video_stream_id : Option < MediaStreamId > ,
59
65
) -> orfail:: Result < Self > {
60
66
let file = std:: fs:: OpenOptions :: new ( )
61
67
. create ( true )
@@ -74,8 +80,10 @@ impl Mp4Writer {
74
80
audio_sample_entry : None ,
75
81
video_sample_entry : None ,
76
82
finalize_time : Mp4FileTime :: from_unix_time ( Duration :: ZERO ) ,
77
- input_audio_rx : layout. has_audio ( ) . then_some ( input_audio_rx) ,
78
- input_video_rx : layout. has_video ( ) . then_some ( input_video_rx) ,
83
+ input_audio_stream_id,
84
+ input_video_stream_id,
85
+ input_audio_queue : VecDeque :: new ( ) ,
86
+ input_video_queue : VecDeque :: new ( ) ,
79
87
appending_video_chunk : true ,
80
88
stats : Mp4WriterStats :: default ( ) ,
81
89
} ;
@@ -92,33 +100,16 @@ impl Mp4Writer {
92
100
& self . stats
93
101
}
94
102
95
- /// 新しい入力(合成後の映像と音声)を待機して、それの出力ファイルへの書き込みを行う
96
- ///
97
- /// 結果は現在の書き込み位置を示すタイムスタンプで、全ての書き込みが完了した場合には `Ok(None)` が返される。
98
- pub fn poll ( & mut self ) -> orfail:: Result < Option < Duration > > {
99
- let ( audio, video) = self . peek_input_audio_and_video ( ) ;
100
- let audio_timestamp = audio. map ( |x| x. timestamp ) ;
101
- let video_timestamp = video. map ( |x| x. timestamp ) ;
102
-
103
- let ( result, elapsed) = Seconds :: elapsed ( || {
104
- self . handle_next_audio_and_video ( audio_timestamp, video_timestamp)
105
- . or_fail ( )
106
- } ) ;
107
- self . stats . total_processing_seconds . add ( elapsed) ;
108
-
109
- result
110
- }
111
-
112
103
fn handle_next_audio_and_video (
113
104
& mut self ,
114
105
audio_timestamp : Option < Duration > ,
115
106
video_timestamp : Option < Duration > ,
116
- ) -> orfail:: Result < Option < Duration > > {
117
- match ( audio_timestamp, video_timestamp) {
107
+ ) -> orfail:: Result < bool > {
108
+ match ( audio_timestamp, video_timestamp) {
118
109
( None , None ) => {
119
110
// 全部の入力の処理が完了した
120
111
self . finalize ( ) . or_fail ( ) ?;
121
- return Ok ( None ) ;
112
+ return Ok ( false ) ;
122
113
}
123
114
( None , Some ( _) ) => {
124
115
// 残りは映像のみ
@@ -149,30 +140,19 @@ impl Mp4Writer {
149
140
}
150
141
}
151
142
152
- // 進捗(現在のタイムスタンプ)を呼び出し元に返す
153
- Ok ( Some ( self . current_duration ( ) ) )
143
+ Ok ( true )
154
144
}
155
145
156
- fn current_duration ( & self ) -> Duration {
146
+ pub fn current_duration ( & self ) -> Duration {
157
147
self . stats
158
148
. total_audio_track_seconds
159
149
. get_duration ( )
160
150
. max ( self . stats . total_video_track_seconds . get_duration ( ) )
161
151
}
162
152
163
- fn peek_input_audio_and_video ( & mut self ) -> ( Option < & AudioData > , Option < & VideoFrame > ) {
164
- let audio = self . input_audio_rx . as_mut ( ) . and_then ( |rx| rx. peek ( ) ) ;
165
- let video = self . input_video_rx . as_mut ( ) . and_then ( |rx| rx. peek ( ) ) ;
166
- ( audio, video)
167
- }
168
-
169
153
fn append_video_frame ( & mut self , new_chunk : bool ) -> orfail:: Result < ( ) > {
170
154
// 次の入力を取り出す(これは常に成功する)
171
- let frame = self
172
- . input_video_rx
173
- . as_mut ( )
174
- . and_then ( |rx| rx. recv ( ) )
175
- . or_fail ( ) ?;
155
+ let frame = self . input_video_queue . pop_front ( ) . or_fail ( ) ?;
176
156
177
157
if self . stats . video_codec . get ( ) . is_none ( )
178
158
&& let Some ( name) = frame. format . codec_name ( )
@@ -184,7 +164,7 @@ impl Mp4Writer {
184
164
// サンプルエントリーは最初に一回だけ存在する
185
165
if self . video_sample_entry . is_none ( ) {
186
166
frame. sample_entry . is_some ( ) . or_fail ( ) ?;
187
- self . video_sample_entry = frame. sample_entry ;
167
+ self . video_sample_entry = frame. sample_entry . clone ( ) ;
188
168
} else {
189
169
frame. sample_entry . is_none ( ) . or_fail ( ) ?;
190
170
}
@@ -223,11 +203,7 @@ impl Mp4Writer {
223
203
224
204
fn append_audio_data ( & mut self , new_chunk : bool ) -> orfail:: Result < ( ) > {
225
205
// 次の入力を取り出す(これは常に成功する)
226
- let data = self
227
- . input_audio_rx
228
- . as_mut ( )
229
- . and_then ( |rx| rx. recv ( ) )
230
- . or_fail ( ) ?;
206
+ let data = self . input_audio_queue . pop_front ( ) . or_fail ( ) ?;
231
207
232
208
if self . stats . audio_codec . get ( ) . is_none ( )
233
209
&& let Some ( name) = data. format . codec_name ( )
@@ -239,7 +215,7 @@ impl Mp4Writer {
239
215
// サンプルエントリーは最初に一回だけ存在する
240
216
if self . audio_sample_entry . is_none ( ) {
241
217
data. sample_entry . is_some ( ) . or_fail ( ) ?;
242
- self . audio_sample_entry = data. sample_entry ;
218
+ self . audio_sample_entry = data. sample_entry . clone ( ) ;
243
219
} else {
244
220
data. sample_entry . is_none ( ) . or_fail ( ) ?;
245
221
}
@@ -743,6 +719,75 @@ impl Mp4Writer {
743
719
}
744
720
}
745
721
722
+ impl MediaProcessor for Mp4Writer {
723
+ fn spec ( & self ) -> MediaProcessorSpec {
724
+ MediaProcessorSpec {
725
+ input_stream_ids : self
726
+ . input_audio_stream_id
727
+ . into_iter ( )
728
+ . chain ( self . input_video_stream_id )
729
+ . collect ( ) ,
730
+ output_stream_ids : Vec :: new ( ) ,
731
+ stats : ProcessorStats :: Mp4Writer ( self . stats . clone ( ) ) ,
732
+ }
733
+ }
734
+
735
+ fn process_input ( & mut self , input : MediaProcessorInput ) -> orfail:: Result < ( ) > {
736
+ match input. sample {
737
+ Some ( MediaSample :: Audio ( sample) )
738
+ if Some ( input. stream_id ) == self . input_audio_stream_id =>
739
+ {
740
+ self . input_audio_queue . push_back ( sample) ;
741
+ }
742
+ None if Some ( input. stream_id ) == self . input_audio_stream_id => {
743
+ self . input_audio_stream_id = None ;
744
+ }
745
+ Some ( MediaSample :: Video ( sample) )
746
+ if Some ( input. stream_id ) == self . input_video_stream_id =>
747
+ {
748
+ self . input_video_queue . push_back ( sample) ;
749
+ }
750
+ None if Some ( input. stream_id ) == self . input_video_stream_id => {
751
+ self . input_video_stream_id = None ;
752
+ }
753
+ _ => return Err ( orfail:: Failure :: new ( "BUG: unexpected input stream" ) ) ,
754
+ }
755
+ Ok ( ( ) )
756
+ }
757
+
758
+ fn process_output ( & mut self ) -> orfail:: Result < MediaProcessorOutput > {
759
+ loop {
760
+ if let Some ( id) = self . input_video_stream_id
761
+ && self . input_video_queue . is_empty ( )
762
+ {
763
+ return Ok ( MediaProcessorOutput :: Pending {
764
+ awaiting_stream_id : id,
765
+ } ) ;
766
+ } else if let Some ( id) = self . input_audio_stream_id
767
+ && self . input_audio_queue . is_empty ( )
768
+ {
769
+ return Ok ( MediaProcessorOutput :: Pending {
770
+ awaiting_stream_id : id,
771
+ } ) ;
772
+ }
773
+
774
+ let audio_timestamp = self . input_audio_queue . front ( ) . map ( |x| x. timestamp ) ;
775
+ let video_timestamp = self . input_video_queue . front ( ) . map ( |x| x. timestamp ) ;
776
+
777
+ let ( result, elapsed) = Seconds :: elapsed ( || {
778
+ self . handle_next_audio_and_video ( audio_timestamp, video_timestamp)
779
+ . or_fail ( )
780
+ } ) ;
781
+
782
+ self . stats . total_processing_seconds . add ( elapsed) ;
783
+
784
+ if !result? {
785
+ return Ok ( MediaProcessorOutput :: Finished ) ;
786
+ }
787
+ }
788
+ }
789
+ }
790
+
746
791
#[ derive( Debug ) ]
747
792
struct Chunk {
748
793
offset : u64 ,
0 commit comments