322 lines
11 KiB
C#
322 lines
11 KiB
C#
using JD.Core;
|
|
using PleoNET;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using JD.Asset;
|
|
using JD.Asset.Types.Map;
|
|
using System.Collections.Generic;
|
|
|
|
namespace PleoStreamSimulator
|
|
{
|
|
public enum AlgorithmType
|
|
{
|
|
AlgorithmTypeStandard,
|
|
AlgorithmTypeSimpler,
|
|
}
|
|
|
|
class PleoInterface : IDisposable
|
|
{
|
|
public enum VideoState
|
|
{
|
|
VideoStateIdle,
|
|
VideoStateInit,
|
|
VideoStateReady,
|
|
VideoStatePlaying,
|
|
VideoStatePaused,
|
|
VideoStateStop,
|
|
VideoStateUnloading
|
|
}
|
|
|
|
private Map m_map;
|
|
public Map MapInfo {
|
|
get
|
|
{
|
|
return m_map;
|
|
}
|
|
set
|
|
{
|
|
m_map = value;
|
|
OnMapChanged();
|
|
}
|
|
}
|
|
|
|
private AlgorithmType m_algorithmUsage;
|
|
public AlgorithmType AlgorithmUsage
|
|
{
|
|
get
|
|
{
|
|
return m_algorithmUsage;
|
|
}
|
|
|
|
set
|
|
{
|
|
m_algorithmUsage = value;
|
|
}
|
|
}
|
|
|
|
private List<PleoNET.Vector2> m_bandwidthCurves;
|
|
public List<PleoNET.Vector2> BandwidthCurves
|
|
{
|
|
get
|
|
{
|
|
return m_bandwidthCurves;
|
|
}
|
|
|
|
set
|
|
{
|
|
m_bandwidthCurves = value;
|
|
}
|
|
}
|
|
|
|
public uint NetworkBandwidthSpeed { get; set; }
|
|
|
|
public VideoQuality StartingQualityLevel { get; set; }
|
|
|
|
private static PleoInterface ms_pleoInterfaceInstance;
|
|
|
|
private PleoVideoClip m_videoClip;
|
|
private const uint MinBandwidthSpeed = 50 * 1024 * 8; //50kbps
|
|
private const uint MaxBandwidthSpeed = 10 * 1024 * 1024 * 8; //10mbps
|
|
private VideoState m_videoState;
|
|
private VideoStats m_videoStats;
|
|
private VideoFormat m_videoFormat = VideoFormat.CLASSIC;
|
|
|
|
public static PleoInterface Instance()
|
|
{
|
|
if (ms_pleoInterfaceInstance == null)
|
|
ms_pleoInterfaceInstance = new PleoInterface();
|
|
|
|
return ms_pleoInterfaceInstance;
|
|
}
|
|
|
|
private PleoInterface()
|
|
{
|
|
NetworkBandwidthSpeed = MinBandwidthSpeed;
|
|
m_bandwidthCurves = new List<Vector2>();
|
|
|
|
StartingQualityLevel = VideoQuality.LOW;
|
|
}
|
|
|
|
public void Initialize(uint netSpeed, VideoFormat videoFormat, VideoQuality videoStartQuality)
|
|
{
|
|
NetworkBandwidthSpeed = netSpeed >= MinBandwidthSpeed && netSpeed <= MaxBandwidthSpeed ? netSpeed : MinBandwidthSpeed;
|
|
AlgorithmUsage = AlgorithmType.AlgorithmTypeStandard;
|
|
StartingQualityLevel = videoStartQuality;
|
|
m_videoFormat = videoFormat;
|
|
|
|
m_videoState = VideoState.VideoStateIdle;
|
|
|
|
PleoNetInterface.PleoInitialize(ErrorHandler.Instance().GetErrorHandlerCallback(), true);
|
|
PleoNetInterface.RegisterForUpdate(Update);
|
|
|
|
m_videoStats = new VideoStats(videoFormat);
|
|
|
|
Console.WriteLine("[PleoStreamSimulator] Pleo Initialized");
|
|
}
|
|
|
|
protected void OnMapChanged()
|
|
{
|
|
m_videoStats.SetStatsParam(MapInfo.Name);
|
|
}
|
|
|
|
public void Destroy()
|
|
{
|
|
m_videoStats.Destroy();
|
|
|
|
if (m_videoClip != null)
|
|
UnloadClip();
|
|
|
|
PleoNetInterface.UnregisterForUpdate(Update);
|
|
PleoNetInterface.PleoShutdown();
|
|
}
|
|
|
|
//Call this from the pleo timer to keep it in sync with the pleo thread
|
|
private static void Update(Object _sender, EventArgs _args)
|
|
{
|
|
Instance().Update(_args as UpdateEventArgs);
|
|
}
|
|
|
|
public void Update(UpdateEventArgs eventArgs)
|
|
{
|
|
//Video state machine
|
|
switch (m_videoState)
|
|
{
|
|
case VideoState.VideoStateIdle:
|
|
//Nothing.
|
|
break;
|
|
|
|
case VideoState.VideoStateInit:
|
|
Console.WriteLine("[PleoStreamSimulator] Initializing video");
|
|
|
|
PleoVideoClip.SetStreamingAlgorithm((uint)AlgorithmUsage);
|
|
|
|
LoadClip();
|
|
|
|
//Just initialize system clock's start & end time since it uses timeGetTime() which moves very ahead,
|
|
//even before the video is loaded and frameToDecode moves quite ahead leading a huge seek request and
|
|
//pleo fails to seek resulting in unnecessary starvation
|
|
//So let the video be in ready state, and then play() accordingly.
|
|
m_videoClip.Play();
|
|
m_videoClip.Stop();
|
|
|
|
m_videoState = VideoState.VideoStateReady;
|
|
break;
|
|
|
|
case VideoState.VideoStateReady:
|
|
if (m_videoClip.IsReadyToPlay())
|
|
{
|
|
Console.WriteLine("[PleoStreamSimulator] Video is Ready");
|
|
|
|
List<PleoNET.Vector2> scaledCurve = GetScaledBandwidthCurve(eventArgs.m_time, m_videoClip.GetVideoclipDuration(), NetworkBandwidthSpeed);
|
|
m_videoClip.SetBandwidthCurve(scaledCurve.ToArray());
|
|
|
|
ErrorHandler.EventCapturerCallback(new PleoClientErrorHandler.Event("OnVideoStart-" + eventArgs.m_time + "-" + m_videoClip.GetVideoclipDuration()));
|
|
m_videoClip.Play();
|
|
m_videoState = VideoState.VideoStatePlaying;
|
|
}
|
|
break;
|
|
|
|
case VideoState.VideoStatePlaying:
|
|
if (m_videoClip.IsStarving())
|
|
{
|
|
Console.WriteLine("[PleoStreamSimulator] Video is starving. Will pause now");
|
|
|
|
m_videoClip.Pause();
|
|
m_videoState = VideoState.VideoStatePaused;
|
|
ErrorHandler.EventCapturerCallback(new PleoClientErrorHandler.Event("OnVideoStarvingStart-Starving started-" + eventArgs.m_time));
|
|
}
|
|
else if (m_videoClip.IsFinished())
|
|
{
|
|
Console.WriteLine("[PleoStreamSimulator] Video finished. Stopping now");
|
|
|
|
m_videoClip.Stop();
|
|
m_videoState = VideoState.VideoStateStop;
|
|
}
|
|
else
|
|
{
|
|
//Have to call the image to display, just to pop the image from the queue.
|
|
//So that the decoder can push the further frames into the queue.
|
|
m_videoClip.SelectImageToDisplay();
|
|
}
|
|
break;
|
|
|
|
case VideoState.VideoStatePaused:
|
|
if (!m_videoClip.IsStarving())
|
|
{
|
|
Console.WriteLine("[PleoStreamSimulator] Resuming video");
|
|
|
|
m_videoClip.Resume();
|
|
m_videoState = VideoState.VideoStatePlaying;
|
|
|
|
ErrorHandler.EventCapturerCallback(new PleoClientErrorHandler.Event("OnVideoStarvingEnd-Starving ended-" + eventArgs.m_time));
|
|
}
|
|
break;
|
|
|
|
case VideoState.VideoStateStop:
|
|
if(m_videoClip != null)
|
|
{
|
|
m_videoState = VideoState.VideoStateUnloading;
|
|
ErrorHandler.EventCapturerCallback(
|
|
new PleoNET.PleoClientErrorHandler.Event(
|
|
"OnVideoEnd- " + PleoInterface.Instance().MapInfo + " Ended Successfully-" + eventArgs.m_time));
|
|
UnloadClip();
|
|
}
|
|
break;
|
|
|
|
case VideoState.VideoStateUnloading:
|
|
Console.WriteLine("[PleoStreamSimulator] Unloading the video");
|
|
|
|
//Do what? May be next run with other algorithm.
|
|
if (m_videoClip == null)
|
|
{
|
|
m_videoState = VideoState.VideoStateIdle;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (m_videoClip != null)
|
|
{
|
|
if (NetworkBandwidthSpeed != m_videoClip.GetNetworkBandwidthSpeed())
|
|
{
|
|
m_videoClip.SetNetworkBandwidthSpeed(NetworkBandwidthSpeed);
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsVideoFinished()
|
|
{
|
|
return m_videoState == VideoState.VideoStateIdle;
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
Debug.Assert(m_videoState == VideoState.VideoStateIdle);
|
|
m_videoState = VideoState.VideoStateInit;
|
|
}
|
|
|
|
protected void LoadClip()
|
|
{
|
|
PathInfo pathInfo = null;
|
|
switch (m_videoFormat)
|
|
{
|
|
case VideoFormat.CLASSIC:
|
|
pathInfo = MapInfo.VideoCoachMpd;
|
|
break;
|
|
case VideoFormat.HD:
|
|
pathInfo = MapInfo.VideoCoachMpdHd;
|
|
break;
|
|
case VideoFormat.VP9:
|
|
pathInfo = MapInfo.VideoCoachMpdVp9;
|
|
break;
|
|
case VideoFormat.VP8_PREVIEW:
|
|
pathInfo = MapInfo.VideoCoachMpdPreviewVp8;
|
|
break;
|
|
case VideoFormat.VP9_PREVIEW:
|
|
pathInfo = MapInfo.VideoCoachMpdPreviewVp9;
|
|
break;
|
|
};
|
|
if (!string.IsNullOrEmpty(pathInfo))
|
|
{
|
|
string customizedMpdPath = PleoMPDCustomizable.GenerateCustomizedPleoMPD(MapInfo, m_videoFormat);
|
|
PleoDashMPD mpd = new PleoDashMPD(customizedMpdPath);
|
|
|
|
//Delete the temp file, it's in the memory we don't require it anymore.
|
|
File.Delete(customizedMpdPath);
|
|
|
|
m_videoClip = new PleoVideoClip(mpd, 0, 0, _simulate:true, _startingQualityLevel:(int)StartingQualityLevel);
|
|
}
|
|
}
|
|
|
|
protected void UnloadClip()
|
|
{
|
|
if (m_videoClip != null)
|
|
{
|
|
m_videoClip.Stop();
|
|
m_videoClip.Dispose();
|
|
m_videoClip = null;
|
|
}
|
|
}
|
|
|
|
private List<PleoNET.Vector2> GetScaledBandwidthCurve(double _startTime, double _clipDuration, uint _networkBandwidthSpeed)
|
|
{
|
|
List<PleoNET.Vector2> scaledCurve = new List<PleoNET.Vector2>();
|
|
foreach (PleoNET.Vector2 unscaledPoints in m_bandwidthCurves)
|
|
{
|
|
scaledCurve.Add(new PleoNET.Vector2(
|
|
_startTime + unscaledPoints.x * _clipDuration,
|
|
unscaledPoints.y * _networkBandwidthSpeed));
|
|
}
|
|
|
|
return scaledCurve;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
m_videoClip?.Dispose();
|
|
}
|
|
}
|
|
}
|