I'm working on a tour in webvr and using a-frame to build it. I have a bizarre problem. I seem to be able to get aframe to play a video inside a videosphere and correctly display every second of it the first time I enter a new scene, but whenever I exit from it and try to enter it again, only the sound works as supposed. I'm wondering if I'm doing something wrong in the loading of the video or something
I'm collecting the path to the video from a json file in which I describe what each rooms contains (they may have interactable pins for 16:9 video, images and the sort, and also pins that simply load a new scene).
After loading the json, I set the source of the videosphere, name image360, as such:
document.getElementById("image360").setAttribute('src', "#" + jsonArray.zones[zoneID].locations[locationID].name);
I then play the video using the following code :
video = document.querySelector('#' + jsonArray.zones[zoneID].locations[locationID].name);
video.muted = false;
video.addEventListener("ended", videoEnded);
video.play();
The event listener I add to the video takes care of taking the user back to the previous scene once the video ends, which I do using this code:
//This function is called immediately after the end of a 360 video. Thus it first starts by obtaining the scene it should load after the end of the scene
var thisEl = document.querySelector('#' + jsonArray.zones[zoneID].locations[locationID].name);
var currentLocation = jsonArray.zones[zoneID].locations[locationID];
var locationToReturnTo = currentLocation.locationToReturnTo;
var zoneToReturnTo = currentLocation.zoneToReturnTo;
//With the information obtained, the room is then loaded
generateRoom(zoneToReturnTo, locationToReturnTo);
//After loading the room, time to generate the correct pins
generatePins(zoneToReturnTo, locationToReturnTo);
I'm truly at a loss here, and have no idea why this doesn't work. I should note that javascript and aframe are not my area of expertise at all, I just had to pick up this project after a former colleague of mine, who was working on it, left the company abruptly, so excuse me if I'm making a basic mistake.
Thanks in advance.
Switching videos directly on a entity may not work properly:
document.querySelector("a-video").setAttribute("src", "vid.mp4")
because of the current tmp <video> handling.
You should try using the assets management system:
<a-assets>
<video id="vid" src="derby.mp4"></video>
</a-assets>
<!-- Scene. -->
<a-plane src="#vid"></a-plane>
JS
(#vid).setAttribute("src", "newvid.mp4")
Related
A notable issue that's appearing as I'm building a simple audio streaming element in HTML5 is that the <audio> tag doesn't behave as one would expect in regards to playing and pausing a live audio stream.
I'm using the most basic HTML5 code for streaming the audio, an <audio> tag with controls, the source of which is a live stream.
Current outcome: When the stream is first played, it plays whatever is streaming as expected. When it's paused and played again, however, the audio resumes exactly where it left off when the stream was previously paused. The user is now listening to a delayed version of the stream. This occurrence isn't browser-specific.
Desired outcome: When the stream is paused, I want the stream to stop. When it is played again, I want it resume where the stream is currently at, not where it was when the user paused the stream.
Does anyone know of a way to make this audio stream resume properly after it's been paused?
Some failed attempts I've made to fix this issue:
Altering the currentTime of the audio element does nothing to streaming audio.
I've removed the audio element from the DOM when the user stops stream playback and added it back in when user resumes playback. The stream still continues where the user left off and worse yet downloads another copy of the stream behind the scenes.
I've added a random GET variable to the end of the stream URL every time the stream is played in an attempt to fool the browser into believing that it's playing a new stream. Playback still resumes where the user paused the stream.
Best way to stop a stream, and then start it again seems to be removing the source and then calling load:
var sourceElement = document.querySelector("source");
var originalSourceUrl = sourceElement.getAttribute("src");
var audioElement = document.querySelector("audio");
function pause() {
sourceElement.setAttribute("src", "");
audioElement.pause();
// settimeout, otherwise pause event is not raised normally
setTimeout(function () {
audioElement.load(); // This stops the stream from downloading
});
}
function play() {
if (!sourceElement.getAttribute("src")) {
sourceElement.setAttribute("src", originalSourceUrl);
audioElement.load(); // This restarts the stream download
}
audioElement.play();
}
Resetting the audio source and calling the load() method seems to be the simplest solution when you want to stop downloading from the stream.
Since it's a stream, the browser will stop downloading only when the user gets offline. Resetting is necessary to protect your users from burning through their cellular data or to avoid serving outdated content that the browser downloaded when they paused the audio.
Keep in mind though that when the source attribute is set to an empty string, like so audio.src = "", the audio source will instead be set to the page's hostname. If you use a random word, that word will be appended as a path.
So as seen below, setting audio.src ="", means that audio.src === "https://stacksnippets.net/js". Setting audio.src="meow" will make the source be audio.src === "https://stacksnippets.net/js/meow" instead. Thus the 3d paragraph is not visible.
const audio1 = document.getElementById('audio1');
const audio2 = document.getElementById('audio2');
document.getElementById('p1').innerHTML = `First audio source: ${audio1.src}`;
document.getElementById('p2').innerHTML = `Second audio source: ${audio2.src}`;
if (audio1.src === "") {
document.getElementById('p3').innerHTML = "You can see me because the audio source is set to an empty string";
}
<audio id="audio1" src=""></audio>
<audio id="audio2" src="meow"></audio>
<p id="p1"></p>
<p id="p2"></p>
<p id="p3"></p>
Be aware of that behavior if you do rely on the audio's source at a given moment. Using the about URI scheme seems to trick it into behaving in a more reliable way. So using "about:" or "about:about", "about:blank", etc. will work fine.
const resetAudioSource = "about:"
const audio = document.getElementById('audio');
audio.src = resetAudioSource;
document.getElementById('p1').innerHTML = `Audio source: -- "${audio.src}"`;
// Somewhere else in your code...
if (audio.src === resetAudioSource){
document.getElementById('p2').innerHTML = "You can see me because you reset the audio source."
}
<audio id="audio"></audio>
<p id="p1"></p>
<p id="p2"></p>
Resetting the audio.src and calling the .load() method will make the audio to try to load the new source. The above comes in handy if you want to show a spinner component while the audio is loading, but don't want to also show that component when you reset your audio source.
A working example can be found here: https://jsfiddle.net/v2xuczrq/
If the source is reset using a random word, then you might end up with the loader showing up when you also pause the audio, or until the onError event handler catches it. https://jsfiddle.net/jcwvue0s/
UPDATE: The strings "javascript:;" and "javascript:void(0)" can be used instead of the "about:" URI and this seems to work even better as it will also stop the console warnings caused by "about:".
Note: I'm leaving this answer for the sake of posterity, since it was the best solution I or anyone could come up with at the time for my issue. But I've since marked Ciantic's later idea as the best solution because it actually stops the stream downloading and playback like I originally wanted. Consider that solution instead of this one.
One solution I came up with while troubleshooting this issue was to ignore the play and pause functions on the audio element entirely and just set the volume property of the audio element to 0 when user wishes to stop playback and then set the volume property back to 1 when the user wishes to resume playback.
The JavaScript code for such a function would look much like this if you're using jQuery (also demonstrated in this fiddle):
/*
* Play/Stop Live Audio Streams
* "audioElement" should be a jQuery object
*/
function streamPlayStop(audioElement) {
if (audioElement[0].paused) {
audioElement[0].play();
} else if (!audioElement[0].volume) {
audioElement[0].volume = 1;
} else {
audioElement[0].volume = 0;
}
}
I should caution that even though this achieves the desired functionality for stopping and resuming live audio streams, it isn't ideal because the stream, when stopped, is actually still playing and being downloaded in the background, using up bandwidth in the process.
However, this solution doesn't necessarily take up more bandwidth than just using .play() and .pause() on a streaming audio element. Simply using the audio tag with streaming audio uses up a great deal of bandwidth anyway, because once streaming audio is played, it continues to download the contents of the stream in the background when it is paused.
It should be noted that this method won't work in iOS because of purposefully built-in limitations for iPhones and iPads:
On iOS devices, the audio level is always under the user’s physical control. The volume property is not settable in JavaScript. Reading the volume property always returns 1.
If you choose to use the workaround in this answer, you'll need to create a fallback for iOS devices that uses the play() and pause() functions normally, or your interface will be unable to pause the stream.
Tested #Ciantics code and it worked with some modifications, if you want to use multiple sources.
As the source is getting removed, the HTML audio player becomes inactive, so the source (URL) needs to be added directly after again to become active.
Also added an event listener at the end to connect the function when pausing:
var audioElement = document.querySelector("audio");
var sources = document.querySelector("audio").children;
var sourceList = [];
for(i=0;i<sources.length;i++){
sourceList[i] = sources[i].getAttribute("src");
}
function pause() {
for(i=0;i<sources.length;i++){
sources[i].setAttribute("src", "");
}
audioElement.pause();
// settimeout, otherwise pause event is not raised normally
setTimeout(function () {
audioElement.load(); // This stops the stream from downloading
});
for(i=0;i<sources.length;i++){
if (!sources[i].getAttribute("src")) {
sources[i].setAttribute("src", sourceList[i]);
audioElement.load(); // This restarts the stream download
}
}
}
audioElement.addEventListener("pause", pause);
I'm interested in uploading a series of files to my web server and directing viewers to page which will autoplay the videos from a specific point dependent on the current time. My intention is to create the illusion of a live stream or actual TV channel, where they are unable to control the playback, but will return to the same point if they refresh the page.
I'm having difficulty finding answers, since it's descriptively so close to an actual webcast.
Here's my thought process on a solution.
Use the JavaScript Date Object API to capture the current time
Include your video with preload set to true, and controls false
Then use onLoad() and setup your video in JS
$(video).get(0).currentTime = XX; //You need an algorithm based on the time & length of video
$(video).get(0).play();
I am new to actionScript programming. I know some html and I am currently learning html5. I need to do an interactive video by putting html content in a specific time of the video. I'll be more concise:
For example, I have a video that is 5 minutes long, let's suppose that from the second 3:50 to 4:00 I need to display two boxes over the video, each one representing one choice. If at 3:50 the video shows the possibility to the viewer to select among two paths (the video told the user to select among those paths for instance) the viewer will have the possibility to select one of the paths by clicking on one of the two boxes that will appear in that time interval. I know this needs to be made with the tag and with hyperlinks.
My question is How do I tell the html5 video player to display a canvas from the minute 3:50 to the minute 4:00 in which two hyperlinks will display??
Thanks for your attention I will appreciate very much your help. I need some kind of guidance because I have been looking for many days.
For your use case it seems you want to be able to control the video flow of the user through interactions that jump to different times in the video.
Using html5 video player to seek to a different time in a video (using currentTime) you could create a click event on a box that you lay on top of the video and set the time when you click that box, using:
// Jump 30 seconds into the video
var time = '30';
var video = document.createElement('video');
video.src = "video.mp4";
// Set the time
video.currentTime = time;
video.play();
You can check out how we created an interactive video authoring tool(open source) using html5 and JS and use that.
If you don't want to spend time coding an interactive video you should check out H5Ps authoring tool through this simple example. You can test out creating your own at H5P.org as well. The tool is completely free.
I may be wrong, but I believe that you mean javascript instead of actionscript. If that is the case then I would definitely check this out Video.JS.
When you reach the current time you trigger your method/function which adds whatever you want on top of the video.
var whereYouAt = myPlayer.currentTime();
However, if you DO mean actionscript then you are working with a flash player. Therefore I suggest you take a look at this Vimeo Player
currentTime:Number [read-only] Returns the current playback time of the video.
I'm on an AS3 project, playing a video (H264). I want, for some special reasons, to go to a certain position.
a) I try it with NetStream.seek(). There it only goes to keyframes. In my current setting, this means, i can find a position every 1 second. (for a better resolution, i'd have to encode the movie with as many keyframes as possible, aka every frame a keyframe)
this is definetly not my favourite way, because I don't want to reencode all the vids.
b) I try it with NetStream.step(). This should give me the opportunity to step slowly from frame to frame. But in the documentation it says:
This method is available only when data is streaming from Flash Media Server 3.5.3 or higher and when NetStream.inBufferSeek is true.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#step()
Does this mean, it is not possible with Air for Desktop? When I try it, nothing works.
Any suggestions, how to solve this problem?
Greetings & Thank you!
Nicolas
Flash video can only be advanced by seconds unless you have Flash Media Server hosting your video. Technically, that means that you can have it working as intended in Air, however, the video would have to be streaming (silly adobe...).
You have two options:
1) Import the footage as a movieclip. The Flash IDE has a wizard for this, and if you're developing exclusively in non-FlashIDE environment, you can convert and export as an external asset such as an SWF or SWC. This would then be embedded or runtime loaded into your app giving you access to the per-frame steppable methods of MovieClip. This, however, does come with some audio syncing issues (iirc). Also, scrubbing backwards is not an MC's forté.
2) Write your own video object that loads an image sequence and displays each frame in order. You'd have to setup your own audio syncing abilities, but it might be the most direct solution apart from FLVComponent or NetStream.
I've noticed that flash player 9 scrubs nice and smooth but in players 10+ I get this no scrub problem.
My fix, was to limit frequency the calls to the seek function to <= 200ms. This fixed scrubbing but is much less smooth as player 9. Perhaps because of the "Flash video can only be advanced by seconds" limitation? I used a timer to tigger the function that calls seek() for the video.
private var scrubInterval:Timer = new Timer(200);
private function videoScrubberTouch():void {
_ns.pause();
var bounds:Rectangle = new Rectangle(0,0,340,0);
scrubInterval.addEventListener(TimerEvent.TIMER, scrubTimeline);
scrubInterval.start();
videoThumb.startDrag(false, bounds);
}
private function scrubTimeline(e:TimerEvent):void {
var amt:Number = Math.floor((videoThumb.x / 340) * duration);
trace("SCRUB duration: "+duration+" videoThumb.x: "+videoThumb.x+" amt "+amt);
_ns.seek(amt);
}
Please check this Demo link (or get the SWF file to test outside of browser via desktop Flash Player).
Note: Demo requires FLV with H.264 video codec and AAC or MP3 audio codec.
The source code for that is here: Github link
In the above demo there is (bytes-based) seeking and frame by frame stepping. The functions you want to study mainly are:
Append_SEEK ( position amount ) - This will got to the specified position in bytes and search for the nearest available keyframe.
get_frame_TAG - This will extract a tag holding one frame of data. Audio can be in frames too but lets assume you have video-only. That function is your opportunity to adjust timestamps. When it's run it will also append the tag (so each "get_frame_TAG" is also a "frame step").
For example : You have a 25fps video, you want the third-frame at 4 seconds into playback...
1000 milisecs / 25 fps = 40 units for each timestamp. So 4000 ms == 4 secs + add the 40 x 3rd frame == an expected timestamp of 4120.
So getting that frame means... First find a keyframe. Then step through each frame checking the timestamps that represent a frame you want. If it isnt then change it to the same as most recent keyframe timestamp (this forces Flash to fast-forward through the frames to keep things in sync as it assumes the frame [with smaller than expected timestamp] should have been played by that time). You can "hide" the video object during this process if you don't like the look of fast-forwarding.
For HTML5 Audio, let's say you have a list of two songs you want to play. Currently I have it set up so that when the current song stops playing, it loads the new song and plays it. I want to have it so that it loads the next song while the current song is finishing, maybe 20 seconds before the current song finishes. I tried to change the src attribute for the audio object while the song is playing, but that just immediately stops playback for the current song. Is there some other method that allows me to preload the next song while the current song is playing?
You could use jQuery to create a jQuery object:
var nextSong = document.createElement('audio'); //Creates <audio></audio>
nextSong = $(nextSong); //Converts it to a jQuery object
$(nextSong).attr('autoplay') = false; //You don't want this dynamically loaded audio to start playing automatically
$(nextSong).attr('preload') = "auto"; //Make sure it starts loading the file
$(nextSong).attr('src') = url_to_src; //Loads the src
This should start load the song into an element in the browser's memory and when the song is over, call something like:
$(audio).replace(nextSong);
This isn't tested. You probably don't even need jQuery.
This may work without jQuery:
var nextSong = document.createElement('audio');
nextSong.autoplay = 'false';
nextSong.preload = 'auto';
nextSong.src = url_to_src;
Give it a whirl and let me know!
This might be off the mark, but have you tried calling the element's load() method?
http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#loading-the-media-resource
Edit:
Ah, I think I misunderstood the problem. You want to play two songs back to back in the same media element? I'm not sure how feasible that is... it might be easier to place each song in its own Audio element. You could always dynamically generate these, if you're worried about flexibility.