JD2022-TU1/main/tools/apps/PleoStreamSimulator/Source/PleoInterface.cs

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();
}
}
}