@@ -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 ;
@@ -177,6 +178,14 @@ pub enum NetStreamType {
177178 /// frame IDs ourselves for various API related purposes.
178179 frame_id : u32 ,
179180 } ,
181+ F4v {
182+ context : Arc < mp4parse:: MediaContext > ,
183+
184+ /// The currently playing video track's stream instance.
185+ video_stream : Option < VideoStreamHandle > ,
186+
187+ frame_id : Option < u32 > ,
188+ } ,
180189}
181190
182191#[ derive( Clone , Debug , Collect ) ]
@@ -525,6 +534,10 @@ impl<'gc> NetStream<'gc> {
525534 . expect ( "FLV reader stream position" ) as usize ;
526535 }
527536
537+ if matches ! ( write. stream_type, Some ( NetStreamType :: F4v { .. } ) ) {
538+ todo ! ( "Seeking in F4V streams" ) ;
539+ }
540+
528541 drop ( write) ;
529542
530543 if let Some ( AvmObject :: Avm2 ( _) ) = self . 0 . read ( ) . avm_object {
@@ -809,8 +822,9 @@ impl<'gc> NetStream<'gc> {
809822 return false ;
810823 }
811824
812- match buffer. get ( 0 ..3 ) {
813- Some ( [ 0x46 , 0x4C , 0x56 ] ) => {
825+ match buffer. get ( 0 ..8 ) {
826+ // Only version 1 is valid.
827+ Some ( [ b'F' , b'L' , b'V' , 1 , _, _, _, _] ) => {
814828 let mut reader = FlvReader :: from_parts ( & buffer, write. offset ) ;
815829 match FlvHeader :: parse ( & mut reader) {
816830 Ok ( header) => {
@@ -827,11 +841,32 @@ impl<'gc> NetStream<'gc> {
827841 Err ( e) => {
828842 //TODO: Fire an error event to AS & stop playing too
829843 tracing:: error!( "FLV header parsing failed: {}" , e) ;
830- write. preload_offset = 3 ;
844+ write. preload_offset = 8 ; // ???
831845 false
832846 }
833847 }
834848 }
849+ // Video File Format Specification Version 10, page 32
850+ // Flash Player expects a valid F4V file to begin with the one of the following top-level boxes:
851+ // - ftyp (see “ftyp box” on page 18)
852+ // - moov (see “moov box” on page 19)
853+ // - mdat (see “mdat box” on page 32)
854+ // And the first 4 bytes are the length of the first box.
855+ Some ( [ _, _, _, _, b'f' , b't' , b'y' , b'p' ] )
856+ | Some ( [ _, _, _, _, b'm' , b'o' , b'o' , b'v' ] )
857+ | Some ( [ _, _, _, _, b'm' , b'd' , b'a' , b't' ] ) => {
858+ println ! ( "F4V" ) ;
859+
860+ let mut cursor = slice. as_cursor ( ) ;
861+ let context = mp4parse:: read_mp4 ( & mut cursor) . unwrap ( ) ;
862+ println ! ( "{:#?}" , context) ;
863+ write. stream_type = Some ( NetStreamType :: F4v {
864+ context : Arc :: new ( context) ,
865+ video_stream : None ,
866+ frame_id : None ,
867+ } ) ;
868+ true
869+ }
835870 Some ( magic) => {
836871 //Unrecognized signature
837872 //TODO: Fire an error event to AS & stop playing too
@@ -1234,6 +1269,163 @@ impl<'gc> NetStream<'gc> {
12341269 }
12351270 }
12361271
1272+ if let Some ( NetStreamType :: F4v {
1273+ context : media_context,
1274+ frame_id,
1275+ video_stream,
1276+ } ) = & mut write. stream_type
1277+ {
1278+ println ! ( "frame id: {:?}, end_time: {}" , frame_id, max_time) ;
1279+
1280+ let should_do_frame;
1281+ let sample_id;
1282+
1283+ match frame_id {
1284+ Some ( frame) => {
1285+ should_do_frame = max_time > * frame as f64 * 1000.0 / 30.0 ;
1286+
1287+ if should_do_frame {
1288+ * frame_id = Some ( * frame + 1 ) ;
1289+ sample_id = frame_id. unwrap ( ) ;
1290+ } else {
1291+ sample_id = 0 ;
1292+ }
1293+ }
1294+ None => {
1295+ should_do_frame = true ;
1296+ sample_id = 0 ;
1297+ * frame_id = Some ( 0 ) ;
1298+ }
1299+ }
1300+
1301+ if should_do_frame {
1302+ let sample_sizes = & media_context
1303+ . tracks
1304+ . get ( 1 )
1305+ . unwrap ( )
1306+ . stsz
1307+ . as_ref ( )
1308+ . unwrap ( )
1309+ . sample_sizes ;
1310+ let chunk_runs = & media_context
1311+ . tracks
1312+ . get ( 1 )
1313+ . unwrap ( )
1314+ . stsc
1315+ . as_ref ( )
1316+ . unwrap ( )
1317+ . samples ;
1318+
1319+ let get_samples_in_chunk = |chunk : u32 | -> u32 {
1320+ let mut result = 0 ;
1321+ for cr in chunk_runs {
1322+ if ( cr. first_chunk - 1 ) > chunk {
1323+ break ;
1324+ }
1325+ result = cr. samples_per_chunk ;
1326+ }
1327+ result
1328+ } ;
1329+
1330+ let chunk_of_sample = |sample : u32 | -> ( u32 , u32 ) {
1331+ let mut sample_accum = 0 ;
1332+ for chunk in 0 .. {
1333+ let samples_in_chunk = get_samples_in_chunk ( chunk) ;
1334+ if sample_accum + samples_in_chunk > sample {
1335+ return ( chunk, sample_accum) ;
1336+ }
1337+ sample_accum += samples_in_chunk;
1338+ }
1339+ ( 0 , 0 )
1340+ } ;
1341+
1342+ let ( chunk, first_sample_in_chunk) = chunk_of_sample ( sample_id) ;
1343+ let chunk_offsets = & media_context
1344+ . tracks
1345+ . get ( 1 )
1346+ . unwrap ( )
1347+ . stco
1348+ . as_ref ( )
1349+ . unwrap ( )
1350+ . offsets ;
1351+
1352+ let mut offs = chunk_offsets[ chunk as usize ] as usize ;
1353+
1354+ for sam in first_sample_in_chunk..sample_id {
1355+ offs += sample_sizes[ sam as usize ] as usize ;
1356+ }
1357+
1358+ let siz = sample_sizes[ sample_id as usize ] as usize ;
1359+
1360+ println ! ( "offs: {}, siz: {}" , offs, siz) ;
1361+ let s = buffer;
1362+
1363+ let encoded_frame = EncodedFrame {
1364+ codec : VideoCodec :: H264 ,
1365+ data : s[ offs..offs + siz] . as_ref ( ) ,
1366+ frame_id : frame_id. unwrap ( ) ,
1367+ } ;
1368+
1369+ let video_handle: VideoStreamHandle = match video_stream {
1370+ Some ( stream) => * stream,
1371+ None => {
1372+ match context. video . register_video_stream (
1373+ 1 ,
1374+ ( 8 , 8 ) ,
1375+ VideoCodec :: H264 ,
1376+ VideoDeblocking :: UseVideoPacketValue ,
1377+ ) {
1378+ Ok ( new_handle) => {
1379+ * video_stream = Some ( new_handle) ;
1380+
1381+ new_handle
1382+ }
1383+ Err ( e) => {
1384+ tracing:: error!(
1385+ "Got error when registring FLV video stream: {}" ,
1386+ e
1387+ ) ;
1388+ return ; //TODO: This originally breaks and halts tag processing
1389+ }
1390+ }
1391+ }
1392+ } ;
1393+
1394+ let mdct = media_context. clone ( ) ;
1395+ let trk = mdct. tracks . get ( 1 ) . unwrap ( ) ;
1396+ let stsd = trk. stsd . as_ref ( ) . unwrap ( ) ;
1397+ let descs = stsd. descriptions . get ( 0 ) . unwrap ( ) ;
1398+
1399+ match descs {
1400+ mp4parse:: SampleEntry :: Video ( video) => match & video. codec_specific {
1401+ mp4parse:: VideoCodecSpecific :: AVCConfig ( avcconf) => {
1402+ if * frame_id == Some ( 0 ) {
1403+ println ! ( "preloading avc config" ) ;
1404+ let _ = context. video . configure_video_stream_decoder (
1405+ video_handle,
1406+ avcconf. as_slice ( ) ,
1407+ ) ;
1408+ }
1409+ }
1410+ mp4parse:: VideoCodecSpecific :: VPxConfig ( _) => todo ! ( ) ,
1411+ mp4parse:: VideoCodecSpecific :: AV1Config ( _) => todo ! ( ) ,
1412+ mp4parse:: VideoCodecSpecific :: ESDSConfig ( _) => todo ! ( ) ,
1413+ mp4parse:: VideoCodecSpecific :: H263Config ( _) => todo ! ( ) ,
1414+ mp4parse:: VideoCodecSpecific :: HEVCConfig ( _) => todo ! ( ) ,
1415+ } ,
1416+ mp4parse:: SampleEntry :: Audio ( _) => todo ! ( ) ,
1417+ mp4parse:: SampleEntry :: Unknown => todo ! ( ) ,
1418+ }
1419+
1420+ write. last_decoded_bitmap = Some (
1421+ context
1422+ . video
1423+ . decode_video_stream_frame ( video_handle, encoded_frame, context. renderer )
1424+ . unwrap ( ) ,
1425+ ) ;
1426+ }
1427+ }
1428+
12371429 write. stream_time = max_time;
12381430 if let Err ( e) = self . commit_sound_stream ( context, & mut write) {
12391431 //TODO: Fire an error event at AS.
0 commit comments