currentTime set to different value after loadmetadata event during seek - google-chrome

I am currently on an adventure with my sanity at stake where I try to simply play an mp3 file on my web-application. This mp3 is loaded chunk-wise in order to make the experience more responsive.
Even though the documentation of the MediaSource API is just remarkably, almost violently, useful and hundreds of well explained examples pave the way towards salvation, I find myself not able to make everything work properly.
The last issue I am facing is the seeking-functionality. The simple idea I had for this feature is to simply kill everything that's alive, burn everything down and revive again. To be more precise, if the user seeks, I stop the machinery and start playing everything from scratch, but with the desired offset (in seconds).
Everything is working so far except one last thing: currentTime, gets set to a different value after I have set it to the time that the user requested.
The track continues playing as expected but the problem is that currentTime is what's being used to visualize the current location within the track for the user.
This is how the events take place before and after the user seeks
[play-click] <--- User clicks play button
:
[timeupdate] currentTime: 00.01
[timeupdate] currentTime: 00.02
[timeupdate] currentTime: 00.03
[seek-click] <--- User seeks, stop() is called and then playFrom(seconds)
[loadstart] currentTime: 33.33 # This is the (correct) time set in playFrom(seconds)
[loadmetadata] currentTime: 10.12 # Now the time gets set to this (wrong) value
[seek] currentTime: 10.12 # The triggered seek event comes in
[seeked] currentTime: 10.12
[canplay] currentTime: 10.12
[playing] currentTime: 10.12
[timeupdate] currentTime: 10.13 # Track continues to play correctly but this value is still wrong
:
This is how seeking takes place:
public play(track: Track) {
this.track = track;
this.playFrom(0);
}
public seekTo(seconds: number) {
this.stop();
this.playFrom(seconds);
}
public stop() {
this.logger.debug('Stop ..');
this.sourceBuffer.abort();
this.audioObj.pause();
this.removeEvents(this.audioObj, this.audioEvents, this.audioEventsHandler);
this.audioObj = null;
this.sourceBuffer = null;
this.mediaSource = null;
}
Now, the playFrom(seconds) method is a bit more complicated than that but all it really does is re-create everything that I just destroyed:
private playFrom(seconds: number) {
this.logger.debug(`Start playing from ${seconds.toFixed(2)} seconds`)
// [:] (collapsed some less important code here)
// Create the media source object
this.mediaSource = new MediaSource();
this.mediaSource.addEventListener('sourceopen', this.onSourceOpen);
// Create audio element
this.audioObj = document.createElement('audio');
this.audioObj.src = URL.createObjectURL(this.mediaSource);
this.audioObj.currentTime = seconds;
this.addEvents(this.audioObj, this.audioEvents, this.audioEventsHandler);
this.logger.debug(`Object-url: ${this.audioObj.src}`);
this.state = {currentTime: seconds, playing: true};
}
How can I get the correct current-time here?
I tried another idea where I was tring to create and add a new SourceBuffer to the MediaSource and let it play the audio from there but I wasn't able to do that either.

What is the duration reported by the MediaSource (or by the media element) at the time of issuing the seek to the media element? If it is infinite, you may need to use the {set,clear}LiveSeekableRange APIs in MSE to let the implementation know if you wish to let the player seek beyond the currently buffered media's highest presentation end time. Alternatively, you could possibly set the MediaSource duration before seeking to be something finite, but at least as high as the highest currently buffered media presentation end time.

Related

Loading a movie clip using SharedObject/AS3

