Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Video base/stub impls #3117

Merged
merged 30 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
67f843e
core: Add `VideoBackend` to host video decoders
kmeisthax Oct 15, 2020
414bcc2
core: Add video display object
kmeisthax Oct 15, 2020
7298195
swf: Make video-related tags publicly parsable
kmeisthax Oct 16, 2020
98ec2ec
core: Parse `DefineVideoStream` and use it to put videos into the cha…
kmeisthax Oct 16, 2020
267dfbd
core: Also implement `VideoFrame` tag.
kmeisthax Oct 17, 2020
1a489f5
core: Further adjustment to video backend type
kmeisthax Feb 6, 2021
940461f
core: Add software video backend, separate from the null backend, to …
kmeisthax Dec 22, 2020
c7c3539
video: Stub seeking.
kmeisthax Dec 22, 2020
319df7e
core: Allow instantiation of videos
kmeisthax Dec 23, 2020
ec51a29
video: Implement a very basic video decoding loop
kmeisthax Dec 23, 2020
59ad6d1
video: Always decode the first frame
kmeisthax Dec 23, 2020
ad9e8a9
video: Report what frame gave an error
kmeisthax Dec 23, 2020
4e88708
video: Enable software video decoding on web and exporter.
kmeisthax Dec 24, 2020
8fec01d
video: Warn if we are missing a frame to render.
kmeisthax Dec 24, 2020
47257c0
video: Warn if duplicate video frames are encountered
kmeisthax Dec 25, 2020
d7a1833
avm1: Stub impl the `Video` class.
kmeisthax Jan 5, 2021
8caf752
avm2: Stub the `Video` class.
kmeisthax Jan 5, 2021
9d242d8
video: Snap seeks to nearest keyframe
kmeisthax Jan 7, 2021
38cf454
video: Frames with no data associated are treated as empty Pframes.
kmeisthax Jan 8, 2021
b86aeb1
chore: Clippy compliance for stub video backend
kmeisthax Feb 7, 2021
61e93e7
core: Fix various documentation errors in video
kmeisthax Feb 11, 2021
6d6661a
avm2: `Video` is a sealed class.
kmeisthax Feb 11, 2021
6bc3585
core: Seeks before instantiation are valid and should be cached for l…
kmeisthax Feb 12, 2021
2ddc512
core: Rename the video dependency enums to better describe their mean…
kmeisthax Feb 12, 2021
afc5553
chore: Spelling matters
kmeisthax Feb 12, 2021
f41301b
chore: Clippy wants this too
kmeisthax Feb 14, 2021
1412b60
core/video: Make seeking to non-keyframes possible.
torokati44 Feb 12, 2021
f108fff
core/video: Loop to frame modulo num_frames. Fixes z0r.de/1843
torokati44 Feb 12, 2021
c4ad641
core: Future-proof the non-keyframe seek for other stream types.
kmeisthax Feb 15, 2021
8a3fddc
core: Adjust documentation
kmeisthax Feb 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions core/src/avm1/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub(crate) mod system_security;
pub(crate) mod text_field;
mod text_format;
mod transform;
mod video;
mod xml;

pub fn random<'gc>(
Expand Down Expand Up @@ -506,6 +507,8 @@ pub struct SystemPrototypes<'gc> {
pub date: Object<'gc>,
pub bitmap_data: Object<'gc>,
pub bitmap_data_constructor: Object<'gc>,
pub video: Object<'gc>,
pub video_constructor: Object<'gc>,
}

/// Initialize default global scope and builtins for an AVM1 instance.
Expand Down Expand Up @@ -582,6 +585,8 @@ pub fn create_globals<'gc>(
);
let date_proto: Object<'gc> = date::create_proto(gc_context, object_proto, function_proto);

let video_proto: Object<'gc> = video::create_proto(gc_context, object_proto, function_proto);

//TODO: These need to be constructors and should also set `.prototype` on each one
let object = object::create_object_object(gc_context, object_proto, function_proto);

Expand Down Expand Up @@ -697,6 +702,13 @@ pub fn create_globals<'gc>(
Some(function_proto),
transform_proto,
);
let video = FunctionObject::constructor(
gc_context,
Executable::Native(video::constructor),
constructor_to_fn!(video::constructor),
Some(function_proto),
video_proto,
);

flash.define_value(gc_context, "geom", geom.into(), Attribute::empty());
flash.define_value(gc_context, "filters", filters.into(), Attribute::empty());
Expand Down Expand Up @@ -1255,6 +1267,8 @@ pub fn create_globals<'gc>(
date: date_proto,
bitmap_data: bitmap_data_proto,
bitmap_data_constructor: bitmap_data,
video: video_proto,
video_constructor: video,
},
globals.into(),
broadcaster_functions,
Expand Down
30 changes: 30 additions & 0 deletions core/src/avm1/globals/video.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Video class

