using ytLive.Services.Audio;
using ytLive.Services.Encoder;
namespace ytLive.Services;
///
/// TASK 21: decodes a local media file (any format ffmpeg handles) into
/// s for a MediaSource. This is the
/// shape over the shared — the same pipe an alert clip uses
/// (TASK 47), so the two features cannot drift apart again in how they decode.
/// Video AND the file's own soundtrack are decoded. The manager scales the audio by
/// the layer's level/mute and hands it to the mixer's desktop group.
///
public sealed class MediaVideoSource : IMediaFrameSource
{
public event Action? FrameAvailable;
public event Action? AudioAvailable;
public event Action? Completed;
public string Key => _path;
public bool Looping
{
get => _clip.Looping;
set => _clip.Looping = value;
}
private readonly string _path;
private readonly VideoClipPipe _clip;
public MediaVideoSource(
string path,
int width,
int height,
IFfmpegLocator locator,
Func processFactory,
IFrameRateProbe? frameRateProbe = null,
Func? delay = null)
{
_path = path;
_clip = new VideoClipPipe(
path, width, height, locator, processFactory, frameRateProbe, delay, audio: true);
_clip.FrameAvailable += frame => FrameAvailable?.Invoke(frame);
_clip.AudioAvailable += sample => AudioAvailable?.Invoke(sample);
_clip.Completed += () => Completed?.Invoke();
}
public Task StartAsync(CancellationToken cancellationToken = default) => _clip.StartAsync(cancellationToken);
public Task StopAsync() => _clip.StopAsync();
public void Dispose() => _clip.Dispose();
}