core: Process `onMetaData` to get our stream definition with.

We use `preload_offset` to avoid processing the same metadata multiple times.
This commit is contained in:
David Wendt 2023-04-15 23:40:22 -04:00 committed by kmeisthax
parent 286acddfea
commit 3410d9fcf8
1 changed files with 144 additions and 45 deletions

View File

@ -6,9 +6,14 @@ use crate::loader::Error;
use crate::string::AvmString; use crate::string::AvmString;
use flv_rs::{ use flv_rs::{
AudioData as FlvAudioData, Error as FlvError, FlvReader, Header as FlvHeader, AudioData as FlvAudioData, Error as FlvError, FlvReader, Header as FlvHeader,
ScriptData as FlvScriptData, Tag as FlvTag, TagData as FlvTagData, VideoData as FlvVideoData, ScriptData as FlvScriptData, Tag as FlvTag, TagData as FlvTagData, Value as FlvValue,
VideoData as FlvVideoData,
}; };
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use ruffle_video::VideoStreamHandle;
use ruffle_wstr::WStr;
use std::io::Seek;
use swf::{VideoCodec, VideoDeblocking};
/// Manager for all media streams. /// Manager for all media streams.
/// ///
@ -114,7 +119,10 @@ impl<'gc> Eq for NetStream<'gc> {}
#[collect(require_static)] #[collect(require_static)]
pub enum NetStreamType { pub enum NetStreamType {
/// The stream is an FLV. /// The stream is an FLV.
Flv(FlvHeader), Flv {
header: FlvHeader,
stream: Option<VideoStreamHandle>,
},
} }
#[derive(Clone, Debug, Collect)] #[derive(Clone, Debug, Collect)]
@ -221,7 +229,10 @@ impl<'gc> NetStream<'gc> {
Ok(header) => { Ok(header) => {
write.offset = reader.into_parts().1; write.offset = reader.into_parts().1;
write.preload_offset = write.offset; write.preload_offset = write.offset;
write.stream_type = Some(NetStreamType::Flv(header)); write.stream_type = Some(NetStreamType::Flv {
header,
stream: None,
});
} }
Err(FlvError::EndOfData) => return, Err(FlvError::EndOfData) => return,
Err(e) => { Err(e) => {
@ -242,56 +253,144 @@ impl<'gc> NetStream<'gc> {
let end_time = write.stream_time + dt; let end_time = write.stream_time + dt;
//At this point we should know our stream type. //At this point we should know our stream type.
match write.stream_type.as_ref().expect("known stream type") { if matches!(write.stream_type, Some(NetStreamType::Flv { .. })) {
NetStreamType::Flv(header) => { let mut reader = FlvReader::from_parts(&write.buffer, write.offset);
let mut reader = FlvReader::from_parts(&write.buffer, write.offset);
loop { loop {
let tag = FlvTag::parse(&mut reader); let tag = FlvTag::parse(&mut reader);
if let Err(e) = tag { if let Err(e) = tag {
//Corrupt tag or out of data //Corrupt tag or out of data
if !matches!(e, FlvError::EndOfData) { if !matches!(e, FlvError::EndOfData) {
//TODO: Stop the stream so we don't repeatedly yield the same error //TODO: Stop the stream so we don't repeatedly yield the same error
//and fire an error event to AS //and fire an error event to AS
tracing::error!("FLV tag parsing failed: {}", e); tracing::error!("FLV tag parsing failed: {}", e);
}
break;
} }
let tag = tag.expect("valid tag"); break;
if tag.timestamp as f64 >= end_time { }
//All tags processed
if let Err(e) = FlvTag::skip_back(&mut reader) {
tracing::error!("FLV skip back failed: {}", e);
}
break; let tag = tag.expect("valid tag");
if tag.timestamp as f64 >= end_time {
//All tags processed
if let Err(e) = FlvTag::skip_back(&mut reader) {
tracing::error!("FLV skip back failed: {}", e);
} }
match tag.data { break;
FlvTagData::Audio(FlvAudioData { }
format,
rate, let tag_needs_preloading = reader.stream_position().expect("valid position")
size, as usize
sound_type, >= write.preload_offset;
data,
}) => { match tag.data {
tracing::warn!("Stub: Stream audio processing"); FlvTagData::Audio(FlvAudioData {
format,
rate,
size,
sound_type,
data,
}) => {
tracing::warn!("Stub: Stream audio processing");
}
FlvTagData::Video(FlvVideoData {
frame_type,
codec_id,
data,
}) => {
tracing::warn!("Stub: Stream video processing");
}
FlvTagData::Script(FlvScriptData(vars)) => {
let has_stream_already = match write.stream_type {
Some(NetStreamType::Flv { stream, .. }) => stream.is_some(),
_ => unreachable!(),
};
let mut width = None;
let mut height = None;
let mut video_codec_id = None;
let mut frame_rate = None;
let mut duration = None;
for var in vars {
if var.name == b"onMetaData" && !has_stream_already {
match var.data {
FlvValue::Object(subvars) => {
for subvar in subvars {
match (subvar.name, subvar.data) {
(b"width", FlvValue::Number(val)) => {
width = Some(val)
}
(b"height", FlvValue::Number(val)) => {
height = Some(val)
}
(b"videocodecid", FlvValue::Number(val)) => {
video_codec_id = Some(val)
}
(b"framerate", FlvValue::Number(val)) => {
frame_rate = Some(val)
}
(b"duration", FlvValue::Number(val)) => {
duration = Some(val)
}
_ => {}
}
}
}
_ => tracing::error!("Invalid FLV metadata tag!"),
}
} else {
tracing::warn!(
"Stub: Stream data processing (name: {})",
WStr::from_units(var.name)
);
}
} }
FlvTagData::Video(FlvVideoData {
frame_type, let (_, position) = reader.into_parts();
codec_id,
data, if tag_needs_preloading {
}) => { if let (
tracing::warn!("Stub: Stream video processing"); Some(width),
} Some(height),
FlvTagData::Script(FlvScriptData(vars)) => { Some(video_codec_id),
tracing::warn!("Stub: Stream data processing"); Some(frame_rate),
} Some(duration),
FlvTagData::Invalid(e) => { ) = (width, height, video_codec_id, frame_rate, duration)
tracing::error!("FLV data parsing failed: {}", e) {
let num_frames = frame_rate * duration;
if let Some(video_codec) = VideoCodec::from_u8(video_codec_id as u8)
{
match context.video.register_video_stream(
num_frames as u32,
(width as u16, height as u16),
video_codec,
VideoDeblocking::UseVideoPacketValue,
) {
Ok(stream_handle) => match &mut write.stream_type {
Some(NetStreamType::Flv { stream, .. }) => {
*stream = Some(stream_handle)
}
_ => unreachable!(),
},
Err(e) => tracing::error!(
"Got error when registring FLV video stream: {}",
e
),
}
} else {
tracing::error!(
"FLV video stream has invalid codec ID {}",
video_codec_id
);
}
}
} }
reader = FlvReader::from_parts(&write.buffer, position);
}
FlvTagData::Invalid(e) => {
tracing::error!("FLV data parsing failed: {}", e)
} }
} }
} }