I need to play mp4 files one after another without latency (less than 5-10ms). I don't want to use vlc or xugler.
May be there is some solution to make player wait for my trigger.notify() after play() with injection of code (but MediaPlayer is final):
Object trigger = new Object();
synchronized (trigger) {
trigger.wait();
}
I need AudioClip but for mp4 file.
I retested MediaPlayer. Now I waited for READY state and started playing. Latency was 3-12ms. It's not very good but it's acceptable.
new Thread(() -> {
while (mediaPlayer.getStatus() != MediaPlayer.Status.READY) {
try {
Thread.sleep(5);
} catch (InterruptedException ex) {
System.out.println("Sleep was interrupted.");
}
}
long before = System.currentTimeMillis();
mediaPlayer.play();
while (mediaPlayer.getStatus() != MediaPlayer.Status.PLAYING) {}
long after = System.currentTimeMillis();
System.out.println("latency " +(after - before));
}).start();
Related
Greeting,
I'm currently facing a problem that my coroutine can't start. This is the first time I facing this issues and I can't find a proper solution online. Much appreciated if anyone can point me to the right direction to solve this issue.
Here are the code.
path_reference.GetDownloadUrlAsync().ContinueWith((Task<Uri> task) => {
if (!task.IsFaulted && !task.IsCanceled)
{
Debug.Log("Download URL: " + task.Result);
StartCoroutine(DownloadStuff(task.Result));
}
else
{
Debug.Log(task.Exception.ToString());
}
});
}
IEnumerator DownloadStuff(Uri uri)
{
Debug.Log("Start Download");
using (var www = UnityWebRequestTexture.GetTexture(uri))
{
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
var texture = DownloadHandlerTexture.GetContent(www);
//Texture2D texture = new Texture2D(1, 1);
//if you need sprite for SpriteRenderer or Image
Sprite sprite = Sprite.Create(texture, new Rect(0.0f, 0.0f, texture.width,
texture.height), new Vector2(0.5f, 0.5f), 100.0f);
Debug.Log("Finished downloading!");
}
Debug.Log(www.downloadProgress);
}
}'
The task returned by Firebase probably finishes execution on a thread other than the main thread, and Unity coroutines can only run on the main thread.
Unity's support of multithreading and async is pretty spotty, including "eating" some errors if the continuations of those errors would execute on another thread other than the main thread.
To fix this, you need to change the function that starts your coroutine:
try {
// Important: ConfigureAwait(true) ensures the code after this will run in the
// same thread as the code before (which is the main thread!)
var url = await path_reference.GetDownloadUrlAsync().ConfigureAwait(true);
StartCoroutine(DownloadStuff(url));
} catch (Exception ex) {
// Tip: when logging errors, use LogException and pass the whole exception,
// that way you will get pretty links to the error line in the whole stack trace.
Debug.LogException(ex);
}
As an aside, I usually have a few extension methods on all my projects to deal with that while staying in async-world instead of coroutine-world (because at least with async I can catch errors and not just "halt and catch fire" like Unity's coroutines)
The main ones are:
public static Task ToTask(this YieldInstruction self, MonoBehaviour owner) {
var source = new TaskCompletionSource<object>();
IEnumerable Routine() {
yield return self;
source.SetResult(null);
}
return source.Task;
}
private static Task SendAsync(this UnityWebRequest self, MonoBehaviour owner) {
var source = new TaskCompletionSource<object>();
await self.SendWebRequest().ToTask(owner);
if (
self.isHttpError
|| self.isNetworkError
) {
source.SetException(new Exception(request.error));
yield break;
}
source.SetResult(null);
}
Which you can use like this, inside a MonoBehaviour:
await new WaitForSeconds(0.2f).ToTask(this);
UnityWebRequest request = /* etc */;
await request.SendAsync(this);
var texture = DownloadHandlerTexture.GetContent(request);
Note that these methods do not require ConfigureAwait, since their SetResult/SetException invocations are ran from Unity-provided coroutine continuations.
Here is my code:
public void run() {
String filePath = queue.poll();
// Nothing more to play
if (filePath == null) {
isPlaying = false;
return;
}
isPlaying = true;
File file = new File(filePath);
String uriString = file.toURI().toString();
player = new MediaPlayer(new Media(uriString));
player.play();
player.setOnEndOfMedia(this);
}
As you can see, run() is called repeatedly. On every call, it gets the head element from the queue and plays it.
Sometimes it works, and sometimes play ends abruptly, with onEndOfMedia never firing.
The snippet does not show it, but I did try overriding onError, catching exceptions, etc. On occasion, the play still just ends abruptly.
Perhaps I should be firing it from a particular thread?
Edit: it's too early to say for sure, but making player a field variable might have solved the problem. Was my player garbage collected while playing?
While at it: is this the correct way of doing things? Does it introduce any memory leaks?
When I instantiate a new StageVideo instsance with stage.stageVideos[0] everything works great, but when i leave my view that's displaying the video it sticks on the stage. So when i goto another view it's still showing on the stage in the background. I tried setting sv = null, removing event listeners...etc.
I've created a StageVideoDisplay class which is instantiated in mxml like: and on view initialization i call a play() method:
if ( _path )
{
//...
// Connections
_nc = new NetConnection();
_nc.connect(null);
_ns = new NetStream(_nc);
_ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
_ns.client = this;
// Screen
_video = new Video();
_video.smoothing = true;
// Video Events
// the StageVideoEvent.STAGE_VIDEO_STATE informs you whether
// StageVideo is available
stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY,
onStageVideoState);
// in case of fallback to Video, listen to the VideoEvent.RENDER_STATE
// event to handle resize properly and know about the acceleration mode running
_video.addEventListener(VideoEvent.RENDER_STATE, videoStateChange);
//...
}
On video stage event:
if ( stageVideoInUse ) {
try {
_rc = new Rectangle(0,0,this.width,this.height);
_sv.viewPort = _rc;
} catch (e:Error) {}
} else {
try {
_video.width = this.width;
_video.height = this.height;
} catch (e:Error) {}
}
And on stage video availability event:
protected function toggleStageVideo(on:Boolean):void
{
// To choose StageVideo attach the NetStream to StageVideo
if (on)
{
stageVideoInUse = true;
if ( _sv == null )
{
try {
_sv = stage.stageVideos[0];
_sv.addEventListener(StageVideoEvent.RENDER_STATE, stageVideoStateChange);
_sv.attachNetStream(_ns);
_sv.depth = 1;
} catch (e:Error) {}
}
if (classicVideoInUse)
{
// If you use StageVideo, remove from the display list the
// Video object to avoid covering the StageVideo object
// (which is always in the background)
stage.removeChild ( _video );
classicVideoInUse = false;
}
} else
{
// Otherwise attach it to a Video object
if (stageVideoInUse)
stageVideoInUse = false;
classicVideoInUse = true;
try {
_video.attachNetStream(_ns);
stage.addChildAt(_video, 0);
} catch (e:Error) {}
}
if ( !played )
{
played = true;
_ns.play(path);
}
}
What happens is in the view when i navigator.popView(), the stageVideo remains on the stage, even in other views, and when returning to that view to play a different stream the same stream is kind of "burned" on top. I can not figure out a way to get rid of it! Thanks in advance!
In Flash Player 11, Adobe added the dispose() method to the NetStream class.
This is useful to clear the Video or StageVideo object when you're done with it.
When you call the dispose() method at runtime, you may get an exception indicating that there is no property named dispose on the NetStream object.
This occurs because Flash Builder is not compiling the app with the proper SWF version. To fix that, just add this to your compiler directives:
-swf-version=13
In the new Flash Builder 4.7, we hopefully won't have to specify the SWF version to use the newer Flash Player features.
This seems to be the best solution, but if you can't use Flash 11 (or the latest Adobe AIR), some other work arounds would be:
set the viewPort of stage video object to a rectangle that has width=0 and height=0
since stage video appears underneath your app, you can always cover the viewport (with a background color or some DisplayObject)
Ok the issue was actually that, even though it seemed like stage video was in use since i got the "Accelerated" message in status, that it was actually the video render even running and classic video was actually in use. The only thing I needed to do was add stage.removeChild( _video ) to the close() event in the class!!
I have a page that doing something, it can take 1,2 hours or even more...
After a while I get request timed out, I want that this specific page will NOT get request timed out - ever(or at least 24 hours).
How do I do it?
Thanks.
You can make a thread with a signal in it to know it its still running or not.
I suggest to use a mutex signal because is the only one that can be the same across many pools and threads.
The thread code can be:
public class RunThreadProcess
{
// Some parametres
public int cProductID;
// my thread
private Thread t = null;
// start it
public Thread Start()
{
t = new Thread(new ThreadStart(this.work));
t.IsBackground = true;
t.SetApartmentState(ApartmentState.MTA);
t.Start();
return t;
}
// actually work
private void work()
{
// while the mutex is locked, the thread is still working
Mutex mut = new Mutex("WorkA");
try
{
mut.WaitOne();
// do thread work
all parametres are available here
}
finally
{
mut.ReleaseMutex();
}
}
}
And you call it as
Mutex mut = new Mutex("WorkA");
try
{
if(mut.WaitOne(1000))
{
// release it here to start it from the thread as signal
mut.ReleaseMutex();
// you start the thread
var OneAction = new RunThreadProcess();
OneAction.cProductID = 100;
OneAction.Start();
}
else
{
// still running
}
}
finally
{
mut.ReleaseMutex();
}
I have a VideoDisplay instance playing some video. When I click on the video slider (also my component) the property videoDisplay.playheadTime is set and the videoDisplay.state goes from 'playing' into a 'seeking' state for a brief moment (the videoDisplay seeks for a new position and then plays the video again). Intended bevaiour.
But if I'm (or any random user) fast enough, I can set the playheadTime again while the player is still in 'seeking' state. When repeated several times every click is enqueued and the videoDisplay jump on every place of the video I have clicked(this is happening in an interval about 10-15 second after my last click). When I use live dragging the videoDisplay, overwhelmed by seekings, goes into 'error' state.
My question is - is there any way to cancel seeking state of the VideoDisplay class? For example player is in 'seeking' state, I set playheadTime, and the player forgets about last seeking and try to find the new place of the video.
I will downvote pointless answers like 'Use the Flex4 VideoPlayer class'!
One possible way is wrap the video display in a component and manage the seek a little better yourself. So if someone calls seek, make sure that the video is not currently seeking, if so, then wait till the current operation is complete before proceeding to the new one. If the user tries to seek again, discard all currently pending operations and make the latest one the next operation. Working on this exact problem right now.... Here's the code:
public function Seek(nSeconds:Number, bPlayAfter:Boolean):void
{
trace("Player Seek: "+ nSeconds);
var objSeekComand:VideoPlayerSeekCommand = new VideoPlayerSeekCommand(ucPlayer, nSeconds, bPlayAfter);
ProcessCommand(objSeekComand);
}
protected function ProcessCommand(objCommand:ICommand):void
{
if(_objCurrentCommand != null)
{
_objCurrentCommand.Abort();
}
_objCurrentCommand = objCommand
objCommand.SignalCommandComplete.add(OnCommandComplete);
objCommand.Execute();
}
Here's the Command
public class VideoPlayerSeekCommand extends CommandBase
{
private var _ucVideoDisplay:VideoDisplay;
private var _nSeekPoint:Number;
private var _bPlayAfterSeek:Boolean;
private var _bIsExecuting:Boolean;
public function VideoPlayerSeekCommand(ucVideoDisplay:VideoDisplay, nSeekPointInSeconds:Number, bPlayAfterSeek:Boolean, fAutoAttachSignalHandler:Function = null)
{
_ucVideoDisplay = ucVideoDisplay;
_nSeekPoint = nSeekPointInSeconds;
_bPlayAfterSeek = bPlayAfterSeek;
super(fAutoAttachSignalHandler);
}
override public function Execute():void
{
//First check if we are playing, and puase if needed
_bIsExecuting = true;
if(_ucVideoDisplay.playing == true)
{
_ucVideoDisplay.addEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, OnPlayerStateChangedFromPlay, false, 0, true);
_ucVideoDisplay.pause();
}
else
{
DoSeek();
}
}
protected function OnPlayerStateChangedFromPlay(event:MediaPlayerStateChangeEvent):void
{
_ucVideoDisplay.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, OnPlayerStateChangedFromPlay);
if(_bIsExecuting == true)
{
if(_ucVideoDisplay.playing == false)
{
DoSeek();
}
else
{
throw new Error("VideoPlayerSeekAndPlayCommand - OnPlayerStateChangedFromPlay error");
}
}
}
private function DoSeek():void
{
if(_bIsExecuting == true)
{
_ucVideoDisplay.seek(_nSeekPoint);
CheckSeekComplete();
}
}
private function CheckSeekComplete():void
{
if(_bIsExecuting == true)
{
if (Math.abs( _ucVideoDisplay.currentTime - _nSeekPoint) < 2)
{
if(_bPlayAfterSeek == true)
{
_ucVideoDisplay.play();
}
DispatchAndDestroy();
}
else
{
CoreUtils.CallLater(CheckSeekComplete, .07);
}
}
}
override public function Abort():void
{
_bIsExecuting = false;
SignalCommandComplete.removeAll();
}
}
Im Using AS3 Signals here instead of events, and the CoreUtils.Call later you can use setInterval, or a Timer. But the idea is to not call seek until the video is paused, and to keep track of when the seek is complete.