use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::globals::display_object;
use crate::avm1::object::Object;
use crate::avm1::value::Value;
use crate::avm1::ScriptObject;
use gc_arena::MutationContext;

/// Implements `Video`
pub fn constructor<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined)
}

pub fn create_proto<'gc>(
gc_context: MutationContext<'gc, '_>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
let object = ScriptObject::object(gc_context, Some(proto));

display_object::define_display_object_proto(gc_context, object, fn_proto);

object.into()
}
2 changes: 2 additions & 0 deletions core/src/avm1/object/script_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,7 @@ mod tests {
use crate::backend::render::NullRenderer;
use crate::backend::storage::MemoryStorageBackend;
use crate::backend::ui::NullUiBackend;
use crate::backend::video::NullVideoBackend;
use crate::context::UpdateContext;
use crate::display_object::MovieClip;
use crate::focus_tracker::FocusTracker;
Expand Down Expand Up @@ -890,6 +891,7 @@ mod tests {
renderer: &mut NullRenderer::new(),
locale: &mut NullLocaleBackend::new(),
log: &mut NullLogBackend::new(),
video: &mut NullVideoBackend::new(),
mouse_hovered_object: None,
mouse_position: &(Twips::new(0), Twips::new(0)),
drag_object: &mut None,
Expand Down
2 changes: 2 additions & 0 deletions core/src/avm1/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::backend::navigator::NullNavigatorBackend;
use crate::backend::render::NullRenderer;
use crate::backend::storage::MemoryStorageBackend;
use crate::backend::ui::NullUiBackend;
use crate::backend::video::NullVideoBackend;
use crate::context::ActionQueue;
use crate::display_object::{MovieClip, TDisplayObject};
use crate::focus_tracker::FocusTracker;
Expand Down Expand Up @@ -59,6 +60,7 @@ where
renderer: &mut NullRenderer::new(),
locale: &mut NullLocaleBackend::new(),
log: &mut NullLogBackend::new(),
video: &mut NullVideoBackend::new(),
mouse_hovered_object: None,
mouse_position: &(Twips::new(0), Twips::new(0)),
drag_object: &mut None,
Expand Down
17 changes: 17 additions & 0 deletions core/src/avm2/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub struct SystemPrototypes<'gc> {
pub scene: Object<'gc>,
pub application_domain: Object<'gc>,
pub event: Object<'gc>,
pub video: Object<'gc>,
}

impl<'gc> SystemPrototypes<'gc> {
Expand Down Expand Up @@ -128,6 +129,7 @@ impl<'gc> SystemPrototypes<'gc> {
scene: empty,
application_domain: empty,
event: empty,
video: empty,
}
}
}
Expand Down Expand Up @@ -554,5 +556,20 @@ pub fn load_player_globals<'gc>(
script,
)?;

// package `flash.media`
activation
.context
.avm2
.system_prototypes
.as_mut()
.unwrap()
.video = class(
activation,
flash::media::video::create_class(mc),
implicit_deriver,
domain,
script,
)?;

Ok(())
}
1 change: 1 addition & 0 deletions core/src/avm2/globals/flash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

pub mod display;
pub mod events;
pub mod media;
pub mod system;
3 changes: 3 additions & 0 deletions core/src/avm2/globals/flash/media.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//! `flash.media` namespace

pub mod video;
49 changes: 49 additions & 0 deletions core/src/avm2/globals/flash/media/video.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! `flash.media.Video` builtin/prototype

use crate::avm2::activation::Activation;
use crate::avm2::class::{Class, ClassAttributes};
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::Object;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::{GcCell, MutationContext};

/// Implements `flash.media.Video`'s instance constructor.
pub fn instance_init<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
activation.super_init(this, &[])?;
}

Ok(Value::Undefined)
}

/// Implements `flash.media.Video`'s class constructor.
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}

/// Construct `Video`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
let class = Class::new(
QName::new(Namespace::package("flash.media"), "Video"),
Some(QName::new(Namespace::package("flash.media"), "DisplayObject").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
);

let mut write = class.write(mc);

write.set_attributes(ClassAttributes::SEALED);

class
}
1 change: 1 addition & 0 deletions core/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod navigator;
pub mod render;
pub mod storage;
pub mod ui;
pub mod video;
172 changes: 172 additions & 0 deletions core/src/backend/video.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//! Video decoder backends

use crate::backend::render::{BitmapInfo, RenderBackend};
use generational_arena::{Arena, Index};
use swf::{VideoCodec, VideoDeblocking};

mod software;

pub use crate::backend::video::software::SoftwareVideoBackend;

pub type VideoStreamHandle = Index;

pub type Error = Box<dyn std::error::Error>;