Ok, so I'm working on a quiz game for work. I have each question on its own movie clip (I know that's not the preferred method, but it's what I'm most familiar with and a compressed project timeline kinda forced my hand).
Because players will only be able to play the game once (there's a prize involved), I'm attempting to implement a "save/load" system which will allow players to pickup where they left off, in case their browser crashes, they accidentally close the game window, etc. I already have a SO setup to accept save data. So for example, I have the following code on frame 1 of the Q1 scene:
var currentLevel = "Q1"; //first time the var is defined. "Q1" is the name of the scene
answersSO.data.currentLevel = currentLevel;
answersSO.flush();
And I have the following code on frame 1 of the very first scene of the movie:
function checkProgress() {
if(answersSO.data.currentLevel != undefined) {
currentLevel = answersSO.data.currentLevel;
MovieClip(this.root).gotoAndPlay(1, currentLevel);
} else {
gotoAndPlay(2);
}
}
checkProgress();
First, I gotta say that I'm a real nub at AS. I test play the game up to the Q1 scene, then close the window. I restart the game and it instantly skips all of scene 1 and proceeds to scene 2 (which is an intro to the game, not Q1). No error is thrown.
What I'm looking to do is save some data to the currentLevel variable, and save that variable to the SO. At the start of the game, the script should check if the SO has any data in it. If not, then there is no save data—proceed as normal. If there is data, load it and play the last recorded scene the player was at.
I can't figure out (1) how to get this working and (2), of somewhat less importance, why no error is being thrown.
EDIT!
This is my updated code:
stop();
import flash.net.SharedObject;
var answersSO:SharedObject = SharedObject.getLocal("PWFGame"); //DECLARE SO VARIABLE
var currentProgress = "";
//CHECK SO FOR PROGRESS
checkProgress();
function checkProgress() {
if(answersSO.data.currentLevel == null) {
nameField.text = "Enter Name Here";
gotoAndPlay(2);
} else {
currentProgress = answersSO.data.currentLevel;
MovieClip(this.root).gotoAndPlay(1, currentProgress);
}
}
It does work, but the last line in the else statement is acting strangely. It skips over the first scene completely and plays the next scene from frame 1 (which is called "Intro"). Even if I change currentProgress to "Q1" it does the same thing. Why would this line of code work elsewhere and not here?
SharedObjects can be deleted by the user. A player could delete that file and start again.
If there is a login name, you should store that data on the server using a server side script (php, asp, etc.)
So add a check at the beginning and let the user play just if his name has not been stored on the server. Than call this list form flash at the beginning of the game.
For the browser crash, you can store the data only when the game is ended. So when you refresh the page you can start it again.
If there is no username, you should get other data, but it isn't so simple as an IP could change and more important data can't be retrieved by the flash player (you can do it with a desktop application made with AIR).
At the and...
.gotoAndPlay(1, currentLevel);
Remeber that the first value is the scene and the second is the frame.
Nadia
Ok, after much digging, I've discovered that gotoAndPlay() on frame 1 can cause issues. I moved the updated code block (seen above) to frame 2, and everything works.

Get Current Location Instantly

I am using the following code to request current location:
private void RequestCurrentLocation()
{
Criteria locationCriteria = new Criteria () { Accuracy = Accuracy.NoRequirement, PowerRequirement = Power.NoRequirement };
this.mgr = GetSystemService (Context.LocationService) as LocationManager;
String locationProvider = this.mgr.GetBestProvider (locationCriteria, true);
this.mgr.RequestLocationUpdates (locationProvider, 0, 0, this);
}
As I need location only once, so I call RemoveUpdates() as soon as OnLocationChanged is raised and then carry on with rest of the working:
public void OnLocationChanged (Location location)
{
this.mgr.RemoveUpdates (this);
//Rest of the method
}
I face two issues:
1) Although I have provided zero in the distance and time parameters of RequestLocationUpdates but it still needs at least 2-3 seconds before the OnLocationChanged is triggered. How can I make it instantaneous?
2) I intermittently face the issue that the OnLocationChanged event does not fire at all. Yesterday I spent the whole day to get my code working which was flawlessly working a day earlier. It's really strange that something works properly one day and the next day it simply stops even with the very same source code! Can somebody give me an idea?
Thanks.
There's no way to get an accurate, up-to-date location instantly.
For a device to determine location it needs to track GPS, WiFi, 3G towers and that depends on radio signal strength, transmission speed and other little stuff like that. You can trick the parameters of your request to try to have a faster update (for example, instead of best provider, try to use any provider available).
As a alternative approach you can request to getLastKnownLocation, this method is synchronous and replies instantly with the last location that the system found. The issue is that the "last known location" might be null (if the system never found any location), or might be very old (if the last location was days ago).

Saving and Loading progress in action script 3

I've looked everywhere for this answer..but I am not quite sure on how to do it exactly...
I am making a Flash Game using Flash AS3...
I need options where user can save and load their progress..
can somebody give a step by step on how to do this please? I am very new to actionscript...
usually other tuts shows u how to save certain variables
Cookies!
Lucky us it's very simple. Unlucky us it's not obvious how it's done:
// have that referenced all the time:
var cookies: SharedObject = SharedObject.getLocal("myGameData");
// start saving
cookies.data.progress = 50;
cookies.data.lives = 5;
cookies.data.anyRandomVariable = "my name or something";
cookies.flush();
// now it's saved
// start loading
var progress: Number = cookies.data.progress;
var lives: int = cookies.data.lives = 5;
var anyRandomBlah: String = cookies.data.anyRandomVariable;
// now it's loaded
(following the comments above...)
Yes, pretty much along those lines. I was asking questions to try to get you to see that you do actually have variables/data which you'd save. =b
If you want to save in the middle of the level, it is up to you... I don't know how you've designed your game, but basically you can write the saving code to trigger whenever you want based on any conditions you want.
For saving in the middle of levels, since you are handling the saving and loading you could try having a value like level "7.5" or whatever notation you want to indicate a level that is partway done. Or just use whole numbers like 750 and treat it as a percentage (level 7 at 50% done). And so forth. It's up to you.
A very similar question has been asked how to save a current frame value using a SharedObject. In your case, replace 'current frame' with whatever values you want to save:
Actionscript 3 saving currentframe location to local hard drive?

