Как вы уже знаете, я переписываю на C#
свой старый видеоплеер. Он работает по такому принципу:
Form > Player GUI > Player engine
То есть, на форме лежит UserControl
, который использует движок плеера, в котором прописаны интерфейсы DirectShow.
код движка:
public int Play()
{
int res = S_OK;
if (State == PLAYER_STATE.Null)
{
res = BuildGraph();
}
if (State != PLAYER_STATE.Null)
{
_state = PLAYER_STATE.Playing;
mediaControl.Run(); //тут всегда S_FALSE. Это глюк WinAPI.
return S_OK;
}
return res;
}
public int Pause()
{
System.Diagnostics.Debug.WriteLine($"Engine before pause: {State}");
int res = State == PLAYER_STATE.Null ? S_FALSE : mediaControl.Pause();
if (res == S_OK)
{
_state = PLAYER_STATE.Paused;
}
System.Diagnostics.Debug.WriteLine($"Engine after pause: {State}");
return res;
}
Код GUI (UserControl):
private ZeratoolPlayerEngine playerEngine;
public PLAYER_STATE State => playerEngine.State;
public int Play()
{
if (State == PLAYER_STATE.Null)
{
playerEngine.GraphMode = PrefferedGraphMode;
}
int res = playerEngine.Play();
btnPlay.BackgroundImage = res == S_OK ? Resources.play_active.ToBitmap() : Resources.play_inactive;
btnPause.BackgroundImage = Resources.pause_inactive;
return res;
}
public int Pause()
{
System.Diagnostics.Debug.WriteLine($"GUI before pause: {State}");
if (State == PLAYER_STATE.Paused)
{
return S_FALSE;
}
int res = playerEngine.Pause();
System.Diagnostics.Debug.WriteLine($"GUI Pause result: {ZeratoolPlayerEngine.ErrorCodeToString(res)}");
System.Diagnostics.Debug.WriteLine($"GUI after pause: {State}");
if (res == S_OK)
{
btnPlay.BackgroundImage = Resources.play_inactive;
btnPause.BackgroundImage = Resources.pause_active.ToBitmap();
seekBar.Refresh();
UpdateTrackPositionIndicator();
}
return res;
}
код формы:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
//System.Diagnostics.Debug.WriteLine(e.KeyCode);
int errorCode;
if (activePlayer != null)
{
switch (e.KeyCode)
{
case Keys.Escape:
case Keys.Enter:
Close();
return;
case Keys.Space:
System.Diagnostics.Debug.WriteLine($"\nPressed space: {activePlayer.State}");
errorCode = PlayerPlayPause(activePlayer);
/*if (errorCode != S_OK)
{
ShowError(errorCode);
}*/
break;
case Keys.R:
System.Diagnostics.Debug.WriteLine("\nRebuild key pressed");
double pos = activePlayer.TrackPosition;
activePlayer.Clear();
if (activePlayer.Play() == S_OK && pos > 0.0)
{
activePlayer.TrackPosition = pos;
}
System.Diagnostics.Debug.WriteLine($"State after rebuild: {activePlayer.State}");
break;
}
}
}
private int PlayerPlayPause(ZeratoolPlayerGui pl)
{
switch (pl.State)
{
case PLAYER_STATE.Null:
return pl.Play();
case PLAYER_STATE.Paused:
case PLAYER_STATE.Stopped:
return pl.Play();
case PLAYER_STATE.Playing:
return pl.Pause();
default:
return S_OK;
}
}
Когда нажимаем на R
, то плеер останавливается, очищается, перестраивает Граф, и приобретает состояние Stopped
. То есть, когда Граф построен без ошибок, но еще не запущен. После этого, он сразу запускается, возвращается на позицию, где был остановлен и приобретает состояние Playing
.
Проблема в том, что если нажать R
и, не дожидаясь перестройки графа, нажать Пробел
, то IMediaControl.Pause()
вернёт S_FALSE
и состояние плеера останется равным Playing
. Хотя реально пауза сработает. В результате получается рассинхрон состояний. Видео стоит на паузе, а плеер находится в режиме Playing
.
Выхлоп консоли, когда нажали R
+Пробел
при уже запущеном видео:
Rebuild key pressed
State after rebuild: Playing
Pressed space: Playing
GUI before pause: Playing
Engine before pause: Playing
Engine after pause: Playing
GUI Pause result: S_FALSE
GUI after pause: Playing
Я так понимаю, это не у меня в коде косяк?
Переписал с игнорированием кодов возврата. Так работает, но вся логика сломалась.