avm2: Implement flash.media.ID3Info (#14916)
This commit is contained in:
parent
5b920b8447
commit
556d16302b
|
@ -2625,6 +2625,17 @@ dependencies = [
|
|||
"objc2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id3"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba0a11a3cf6f08d58a5629531bdb4e7c3b8b595e9812a31a7058b1176c4631e"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"byteorder",
|
||||
"flate2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
|
@ -4417,6 +4428,7 @@ dependencies = [
|
|||
"fnv",
|
||||
"futures",
|
||||
"hashbrown 0.14.3",
|
||||
"id3",
|
||||
"image",
|
||||
"indexmap",
|
||||
"jpegxr",
|
||||
|
|
|
@ -65,6 +65,7 @@ enum-map = "2.7.3"
|
|||
ttf-parser = "0.20"
|
||||
num-bigint = "0.4"
|
||||
unic-segment = "0.9.0"
|
||||
id3 = "1.12.0"
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies.futures]
|
||||
version = "0.3.30"
|
||||
|
|
|
@ -175,6 +175,7 @@ pub struct SystemClasses<'gc> {
|
|||
pub avm1movie: ClassObject<'gc>,
|
||||
pub focusevent: ClassObject<'gc>,
|
||||
pub dictionary: ClassObject<'gc>,
|
||||
pub id3info: ClassObject<'gc>,
|
||||
}
|
||||
|
||||
impl<'gc> SystemClasses<'gc> {
|
||||
|
@ -308,6 +309,7 @@ impl<'gc> SystemClasses<'gc> {
|
|||
avm1movie: object,
|
||||
focusevent: object,
|
||||
dictionary: object,
|
||||
id3info: object,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -811,6 +813,7 @@ fn load_playerglobal<'gc>(
|
|||
("flash.geom", "Rectangle", rectangle),
|
||||
("flash.geom", "Transform", transform),
|
||||
("flash.geom", "ColorTransform", colortransform),
|
||||
("flash.media", "ID3Info", id3info),
|
||||
("flash.media", "SoundChannel", soundchannel),
|
||||
("flash.media", "SoundTransform", soundtransform),
|
||||
("flash.media", "Video", video),
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package flash.media {
|
||||
|
||||
public final dynamic class ID3Info {
|
||||
public var album:String;
|
||||
public var artist:String;
|
||||
public var comment:String;
|
||||
public var genre:String;
|
||||
public var songName:String;
|
||||
public var track:String;
|
||||
public var year:String;
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ package flash.media {
|
|||
public native function get isURLInaccessible():Boolean;
|
||||
public native function get url():String;
|
||||
public native function get length():Number;
|
||||
public native function get id3():ID3Info;
|
||||
public native function play(startTime:Number = 0, loops:int = 0, sndTransform:SoundTransform = null):SoundChannel;
|
||||
public native function extract(target:ByteArray, length:Number, startPosition:Number = -1):Number;
|
||||
public native function close():void;
|
||||
|
|
|
@ -4,10 +4,12 @@ use crate::avm2::activation::Activation;
|
|||
use crate::avm2::object::{Object, QueuedPlay, SoundChannelObject, TObject};
|
||||
use crate::avm2::parameters::ParametersExt;
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Avm2;
|
||||
use crate::avm2::Error;
|
||||
use crate::backend::navigator::Request;
|
||||
use crate::character::Character;
|
||||
use crate::display_object::SoundTransform;
|
||||
use crate::string::AvmString;
|
||||
use crate::{avm2_stub_getter, avm2_stub_method};
|
||||
use swf::{SoundEvent, SoundInfo};
|
||||
|
||||
|
@ -280,6 +282,28 @@ pub fn load_compressed_data_from_byte_array<'gc>(
|
|||
Error::RustError(format!("Failed to register sound from bytearray: {e:?}").into())
|
||||
})?;
|
||||
|
||||
let progress_evt = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.progressevent
|
||||
.construct(
|
||||
activation,
|
||||
&[
|
||||
"progress".into(),
|
||||
false.into(),
|
||||
false.into(),
|
||||
bytes.len().into(),
|
||||
bytes.len().into(),
|
||||
],
|
||||
)
|
||||
.map_err(|e| Error::AvmError(AvmString::new_utf8(activation.gc(), e.to_string()).into()))?;
|
||||
|
||||
Avm2::dispatch_event(&mut activation.context, progress_evt, this);
|
||||
|
||||
this.as_sound_object()
|
||||
.unwrap()
|
||||
.read_and_call_id3_event(activation, bytes);
|
||||
|
||||
this.as_sound_object()
|
||||
.unwrap()
|
||||
.set_sound(&mut activation.context, handle)?;
|
||||
|
@ -296,3 +320,16 @@ pub fn load_pcm_from_byte_array<'gc>(
|
|||
avm2_stub_method!(activation, "flash.media.Sound", "loadPCMFromByteArray");
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Implements `Sound.id3`
|
||||
pub fn get_id3<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(id3) = this.as_sound_object().unwrap().id3() {
|
||||
Ok(id3.into())
|
||||
} else {
|
||||
Ok(Value::Null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,6 +241,7 @@ include "flash/media/H264Profile.as"
|
|||
include "flash/media/Microphone.as"
|
||||
include "flash/media/MicrophoneEnhancedMode.as"
|
||||
include "flash/media/MicrophoneEnhancedOptions.as"
|
||||
include "flash/media/ID3Info.as"
|
||||
include "flash/media/Sound.as"
|
||||
include "flash/media/SoundCodec.as"
|
||||
include "flash/media/SoundChannel.as"
|
||||
|
|
|
@ -4,12 +4,16 @@ use crate::avm2::activation::Activation;
|
|||
use crate::avm2::object::script_object::ScriptObjectData;
|
||||
use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject};
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Avm2;
|
||||
use crate::avm2::Error;
|
||||
use crate::avm2::EventObject;
|
||||
use crate::backend::audio::SoundHandle;
|
||||
use crate::context::UpdateContext;
|
||||
use crate::display_object::SoundTransform;
|
||||
use crate::string::AvmString;
|
||||
use core::fmt;
|
||||
use gc_arena::{Collect, GcCell, GcWeakCell, Mutation};
|
||||
use id3::{Tag, TagLike};
|
||||
use std::cell::{Ref, RefMut};
|
||||
use swf::SoundInfo;
|
||||
|
||||
|
@ -29,6 +33,7 @@ pub fn sound_allocator<'gc>(
|
|||
sound_data: SoundData::NotLoaded {
|
||||
queued_plays: Vec::new(),
|
||||
},
|
||||
id3: None,
|
||||
},
|
||||
))
|
||||
.into())
|
||||
|
@ -58,6 +63,9 @@ pub struct SoundObjectData<'gc> {
|
|||
|
||||
/// The sound this object holds.
|
||||
sound_data: SoundData<'gc>,
|
||||
|
||||
/// ID3Info Object
|
||||
id3: Option<Object<'gc>>,
|
||||
}
|
||||
|
||||
#[derive(Collect)]
|
||||
|
@ -129,6 +137,89 @@ impl<'gc> SoundObject<'gc> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn id3(self) -> Option<Object<'gc>> {
|
||||
let this = self.0.read();
|
||||
this.id3
|
||||
}
|
||||
|
||||
pub fn set_id3(self, mc: &Mutation<'gc>, id3: Option<Object<'gc>>) {
|
||||
let mut this = self.0.write(mc);
|
||||
this.id3 = id3;
|
||||
}
|
||||
|
||||
pub fn read_and_call_id3_event(self, activation: &mut Activation<'_, 'gc>, bytes: &[u8]) {
|
||||
let id3 = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.id3info
|
||||
.construct(activation, &[])
|
||||
.expect("failed to construct ID3Info object");
|
||||
let tag = Tag::read_from(bytes);
|
||||
if let Ok(ref tag) = tag {
|
||||
if let Some(v) = tag.album() {
|
||||
id3.set_public_property(
|
||||
"album",
|
||||
AvmString::new_utf8(activation.gc(), v).into(),
|
||||
activation,
|
||||
)
|
||||
.expect("failed set_public_property");
|
||||
}
|
||||
if let Some(v) = tag.artist() {
|
||||
id3.set_public_property(
|
||||
"artist",
|
||||
AvmString::new_utf8(activation.gc(), v).into(),
|
||||
activation,
|
||||
)
|
||||
.expect("failed set_public_property");
|
||||
}
|
||||
if let Some(v) = tag.comments().next() {
|
||||
id3.set_public_property(
|
||||
"comment",
|
||||
AvmString::new_utf8(activation.gc(), v.text.clone()).into(),
|
||||
activation,
|
||||
)
|
||||
.expect("failed set_public_property");
|
||||
}
|
||||
if let Some(v) = tag.genre() {
|
||||
id3.set_public_property(
|
||||
"genre",
|
||||
AvmString::new_utf8(activation.gc(), v).into(),
|
||||
activation,
|
||||
)
|
||||
.expect("failed set_public_property");
|
||||
}
|
||||
if let Some(v) = tag.title() {
|
||||
id3.set_public_property(
|
||||
"songName",
|
||||
AvmString::new_utf8(activation.gc(), v).into(),
|
||||
activation,
|
||||
)
|
||||
.expect("failed set_public_property");
|
||||
}
|
||||
if let Some(v) = tag.track() {
|
||||
id3.set_public_property(
|
||||
"track",
|
||||
AvmString::new_utf8(activation.gc(), v.to_string()).into(),
|
||||
activation,
|
||||
)
|
||||
.expect("failed set_public_property");
|
||||
}
|
||||
if let Some(v) = tag.year() {
|
||||
id3.set_public_property(
|
||||
"year",
|
||||
AvmString::new_utf8(activation.gc(), v.to_string()).into(),
|
||||
activation,
|
||||
)
|
||||
.expect("failed set_public_property");
|
||||
}
|
||||
}
|
||||
self.set_id3(activation.context.gc_context, Some(id3));
|
||||
if tag.is_ok() {
|
||||
let id3_evt = EventObject::bare_default_event(&mut activation.context, "id3");
|
||||
Avm2::dispatch_event(&mut activation.context, id3_evt, self.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the sound had a valid position, and `false` otherwise
|
||||
|
|
|
@ -1589,11 +1589,16 @@ impl<'gc> Loader<'gc> {
|
|||
|
||||
Avm2::dispatch_event(&mut activation.context, progress_evt, sound_object);
|
||||
|
||||
sound_object
|
||||
.as_sound_object()
|
||||
.expect("Not a sound object")
|
||||
.read_and_call_id3_event(&mut activation, body.as_slice());
|
||||
|
||||
let complete_evt = Avm2EventObject::bare_default_event(
|
||||
&mut activation.context,
|
||||
"complete",
|
||||
);
|
||||
Avm2::dispatch_event(uc, complete_evt, sound_object);
|
||||
Avm2::dispatch_event(&mut activation.context, complete_evt, sound_object);
|
||||
}
|
||||
Err(_err) => {
|
||||
// FIXME: Match the exact error message generated by Flash.
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
id3event id3:[object ID3Info]
|
||||
album:test album
|
||||
artist:test artist
|
||||
comment:test comment
|
||||
genre:test genre
|
||||
songName:test title
|
||||
track:5555
|
||||
year:9999
|
|
@ -0,0 +1,24 @@
|
|||
package
|
||||
{
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import flash.media.Sound;
|
||||
import flash.net.URLRequest;
|
||||
|
||||
public class Main extends Sprite
|
||||
{
|
||||
|
||||
public function Main()
|
||||
{
|
||||
var sound:Sound = new Sound();
|
||||
sound.addEventListener(Event.ID3, function(event:Event):void{
|
||||
trace("id3event id3:" + sound.id3);
|
||||
var properties:Array = ["album", "artist", "comment", "genre", "songName", "track", "year"];
|
||||
for (var i:String in properties) trace(properties[i]+":"+sound.id3[properties[i]]);
|
||||
});
|
||||
sound.load(new URLRequest("test_audio.mp3"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
num_frames = 1
|
Binary file not shown.
Loading…
Reference in New Issue