Actionscript 3.0 Point Object not picked up by Garbage Collection

I have just been trying to fix a few memory leaks in my project and have discovered an interesting problem. It seems like a vast majority of my 'Point' objects are not being picked up by the Garbage Collector. Each frame it creates about 5000 new Point objects and less than 10% of them seem to ever get picked up. Even when you use code like this:
var tempPoint :Point = new Point();
tempPoint = null;
Even if I repeat it over 500 times, only a tiny fraction seem to be erased. This is really stating to get on my nerves now and I was wondering if anyone has encountered this before, knows how to solve it / get around it, or cares to enlighten me on what exactly I am doing wrong.
Would love to know anyone's thoughts on this
ps. I am using The Miner to check the resource usage
Edit: Have now done a quick check where I had the program running for about an hour and although the memory usage went up about 140MB it did start garbage collecting at this point and did not go past that. So they will be picked up but not until you have several million created ;)
How long are you waiting for them to be erased?
If you are creating 5000 new objects per frame, it's probably a good idea to use an object pool.
class PointPool {
private var _points:Vector.<Point>;
public function PointPool() {
_points = new Vector.<Point>();
}
public function createPoint(x:Number, y:Number):Point {
var p:Point = null;
if( _points.length > 0 )
p = _points.pop();
else
p = new Point();
p.x = x;
p.y = y;
return p;
}
public function returnPoint(point:Point):void {
_points.push(point);
}
}
Just a thought :)
I will quote Grant Skinner to answer your question:
A very important thing to understand about the Garbage Collector in FP9 is that it’s operations are deferred. Your objects will not be removed immediately when all active references are deleted, instead they will be removed at some indeterminate time in the future (from a developer standpoint).
[...]
It’s very important to remember that you have no control over when your objects will be deallocated, so you must make them as inert as possible when you are finished with them.
So, what's happened with your code? FP created something near 5000 instances of Point. If you look the memory usage over time you may notice that it will drop to the initial value after a few seconds.
The best way to avoid this behaviour is to pool the created objects and reuse them instead of creating a new one.

AS3: Calculating the current upload speed (or throughput)

I am uploading files using the upload() method of the FileReference class. I want to display the current connection speed and I was wondering what was a good way to do that.
My current technique is to use a Timer every 1 mili second such as follows:
var speed:Function = function(event:TimerEvent):void {
speed = Math.round((currentBytes - lastBytes) / 1024);
lastBytes = currentBytes;
}
var speedTimer:Timer = new Timer(1000);
speedTimer.addEventListener(TimerEvent.TIMER, uploadSpeed);
and currentBytes gets set into the ProgressEvent.PROGRESS. This technique seems imprecise. I was wondering what other ways I could use to calculate the upload speed while uploading and display it in real time.
Any ideas or opinions are welcomed!
Thank you very much,
Rudy
If that code block is a copy and paste it certainly won't work as you have expected it to. You declare speed as a function within which you appear to redefine it as a number. I appreciate the Flash IDE let's you get away with sketchy grammar, but code like that is going to lead you into all kinds of trouble. Try to be explicit when writing your code.
Try something like this, replacing yourLoader with whatever identifier you assigned to the uploader:
private var speed:Number = 0;
private var lastBytes:uint = 0;
private function uploadSpeed(event:TimerEvent):void
{
speed = Math.round((yourLoader.currentBytes - lastBytes) / 1024);
lastBytes = yourLoader.currentBytes;
}
private var speedTimer:Timer = new Timer(1000);
speedTimer.addEventListener(TimerEvent.TIMER, uploadSpeed);
That should calculate how many bytes moved in 1 second intervals.
Edit:
You might like to make the interval a little smaller than 1000ms and calculate an average speed for your last n samples. That would make the number your users see appear more stable than it probably does right now. Make speed an Array and .push() the latest sample. Use .shift() to drop the oldest samples so that you don't lose too much accuracy. Trial and error will give you a better idea of how many samples to hold and how often to take them.
You can moniter the upload speed on the server and then send that data back to the client. This technique is often used for ajax file upload forms.