/// An encoded video frame of some video codec.
pub struct EncodedFrame<'a> {
/// The codec used to encode the frame.
pub codec: VideoCodec,

/// The raw bitstream data to funnel into the codec.
pub data: &'a [u8],

/// A caller-specified frame ID. Frame IDs must be consistent between
kmeisthax marked this conversation as resolved.
Show resolved Hide resolved
/// subsequent uses of the same data stream.
pub frame_id: u32,
}

impl<'a> EncodedFrame<'a> {
/// Borrow this frame's data.
pub fn data(&'a self) -> &'a [u8] {
&self.data
}
}

/// What dependencies a given video frame has on any previous frames.
#[derive(Copy, Clone, Debug)]
pub enum FrameDependency {
/// This frame has no reference frames and can be seeked to at any time.
None,

/// This frame has some number of reference frames that prohibit any
/// out-of-order decoding.
///
/// The only legal way to decode a `Past` frame is to decode every prior
/// frame from the last `None` frame. In the event that there is no prior
/// `None` frame, then video decoding should start from the beginning.
Past,
}

impl FrameDependency {
/// Determine if this given frame is a keyframe.
///
/// A keyframe is a frame that can be independently seeked to without
/// decoding any prior or future frames.
pub fn is_keyframe(self) -> bool {
matches!(self, FrameDependency::None)
}
}

/// A backend that provides access to some number of video decoders.
///
/// Implementations of `VideoBackend` are not required to actually support
/// decoding any video formats. However, they must interoperate with at least
/// one `RenderBackend` so that renderable video frames may be passed from the
/// decoder to the renderer.
pub trait VideoBackend {
/// Register a new video stream.
///
/// Most of the parameters provided to this function are advisory: the
/// actual video data stream provided to the decoder may vary in size or
/// number of frames. This function should return an `Error` if it is not
kmeisthax marked this conversation as resolved.
Show resolved Hide resolved
/// possible to decode video with the given parameters.
fn register_video_stream(
&mut self,
num_frames: u32,
size: (u16, u16),
codec: VideoCodec,
filter: VideoDeblocking,
) -> Result<VideoStreamHandle, Error>;

/// Preload a frame of a given video stream.
///
/// No decoding is intended to happen at this point in time. Instead, the
/// video data should be inspected to determine inter-frame dependencies
/// between this and any previous frames in the stream.
///
/// Frames should be preloaded in the order that they are recieved.
///
/// Any dependencies listed here are inherent to the video bitstream. The
/// containing video stream is also permitted to introduce additional
/// interframe dependencies.
fn preload_video_stream_frame(
&mut self,
stream: VideoStreamHandle,
encoded_frame: EncodedFrame<'_>,
) -> Result<FrameDependency, Error>;

/// Decode a frame of a given video stream.
///
/// This function is provided the external index of the frame, the codec
/// used to decode the data, and what codec to decode it with. The codec
/// provided here must match the one used to register the video stream.
///
/// Frames may be decoded in any order that does not violate the frame
/// dependencies declared by the output of `preload_video_stream_frame`.
///
/// The resulting `BitmapInfo` will be renderable only on the given
/// `RenderBackend`. `VideoBackend` implementations are allowed to return
/// an error if a drawable bitmap cannot be produced for the given
/// renderer.
fn decode_video_stream_frame(
&mut self,
stream: VideoStreamHandle,
encoded_frame: EncodedFrame<'_>,
renderer: &mut dyn RenderBackend,
) -> Result<BitmapInfo, Error>;
}

pub struct NullVideoBackend {
streams: Arena<()>,
}

/// Implementation of video that does not decode any video.
///
/// Specifically:
///
/// * Registering a video stream succeeds but does nothing
/// * All video frames are silently marked as keyframes (`None` dependency)
/// * Video stream decoding fails with an error that video decoding is
/// unimplemented
impl NullVideoBackend {
pub fn new() -> Self {
Self {
streams: Arena::new(),
}
}
}

impl Default for NullVideoBackend {
fn default() -> Self {
Self::new()
}
}

impl VideoBackend for NullVideoBackend {
fn register_video_stream(
&mut self,
_num_frames: u32,
_size: (u16, u16),
_codec: VideoCodec,
_filter: VideoDeblocking,
) -> Result<VideoStreamHandle, Error> {
Ok(self.streams.insert(()))
}

fn preload_video_stream_frame(
&mut self,
_stream: VideoStreamHandle,
_encoded_frame: EncodedFrame<'_>,
) -> Result<FrameDependency, Error> {
Ok(FrameDependency::None)
}

fn decode_video_stream_frame(
&mut self,
_stream: VideoStreamHandle,
_encoded_frame: EncodedFrame<'_>,
_renderer: &mut dyn RenderBackend,
) -> Result<BitmapInfo, Error> {
Err("Video decoding not implemented".into())
}
}
Loading