mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-07 02:42:14 +01:00
424 lines
13 KiB
C#
424 lines
13 KiB
C#
using System.IO;
|
|
using Declarations.Events;
|
|
using Declarations.Media;
|
|
using Declarations.Players;
|
|
using Implementation;
|
|
using MediaBrowser.Model.DTO;
|
|
using MediaBrowser.Model.Entities;
|
|
using MediaBrowser.UI.Configuration;
|
|
using MediaBrowser.UI.Playback.InternalPlayer;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.Composition;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using MediaState = Declarations.MediaState;
|
|
|
|
namespace MediaBrowser.UI.Playback.NVlc
|
|
{
|
|
/// <summary>
|
|
/// Class InternalMediaPlayer
|
|
/// </summary>
|
|
[Export(typeof(BaseMediaPlayer))]
|
|
public class InternalMediaPlayerNVlc : BaseInternalMediaPlayer
|
|
{
|
|
/// <summary>
|
|
/// Gets or sets the media player factory.
|
|
/// </summary>
|
|
/// <value>The media player factory.</value>
|
|
private MediaPlayerFactory MediaPlayerFactory { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the video player.
|
|
/// </summary>
|
|
/// <value>The video player.</value>
|
|
private IMediaListPlayer VideoPlayer { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the media list.
|
|
/// </summary>
|
|
/// <value>The media list.</value>
|
|
private IMediaList MediaList { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether [supports multi file playback].
|
|
/// </summary>
|
|
/// <value><c>true</c> if [supports multi file playback]; otherwise, <c>false</c>.</value>
|
|
public override bool SupportsMultiFilePlayback
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance can mute.
|
|
/// </summary>
|
|
/// <value><c>true</c> if this instance can mute; otherwise, <c>false</c>.</value>
|
|
public override bool CanMute
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance can change volume.
|
|
/// </summary>
|
|
/// <value><c>true</c> if this instance can change volume; otherwise, <c>false</c>.</value>
|
|
public override bool CanControlVolume
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance is muted.
|
|
/// </summary>
|
|
/// <value><c>true</c> if this instance is muted; otherwise, <c>false</c>.</value>
|
|
protected override bool IsMuted
|
|
{
|
|
get { return VideoPlayer != null && VideoPlayer.InnerPlayer.Mute; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The _current playlist index
|
|
/// </summary>
|
|
private int _currentPlaylistIndex;
|
|
|
|
/// <summary>
|
|
/// Gets the index of the current playlist.
|
|
/// </summary>
|
|
/// <value>The index of the current playlist.</value>
|
|
public override int CurrentPlaylistIndex
|
|
{
|
|
get
|
|
{
|
|
return _currentPlaylistIndex;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the current position ticks.
|
|
/// </summary>
|
|
/// <value>The current position ticks.</value>
|
|
public override long? CurrentPositionTicks
|
|
{
|
|
get
|
|
{
|
|
if (VideoPlayer != null)
|
|
{
|
|
return TimeSpan.FromMilliseconds(VideoPlayer.Time).Ticks;
|
|
}
|
|
|
|
return base.CurrentPositionTicks;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance can monitor progress.
|
|
/// </summary>
|
|
/// <value><c>true</c> if this instance can monitor progress; otherwise, <c>false</c>.</value>
|
|
protected override bool CanMonitorProgress
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the windows forms panel.
|
|
/// </summary>
|
|
/// <value>The windows forms panel.</value>
|
|
private Panel WindowsFormsPanel { get; set; }
|
|
|
|
/// <summary>
|
|
/// Ensures the player.
|
|
/// </summary>
|
|
protected override void EnsureMediaPlayerCreated()
|
|
{
|
|
if (MediaPlayerFactory != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WindowsFormsPanel = new Panel();
|
|
WindowsFormsPanel.BackColor = Color.Black;
|
|
|
|
App.Instance.HiddenWindow.WindowsFormsHost.Child = WindowsFormsPanel;
|
|
|
|
MediaPlayerFactory = new MediaPlayerFactory(new[]
|
|
{
|
|
"-I",
|
|
"dummy",
|
|
"--ignore-config",
|
|
"--no-osd",
|
|
"--disable-screensaver",
|
|
//"--ffmpeg-hw",
|
|
"--plugin-path=./plugins"
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Events_s the media changed.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">The e.</param>
|
|
void Events_MediaChanged(object sender, MediaPlayerMediaChanged e)
|
|
{
|
|
//var current = MediaList.FirstOrDefault(i => i.Tag == e.NewMedia.Tag);
|
|
|
|
//var newIndex = current != null ? MediaList.IndexOf(current) : -1;
|
|
|
|
//var currentIndex = _currentPlaylistIndex;
|
|
|
|
//if (newIndex != currentIndex)
|
|
//{
|
|
// OnMediaChanged(currentIndex, null, newIndex);
|
|
//}
|
|
|
|
//_currentPlaylistIndex = newIndex;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether this instance can play the specified item.
|
|
/// </summary>
|
|
/// <param name="item">The item.</param>
|
|
/// <returns><c>true</c> if this instance can play the specified item; otherwise, <c>false</c>.</returns>
|
|
public override bool CanPlay(DtoBaseItem item)
|
|
{
|
|
return item.IsVideo || item.IsAudio;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance can queue.
|
|
/// </summary>
|
|
/// <value><c>true</c> if this instance can queue; otherwise, <c>false</c>.</value>
|
|
public override bool CanQueue
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Plays the internal.
|
|
/// </summary>
|
|
/// <param name="items">The items.</param>
|
|
/// <param name="options">The options.</param>
|
|
/// <param name="playerConfiguration">The player configuration.</param>
|
|
protected override void PlayInternal(List<DtoBaseItem> items, PlayOptions options, PlayerConfiguration playerConfiguration)
|
|
{
|
|
EnsureMediaPlayerCreated();
|
|
|
|
_currentPlaylistIndex = 0;
|
|
|
|
MediaList = MediaPlayerFactory.CreateMediaList<IMediaList>(items.Select(GetPlayablePath));
|
|
VideoPlayer = MediaPlayerFactory.CreateMediaListPlayer<IMediaListPlayer>(MediaList);
|
|
|
|
VideoPlayer.InnerPlayer.WindowHandle = WindowsFormsPanel.Handle;
|
|
|
|
VideoPlayer.InnerPlayer.Events.PlayerStopped += Events_PlayerStopped;
|
|
VideoPlayer.Play();
|
|
|
|
var position = options.StartPositionTicks;
|
|
|
|
if (position > 0)
|
|
{
|
|
VideoPlayer.Time = Convert.ToInt64(TimeSpan.FromTicks(position).TotalMilliseconds);
|
|
}
|
|
|
|
VideoPlayer.MediaListPlayerEvents.MediaListPlayerNextItemSet += MediaListPlayerEvents_MediaListPlayerNextItemSet;
|
|
|
|
base.PlayInternal(items, options, playerConfiguration);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the playable path.
|
|
/// </summary>
|
|
/// <param name="item">The item.</param>
|
|
/// <returns>System.String.</returns>
|
|
private string GetPlayablePath(DtoBaseItem item)
|
|
{
|
|
if (item.VideoType.HasValue && item.VideoType.Value == VideoType.BluRay)
|
|
{
|
|
var file = Directory.EnumerateFiles(item.Path, "*.m2ts", SearchOption.AllDirectories).OrderByDescending(f => new FileInfo(f).Length).FirstOrDefault();
|
|
|
|
if (!string.IsNullOrEmpty(file))
|
|
{
|
|
return file;
|
|
}
|
|
}
|
|
|
|
return item.Path;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Medias the list player events_ media list player next item set.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">The e.</param>
|
|
void MediaListPlayerEvents_MediaListPlayerNextItemSet(object sender, MediaListPlayerNextItemSet e)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the name.
|
|
/// </summary>
|
|
/// <value>The name.</value>
|
|
public override string Name
|
|
{
|
|
get { return "Internal Player"; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance can pause.
|
|
/// </summary>
|
|
/// <value><c>true</c> if this instance can pause; otherwise, <c>false</c>.</value>
|
|
public override bool CanPause
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance can seek.
|
|
/// </summary>
|
|
/// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value>
|
|
public override bool CanSeek
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Queues the internal.
|
|
/// </summary>
|
|
/// <param name="items">The items.</param>
|
|
protected override void QueueInternal(List<DtoBaseItem> items)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Seeks the internal.
|
|
/// </summary>
|
|
/// <param name="positionTicks">The position ticks.</param>
|
|
/// <returns>Task.</returns>
|
|
protected override Task SeekInternal(long positionTicks)
|
|
{
|
|
return Task.Run(() => VideoPlayer.Time = Convert.ToInt64(TimeSpan.FromTicks(positionTicks).TotalMilliseconds));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pauses the internal.
|
|
/// </summary>
|
|
/// <returns>Task.</returns>
|
|
protected override Task PauseInternal()
|
|
{
|
|
return Task.Run(() => VideoPlayer.Pause());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Uns the pause internal.
|
|
/// </summary>
|
|
/// <returns>Task.</returns>
|
|
protected override Task UnPauseInternal()
|
|
{
|
|
return Task.Run(() => VideoPlayer.Pause());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops the internal.
|
|
/// </summary>
|
|
/// <returns>Task.</returns>
|
|
protected override Task StopInternal()
|
|
{
|
|
return Task.Run(() => VideoPlayer.Stop());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles the PlayerStopped event of the Events control.
|
|
/// </summary>
|
|
/// <param name="sender">The source of the event.</param>
|
|
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
|
|
void Events_PlayerStopped(object sender, EventArgs e)
|
|
{
|
|
OnPlayerStopped(CurrentPlaylistIndex, CurrentPositionTicks);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when [player stopped].
|
|
/// </summary>
|
|
protected override void OnPlayerStoppedInternal()
|
|
{
|
|
VideoPlayer.MediaListPlayerEvents.MediaListPlayerNextItemSet -= MediaListPlayerEvents_MediaListPlayerNextItemSet;
|
|
|
|
MediaList.Dispose();
|
|
|
|
VideoPlayer.InnerPlayer.Events.PlayerStopped -= Events_PlayerStopped;
|
|
VideoPlayer.InnerPlayer.Dispose();
|
|
|
|
//VideoPlayer.Dispose();
|
|
VideoPlayer = null;
|
|
|
|
_currentPlaylistIndex = 0;
|
|
|
|
base.OnPlayerStoppedInternal();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the volume.
|
|
/// </summary>
|
|
/// <returns>System.Int32.</returns>
|
|
protected override int GetVolume()
|
|
{
|
|
return VideoPlayer.InnerPlayer.Volume;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the volume, on a scale from 0-100
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
protected override void SetVolume(int value)
|
|
{
|
|
if (value > 0 && VideoPlayer.InnerPlayer.Mute)
|
|
{
|
|
VideoPlayer.InnerPlayer.Mute = false;
|
|
}
|
|
|
|
VideoPlayer.InnerPlayer.Volume = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the mute.
|
|
/// </summary>
|
|
/// <param name="mute">if set to <c>true</c> [mute].</param>
|
|
protected override void SetMute(bool mute)
|
|
{
|
|
VideoPlayer.InnerPlayer.Mute = mute;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases unmanaged and - optionally - managed resources.
|
|
/// </summary>
|
|
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
|
protected override void Dispose(bool dispose)
|
|
{
|
|
base.Dispose(dispose);
|
|
|
|
if (dispose)
|
|
{
|
|
if (MediaList != null)
|
|
{
|
|
MediaList.Dispose();
|
|
}
|
|
if (VideoPlayer != null)
|
|
{
|
|
if (VideoPlayer.InnerPlayer != null)
|
|
{
|
|
VideoPlayer.InnerPlayer.Dispose();
|
|
}
|
|
}
|
|
if (MediaPlayerFactory != null)
|
|
{
|
|
MediaPlayerFactory.Dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|