@@ -33,6 +33,7 @@ use ruffle_video::frame::EncodedFrame;
3333use ruffle_video:: VideoStreamHandle ;
3434use std:: cmp:: max;
3535use std:: io:: { Seek , SeekFrom } ;
36+ use std:: sync:: Arc ;
3637use swf:: { AudioCompression , SoundFormat , VideoCodec , VideoDeblocking } ;
3738use thiserror:: Error ;
3839use url:: Url ;
@@ -176,6 +177,14 @@ pub enum NetStreamType {
176177 /// frame IDs ourselves for various API related purposes.
177178 frame_id : u32 ,
178179 } ,
180+ F4v {
181+ context : Arc < mp4parse:: MediaContext > ,
182+
183+ /// The currently playing video track's stream instance.
184+ video_stream : Option < VideoStreamHandle > ,
185+
186+ frame_id : Option < u32 > ,
187+ } ,
179188}
180189
181190#[ derive( Clone , Debug , Collect ) ]
@@ -514,6 +523,10 @@ impl<'gc> NetStream<'gc> {
514523 . expect ( "FLV reader stream position" ) as usize ;
515524 }
516525
526+ if matches ! ( write. stream_type, Some ( NetStreamType :: F4v { .. } ) ) {
527+ todo ! ( "Seeking in F4V streams" ) ;
528+ }
529+
517530 drop ( write) ;
518531
519532 if let Some ( AvmObject :: Avm2 ( _) ) = self . 0 . read ( ) . avm_object {
@@ -798,8 +811,9 @@ impl<'gc> NetStream<'gc> {
798811 return false ;
799812 }
800813
801- match buffer. get ( 0 ..3 ) {
802- Some ( [ 0x46 , 0x4C , 0x56 ] ) => {
814+ match buffer. get ( 0 ..8 ) {
815+ // Only version 1 is valid.
816+ Some ( [ b'F' , b'L' , b'V' , 1 , _, _, _, _] ) => {
803817 let mut reader = FlvReader :: from_parts ( & buffer, write. offset ) ;
804818 match FlvHeader :: parse ( & mut reader) {
805819 Ok ( header) => {
@@ -816,11 +830,32 @@ impl<'gc> NetStream<'gc> {
816830 Err ( e) => {
817831 //TODO: Fire an error event to AS & stop playing too
818832 tracing:: error!( "FLV header parsing failed: {}" , e) ;
819- write. preload_offset = 3 ;
833+ write. preload_offset = 8 ; // ???
820834 false
821835 }
822836 }
823837 }
838+ // Video File Format Specification Version 10, page 32
839+ // Flash Player expects a valid F4V file to begin with the one of the following top-level boxes:
840+ // - ftyp (see “ftyp box” on page 18)
841+ // - moov (see “moov box” on page 19)
842+ // - mdat (see “mdat box” on page 32)
843+ // And the first 4 bytes are the length of the first box.
844+ Some ( [ _, _, _, _, b'f' , b't' , b'y' , b'p' ] )
845+ | Some ( [ _, _, _, _, b'm' , b'o' , b'o' , b'v' ] )
846+ | Some ( [ _, _, _, _, b'm' , b'd' , b'a' , b't' ] ) => {
847+ println ! ( "F4V" ) ;
848+
849+ let mut cursor = slice. as_cursor ( ) ;
850+ let context = mp4parse:: read_mp4 ( & mut cursor) . unwrap ( ) ;
851+ println ! ( "{:#?}" , context) ;
852+ write. stream_type = Some ( NetStreamType :: F4v {
853+ context : Arc :: new ( context) ,
854+ video_stream : None ,
855+ frame_id : None ,
856+ } ) ;
857+ true
858+ }
824859 Some ( magic) => {
825860 //Unrecognized signature
826861 //TODO: Fire an error event to AS & stop playing too
@@ -1189,6 +1224,168 @@ impl<'gc> NetStream<'gc> {
11891224 }
11901225 }
11911226
1227+ if let Some ( NetStreamType :: F4v {
1228+ context : media_context,
1229+ frame_id,
1230+ video_stream,
1231+ } ) = & mut write. stream_type
1232+ {
1233+ println ! ( "frame id: {:?}, end_time: {}" , frame_id, max_time) ;
1234+
1235+ let should_do_frame;
1236+ let sample_id;
1237+
1238+ match frame_id {
1239+ Some ( frame) => {
1240+ should_do_frame = max_time > * frame as f64 * 1000.0 / 30.0 ;
1241+
1242+ if should_do_frame {
1243+ * frame_id = Some ( * frame + 1 ) ;
1244+ sample_id = frame_id. unwrap ( ) ;
1245+ } else {
1246+ sample_id = 0 ;
1247+ }
1248+ }
1249+ None => {
1250+ should_do_frame = true ;
1251+ sample_id = 0 ;
1252+ * frame_id = Some ( 0 ) ;
1253+ }
1254+ }
1255+
1256+ if should_do_frame {
1257+ let sample_sizes = & media_context
1258+ . tracks
1259+ . get ( 1 )
1260+ . unwrap ( )
1261+ . stsz
1262+ . as_ref ( )
1263+ . unwrap ( )
1264+ . sample_sizes ;
1265+ let chunk_runs = & media_context
1266+ . tracks
1267+ . get ( 1 )
1268+ . unwrap ( )
1269+ . stsc
1270+ . as_ref ( )
1271+ . unwrap ( )
1272+ . samples ;
1273+
1274+ let get_samples_in_chunk = |chunk : u32 | -> u32 {
1275+ let mut result = 0 ;
1276+ for cr in chunk_runs {
1277+ if ( cr. first_chunk - 1 ) > chunk {
1278+ break ;
1279+ }
1280+ result = cr. samples_per_chunk ;
1281+ }
1282+ result
1283+ } ;
1284+
1285+ let chunk_of_sample = |sample : u32 | -> ( u32 , u32 ) {
1286+ let mut sample_accum = 0 ;
1287+ for chunk in 0 .. {
1288+ let samples_in_chunk = get_samples_in_chunk ( chunk) ;
1289+ if sample_accum + samples_in_chunk > sample {
1290+ return ( chunk, sample_accum) ;
1291+ }
1292+ sample_accum += samples_in_chunk;
1293+ }
1294+ ( 0 , 0 )
1295+ } ;
1296+
1297+ let ( chunk, first_sample_in_chunk) = chunk_of_sample ( sample_id) ;
1298+ let chunk_offsets = & media_context
1299+ . tracks
1300+ . get ( 1 )
1301+ . unwrap ( )
1302+ . stco
1303+ . as_ref ( )
1304+ . unwrap ( )
1305+ . offsets ;
1306+
1307+ let mut offs = chunk_offsets[ chunk as usize ] as usize ;
1308+
1309+ for sam in first_sample_in_chunk..sample_id {
1310+ offs += sample_sizes[ sam as usize ] as usize ;
1311+ }
1312+
1313+ let siz = sample_sizes[ sample_id as usize ] as usize ;
1314+
1315+ println ! ( "offs: {}, siz: {}" , offs, siz) ;
1316+ let s = buffer;
1317+
1318+ let encoded_frame = EncodedFrame {
1319+ codec : VideoCodec :: H263 , // TODO
1320+ data : s[ offs..offs + siz] . as_ref ( ) ,
1321+ frame_id : frame_id. unwrap ( ) ,
1322+ } ;
1323+
1324+ let video_handle: VideoStreamHandle = match video_stream {
1325+ Some ( stream) => * stream,
1326+ None => {
1327+ match context. video . register_video_stream (
1328+ 1 ,
1329+ ( 8 , 8 ) ,
1330+ VideoCodec :: H263 , // TODO
1331+ VideoDeblocking :: UseVideoPacketValue ,
1332+ ) {
1333+ Ok ( new_handle) => {
1334+ * video_stream = Some ( new_handle) ;
1335+
1336+ new_handle
1337+ }
1338+ Err ( e) => {
1339+ tracing:: error!(
1340+ "Got error when registring FLV video stream: {}" ,
1341+ e
1342+ ) ;
1343+ return ; //TODO: This originally breaks and halts tag processing
1344+ }
1345+ }
1346+ }
1347+ } ;
1348+
1349+ let mdct = media_context. clone ( ) ;
1350+ let trk = mdct. tracks . get ( 1 ) . unwrap ( ) ;
1351+ let stsd = trk. stsd . as_ref ( ) . unwrap ( ) ;
1352+ let descs = stsd. descriptions . get ( 0 ) . unwrap ( ) ;
1353+
1354+ match descs {
1355+ mp4parse:: SampleEntry :: Video ( video) => match & video. codec_specific {
1356+ mp4parse:: VideoCodecSpecific :: AVCConfig ( avcconf) => {
1357+ let prel_frame = EncodedFrame {
1358+ codec : VideoCodec :: H263 , // TODO: H264
1359+ data : avcconf. as_slice ( ) ,
1360+ frame_id : frame_id. unwrap ( ) ,
1361+ } ;
1362+ if * frame_id == Some ( 0 ) {
1363+ println ! ( "preloading avc config" ) ;
1364+ // TODO: this is a misuse of the API, should add/use a dedicated
1365+ // configuration method for this
1366+ let _ = context
1367+ . video
1368+ . preload_video_stream_frame ( video_handle, prel_frame) ;
1369+ }
1370+ }
1371+ mp4parse:: VideoCodecSpecific :: VPxConfig ( _) => todo ! ( ) ,
1372+ mp4parse:: VideoCodecSpecific :: AV1Config ( _) => todo ! ( ) ,
1373+ mp4parse:: VideoCodecSpecific :: ESDSConfig ( _) => todo ! ( ) ,
1374+ mp4parse:: VideoCodecSpecific :: H263Config ( _) => todo ! ( ) ,
1375+ } ,
1376+ mp4parse:: SampleEntry :: Audio ( _) => todo ! ( ) ,
1377+ mp4parse:: SampleEntry :: Unknown => todo ! ( ) ,
1378+ }
1379+
1380+ write. last_decoded_bitmap = Some (
1381+ context
1382+ . video
1383+ . decode_video_stream_frame ( video_handle, encoded_frame, context. renderer )
1384+ . unwrap ( ) ,
1385+ ) ;
1386+ }
1387+ }
1388+
11921389 write. stream_time = last_tag_time;
11931390 if let Err ( e) = self . commit_sound_stream ( context, & mut write) {
11941391 //TODO: Fire an error event at AS.
0 commit comments