core: If a video does not have an `onMetaData` block, register a compatible-ish datastream so that we can still play the video
This commit is contained in:
parent
b9aaaf5259
commit
8ac8b926ac
|
@ -23,6 +23,7 @@ use ruffle_video::VideoStreamHandle;
|
||||||
use ruffle_wstr::WStr;
|
use ruffle_wstr::WStr;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::io::Seek;
|
use std::io::Seek;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use swf::{VideoCodec, VideoDeblocking};
|
use swf::{VideoCodec, VideoDeblocking};
|
||||||
|
|
||||||
/// Manager for all media streams.
|
/// Manager for all media streams.
|
||||||
|
@ -146,7 +147,11 @@ pub enum NetStreamType {
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
pub struct NetStreamData<'gc> {
|
pub struct NetStreamData<'gc> {
|
||||||
/// All data currently loaded in the stream.
|
/// All data currently loaded in the stream.
|
||||||
buffer: Vec<u8>,
|
///
|
||||||
|
/// NOTE: This is stored as an `Arc` to allow independent borrows of the
|
||||||
|
/// buffer data and stream state.
|
||||||
|
#[collect(require_static)]
|
||||||
|
buffer: Arc<Mutex<Vec<u8>>>,
|
||||||
|
|
||||||
/// The buffer position that we are currently seeking to.
|
/// The buffer position that we are currently seeking to.
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
@ -184,7 +189,7 @@ impl<'gc> NetStream<'gc> {
|
||||||
Self(GcCell::allocate(
|
Self(GcCell::allocate(
|
||||||
gc_context,
|
gc_context,
|
||||||
NetStreamData {
|
NetStreamData {
|
||||||
buffer: Vec::new(),
|
buffer: Arc::new(Mutex::new(Vec::new())),
|
||||||
offset: 0,
|
offset: 0,
|
||||||
preload_offset: 0,
|
preload_offset: 0,
|
||||||
stream_type: None,
|
stream_type: None,
|
||||||
|
@ -200,7 +205,12 @@ impl<'gc> NetStream<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_buffer(self, context: &mut UpdateContext<'_, 'gc>, data: &mut Vec<u8>) {
|
pub fn load_buffer(self, context: &mut UpdateContext<'_, 'gc>, data: &mut Vec<u8>) {
|
||||||
self.0.write(context.gc_context).buffer.append(data);
|
self.0
|
||||||
|
.write(context.gc_context)
|
||||||
|
.buffer
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.append(data);
|
||||||
|
|
||||||
if context.is_action_script_3() {
|
if context.is_action_script_3() {
|
||||||
// Don't ask why but the AS3 test has a spurious status event in it
|
// Don't ask why but the AS3 test has a spurious status event in it
|
||||||
|
@ -218,11 +228,11 @@ impl<'gc> NetStream<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bytes_loaded(self) -> usize {
|
pub fn bytes_loaded(self) -> usize {
|
||||||
self.0.read().buffer.len()
|
self.0.read().buffer.lock().unwrap().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bytes_total(self) -> usize {
|
pub fn bytes_total(self) -> usize {
|
||||||
self.0.read().buffer.len()
|
self.0.read().buffer.lock().unwrap().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start playing media from this NetStream.
|
/// Start playing media from this NetStream.
|
||||||
|
@ -265,6 +275,8 @@ impl<'gc> NetStream<'gc> {
|
||||||
|
|
||||||
pub fn tick(self, context: &mut UpdateContext<'_, 'gc>, dt: f64) {
|
pub fn tick(self, context: &mut UpdateContext<'_, 'gc>, dt: f64) {
|
||||||
let mut write = self.0.write(context.gc_context);
|
let mut write = self.0.write(context.gc_context);
|
||||||
|
let buffer_owned = write.buffer.clone();
|
||||||
|
let buffer = buffer_owned.lock().unwrap();
|
||||||
|
|
||||||
// First, try to sniff the stream's container format from headers.
|
// First, try to sniff the stream's container format from headers.
|
||||||
if write.stream_type.is_none() {
|
if write.stream_type.is_none() {
|
||||||
|
@ -275,9 +287,9 @@ impl<'gc> NetStream<'gc> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match write.buffer.get(0..3) {
|
match buffer.get(0..3) {
|
||||||
Some([0x46, 0x4C, 0x56]) => {
|
Some([0x46, 0x4C, 0x56]) => {
|
||||||
let mut reader = FlvReader::from_parts(&write.buffer, write.offset);
|
let mut reader = FlvReader::from_parts(&buffer, write.offset);
|
||||||
match FlvHeader::parse(&mut reader) {
|
match FlvHeader::parse(&mut reader) {
|
||||||
Ok(header) => {
|
Ok(header) => {
|
||||||
write.offset = reader.into_parts().1;
|
write.offset = reader.into_parts().1;
|
||||||
|
@ -314,7 +326,7 @@ impl<'gc> NetStream<'gc> {
|
||||||
|
|
||||||
//At this point we should know our stream type.
|
//At this point we should know our stream type.
|
||||||
if matches!(write.stream_type, Some(NetStreamType::Flv { .. })) {
|
if matches!(write.stream_type, Some(NetStreamType::Flv { .. })) {
|
||||||
let mut reader = FlvReader::from_parts(&write.buffer, write.offset);
|
let mut reader = FlvReader::from_parts(&buffer, write.offset);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let tag = FlvTag::parse(&mut reader);
|
let tag = FlvTag::parse(&mut reader);
|
||||||
|
@ -359,9 +371,9 @@ impl<'gc> NetStream<'gc> {
|
||||||
let codec = VideoCodec::from_u8(codec_id as u8);
|
let codec = VideoCodec::from_u8(codec_id as u8);
|
||||||
|
|
||||||
match (video_handle, codec, data) {
|
match (video_handle, codec, data) {
|
||||||
(Some(video_handle), Some(codec), FlvVideoPacket::Data(mut data))
|
(maybe_video_handle, Some(codec), FlvVideoPacket::Data(mut data))
|
||||||
| (
|
| (
|
||||||
Some(video_handle),
|
maybe_video_handle,
|
||||||
Some(codec),
|
Some(codec),
|
||||||
FlvVideoPacket::Vp6Data {
|
FlvVideoPacket::Vp6Data {
|
||||||
hadjust: _,
|
hadjust: _,
|
||||||
|
@ -369,16 +381,48 @@ impl<'gc> NetStream<'gc> {
|
||||||
mut data,
|
mut data,
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
|
//Some movies don't actually have metadata, so let's register a
|
||||||
|
//dummy stream just in case. All the actual data in the registration
|
||||||
|
//is lies, of course.
|
||||||
|
let video_handle = match maybe_video_handle {
|
||||||
|
Some(stream) => stream,
|
||||||
|
None => {
|
||||||
|
match context.video.register_video_stream(
|
||||||
|
1,
|
||||||
|
(8, 8),
|
||||||
|
codec,
|
||||||
|
VideoDeblocking::UseVideoPacketValue,
|
||||||
|
) {
|
||||||
|
Ok(new_handle) => {
|
||||||
|
match &mut write.stream_type {
|
||||||
|
Some(NetStreamType::Flv { stream, .. }) => {
|
||||||
|
*stream = Some(new_handle)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
new_handle
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!(
|
||||||
|
"Got error when registring FLV video stream: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if codec == VideoCodec::ScreenVideo
|
if codec == VideoCodec::ScreenVideo
|
||||||
|| codec == VideoCodec::ScreenVideoV2
|
|| codec == VideoCodec::ScreenVideoV2
|
||||||
{
|
{
|
||||||
// ScreenVideo streams consider the FLV
|
// ScreenVideo streams consider the FLV
|
||||||
// video data byte to be integral to their
|
// video data byte to be integral to their
|
||||||
// own bitstream.
|
// own bitstream.
|
||||||
let offset =
|
let offset = data.as_ptr() as usize - buffer.as_ptr() as usize;
|
||||||
data.as_ptr() as usize - write.buffer.as_ptr() as usize;
|
|
||||||
let len = data.len();
|
let len = data.len();
|
||||||
data = &write.buffer[offset - 1..offset + len];
|
data = &buffer[offset - 1..offset + len];
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Currently, no implementation of the decoder backend actually requires
|
// NOTE: Currently, no implementation of the decoder backend actually requires
|
||||||
|
@ -415,7 +459,7 @@ impl<'gc> NetStream<'gc> {
|
||||||
Ok(bitmap_info) => {
|
Ok(bitmap_info) => {
|
||||||
let (_, position) = reader.into_parts();
|
let (_, position) = reader.into_parts();
|
||||||
write.last_decoded_bitmap = Some(bitmap_info);
|
write.last_decoded_bitmap = Some(bitmap_info);
|
||||||
reader = FlvReader::from_parts(&write.buffer, position);
|
reader = FlvReader::from_parts(&buffer, position);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
|
@ -444,9 +488,6 @@ impl<'gc> NetStream<'gc> {
|
||||||
codec_id as u8
|
codec_id as u8
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(None, _, _) => tracing::error!(
|
|
||||||
"Cannot decode FLV video tag before metadata is loaded"
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, position) = reader.into_parts();
|
let (_, position) = reader.into_parts();
|
||||||
|
@ -456,7 +497,7 @@ impl<'gc> NetStream<'gc> {
|
||||||
}) => *frame_id += 1,
|
}) => *frame_id += 1,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
reader = FlvReader::from_parts(&write.buffer, position);
|
reader = FlvReader::from_parts(&buffer, position);
|
||||||
}
|
}
|
||||||
FlvTagData::Script(FlvScriptData(vars)) => {
|
FlvTagData::Script(FlvScriptData(vars)) => {
|
||||||
let has_stream_already = match write.stream_type {
|
let has_stream_already = match write.stream_type {
|
||||||
|
@ -547,7 +588,7 @@ impl<'gc> NetStream<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reader = FlvReader::from_parts(&write.buffer, position);
|
reader = FlvReader::from_parts(&buffer, position);
|
||||||
}
|
}
|
||||||
FlvTagData::Invalid(e) => {
|
FlvTagData::Invalid(e) => {
|
||||||
tracing::error!("FLV data parsing failed: {}", e)
|
tracing::error!("FLV data parsing failed: {}", e)
|
||||||
|
@ -559,7 +600,7 @@ impl<'gc> NetStream<'gc> {
|
||||||
let (_, position) = reader.into_parts();
|
let (_, position) = reader.into_parts();
|
||||||
write.offset = position;
|
write.offset = position;
|
||||||
write.preload_offset = max(write.offset, write.preload_offset);
|
write.preload_offset = max(write.offset, write.preload_offset);
|
||||||
reader = FlvReader::from_parts(&write.buffer, position);
|
reader = FlvReader::from_parts(&buffer, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue