Skip to content
This repository has been archived by the owner on Dec 20, 2023. It is now read-only.

Commit

Permalink
添加FLV备选播放 (#324)
Browse files Browse the repository at this point in the history
* 添加Flv的支持

* 添加旧播放器
  • Loading branch information
Richasy committed Sep 28, 2021
1 parent 379fe78 commit d15c958
Show file tree
Hide file tree
Showing 16 changed files with 324 additions and 67 deletions.
4 changes: 0 additions & 4 deletions src/App/Controls/Player/BiliPlayer/BiliPlayer.Properties.cs
Expand Up @@ -22,10 +22,6 @@ public partial class BiliPlayer
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register(nameof(ViewModel), typeof(PlayerViewModel), typeof(BiliPlayer), new PropertyMetadata(PlayerViewModel.Instance));

private const string MTCName = "MTC";

private BiliPlayerTransportControls _mediaTransport;

/// <summary>
/// 视图模型.
/// </summary>
Expand Down
5 changes: 2 additions & 3 deletions src/App/Controls/Player/BiliPlayer/BiliPlayer.cs
Expand Up @@ -27,10 +27,9 @@ protected override void OnApplyTemplate()
if (ViewModel.BiliPlayer == null)
{
var mediaPlayerElement = GetTemplateChild("MediaPlayerElement") as MediaPlayerElement;
ViewModel.ApplyMediaControl(mediaPlayerElement);
var mediaElement = GetTemplateChild("MediaElement") as MediaElement;
ViewModel.ApplyMediaControl(mediaPlayerElement, mediaElement);
}

_mediaTransport = GetTemplateChild(MTCName) as BiliPlayerTransportControls;
}
}
}
19 changes: 18 additions & 1 deletion src/App/Controls/Player/BiliPlayer/BiliPlayer.xaml
Expand Up @@ -18,7 +18,8 @@
x:Name="MediaPlayerElement"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AreTransportControlsEnabled="True">
AreTransportControlsEnabled="True"
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=ViewModel.IsClassicPlayer, Converter={StaticResource BoolToVisibilityReverseConverter}}">
<MediaPlayerElement.TransportControls>
<local:BiliPlayerTransportControls
x:Name="MTC"
Expand All @@ -29,6 +30,22 @@
IsPlaybackRateEnabled="True" />
</MediaPlayerElement.TransportControls>
</MediaPlayerElement>
<MediaElement
x:Name="MediaElement"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AreTransportControlsEnabled="True"
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=ViewModel.IsClassicPlayer, Converter={StaticResource BoolToVisibilityConverter}}">
<MediaElement.TransportControls>
<local:BiliPlayerTransportControls
x:Name="ClassicMTC"
IsCompactOverlayButtonVisible="True"
IsCompactOverlayEnabled="True"
IsFullWindowButtonVisible="False"
IsPlaybackRateButtonVisible="True"
IsPlaybackRateEnabled="True" />
</MediaElement.TransportControls>
</MediaElement>
</Grid>

<Grid
Expand Down
Expand Up @@ -32,14 +32,6 @@ public BiliPlayerTransportControls()
this.DefaultStyleKey = typeof(BiliPlayerTransportControls);
this._danmakuDictionary = new Dictionary<int, List<DanmakuModel>>();
this._segmentIndex = 1;
this.DanmakuViewModel.DanmakuListAdded += OnDanmakuListAdded;
this.DanmakuViewModel.RequestClearDanmaku += OnRequestClearDanmaku;
this.DanmakuViewModel.PropertyChanged += OnDanmakuViewModelPropertyChanged;
this.DanmakuViewModel.SendDanmakuSucceeded += OnSendDanmakuSucceeded;
this.ViewModel.MediaPlayerUpdated += OnMediaPlayerUdpated;
this.SettingViewModel.PropertyChanged += OnSettingViewModelPropertyChanged;
this.ViewModel.PropertyChanged += OnViewModelPropertyChanged;
this.ViewModel.NewLiveDanmakuAdded += OnNewLiveDanmakuAdded;
this.SizeChanged += OnSizeChanged;
InitializeDanmakuTimer();
InitializeCursorTimer();
Expand Down Expand Up @@ -96,6 +88,15 @@ protected override void OnApplyTemplate()
_forwardSkipButton.Click += OnForwardSkipButtonClick;
}

this.DanmakuViewModel.DanmakuListAdded += OnDanmakuListAdded;
this.DanmakuViewModel.RequestClearDanmaku += OnRequestClearDanmaku;
this.DanmakuViewModel.PropertyChanged += OnDanmakuViewModelPropertyChanged;
this.DanmakuViewModel.SendDanmakuSucceeded += OnSendDanmakuSucceeded;
this.ViewModel.MediaPlayerUpdated += OnMediaPlayerUdpated;
this.SettingViewModel.PropertyChanged += OnSettingViewModelPropertyChanged;
this.ViewModel.PropertyChanged += OnViewModelPropertyChanged;
this.ViewModel.NewLiveDanmakuAdded += OnNewLiveDanmakuAdded;

CheckCurrentPlayerMode();
CheckDanmakuZoom();
CheckMTCControlMode();
Expand Down Expand Up @@ -331,6 +332,7 @@ private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs
if (e.PropertyName == nameof(ViewModel.CurrentFormat))
{
if (ViewModel.CurrentFormat != null &&
_formatListView != null &&
(_formatListView.SelectedItem == null ||
(_formatListView.SelectedItem as VideoFormatViewModel).Data.Quality != ViewModel.CurrentFormat.Quality))
{
Expand All @@ -340,6 +342,7 @@ private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs
else if (e.PropertyName == nameof(ViewModel.CurrentLiveQuality))
{
if (ViewModel.CurrentLiveQuality != null &&
_liveQualityListView != null &&
(_liveQualityListView.SelectedItem == null ||
(_liveQualityListView.SelectedItem as LiveQualityViewModel).Data.Quality != ViewModel.CurrentLiveQuality.Quality))
{
Expand All @@ -349,6 +352,7 @@ private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs
else if (e.PropertyName == nameof(ViewModel.CurrentPlayLine))
{
if (ViewModel.CurrentPlayLine != null &&
_liveQualityListView != null &&
(_livePlayLineListView.SelectedItem == null ||
(_livePlayLineListView.SelectedItem as LivePlayLineViewModel).Data.Order != ViewModel.CurrentPlayLine.Order))
{
Expand Down
4 changes: 4 additions & 0 deletions src/App/Resources/Converter/ObjectToBoolConverter.cs
Expand Up @@ -31,6 +31,10 @@ public object Convert(object value, Type targetType, object parameter, string la
{
result = !string.IsNullOrEmpty(str);
}
else if (value is bool b)
{
result = b;
}
else
{
result = true;
Expand Down
4 changes: 2 additions & 2 deletions src/Controller/Controller.Uwp/BiliController.Player.cs
Expand Up @@ -46,7 +46,7 @@ public async Task<string> GetOnlineViewerCountAsync(int videoId, int cid)
/// <param name="videoId">视频Id.</param>
/// <param name="partId">分P Id.</param>
/// <returns>播放信息.</returns>
public async Task<PlayerDashInformation> GetVideoPlayInformationAsync(long videoId, long partId)
public async Task<PlayerInformation> GetVideoPlayInformationAsync(long videoId, long partId)
{
var result = await _playerProvider.GetDashAsync(videoId, partId);
return result;
Expand All @@ -58,7 +58,7 @@ public async Task<PlayerDashInformation> GetVideoPlayInformationAsync(long video
/// <param name="partId">分集Id.</param>
/// <param name="seasonType">剧集类型.</param>
/// <returns>播放信息.</returns>
public async Task<PlayerDashInformation> GetPgcPlayInformationAsync(int partId, int seasonType)
public async Task<PlayerInformation> GetPgcPlayInformationAsync(int partId, int seasonType)
{
var result = await _playerProvider.GetDashAsync(partId, seasonType);
return result;
Expand Down
8 changes: 4 additions & 4 deletions src/Lib/Lib.Interfaces/IPlayerProvider.cs
Expand Up @@ -35,16 +35,16 @@ public interface IPlayerProvider
/// </summary>
/// <param name="videoId">视频Id.</param>
/// <param name="partId">视频分P的Id.</param>
/// <returns><see cref="PlayerDashInformation"/>.</returns>
Task<PlayerDashInformation> GetDashAsync(long videoId, long partId);
/// <returns><see cref="PlayerInformation"/>.</returns>
Task<PlayerInformation> GetDashAsync(long videoId, long partId);

/// <summary>
/// 获取PGC的剧集Dash播放信息.
/// </summary>
/// <param name="partId">对应剧集的Cid.</param>
/// <param name="seasonType">剧集类型.</param>
/// <returns><see cref="PlayerDashInformation"/>.</returns>
Task<PlayerDashInformation> GetDashAsync(int partId, int seasonType);
/// <returns><see cref="PlayerInformation"/>.</returns>
Task<PlayerInformation> GetDashAsync(int partId, int seasonType);

/// <summary>
/// 获取弹幕元数据信息.
Expand Down
8 changes: 4 additions & 4 deletions src/Lib/Lib.Uwp/PlayerProvider/PlayerProvider.Extension.cs
Expand Up @@ -27,7 +27,7 @@ private CancellationToken GetExpiryToken(int seconds = 5)
return source.Token;
}

private async Task<PlayerDashInformation> InternalGetDashAsync(string cid, string aid = "", string seasonType = "")
private async Task<PlayerInformation> InternalGetDashAsync(string cid, string aid = "", string seasonType = "")
{
var isPgc = string.IsNullOrEmpty(aid) && !string.IsNullOrEmpty(seasonType);

Expand Down Expand Up @@ -60,17 +60,17 @@ private async Task<PlayerDashInformation> InternalGetDashAsync(string cid, strin

var request = await _httpProvider.GetRequestMessageAsync(HttpMethod.Get, url, queryParameters, Models.Enums.RequestClientType.Web);
var response = await _httpProvider.SendAsync(request);
var data = await _httpProvider.ParseAsync<ServerResponse<PlayerDashInformation>, ServerResponse2<PlayerDashInformation>>(response, (str) =>
var data = await _httpProvider.ParseAsync<ServerResponse<PlayerInformation>, ServerResponse2<PlayerInformation>>(response, (str) =>
{
var jobj = JObject.Parse(str);
return jobj.ContainsKey("data");
});

if (data is ServerResponse<PlayerDashInformation> res1)
if (data is ServerResponse<PlayerInformation> res1)
{
return res1.Data;
}
else if (data is ServerResponse2<PlayerDashInformation> res2)
else if (data is ServerResponse2<PlayerInformation> res2)
{
return res2.Result;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Lib/Lib.Uwp/PlayerProvider/PlayerProvider.cs
Expand Up @@ -66,13 +66,13 @@ public async Task<string> GetOnlineViewerCountAsync(long videoId, long partId)
}

/// <inheritdoc/>
public async Task<PlayerDashInformation> GetDashAsync(long videoId, long partId)
public async Task<PlayerInformation> GetDashAsync(long videoId, long partId)
{
return await InternalGetDashAsync(partId.ToString(), videoId.ToString());
}

/// <inheritdoc/>
public async Task<PlayerDashInformation> GetDashAsync(int partId, int seasonType)
public async Task<PlayerInformation> GetDashAsync(int partId, int seasonType)
{
return await InternalGetDashAsync(partId.ToString(), seasonType: seasonType.ToString());
}
Expand Down
Expand Up @@ -9,7 +9,7 @@ namespace Richasy.Bili.Models.BiliBili
/// Dash播放信息.
/// </summary>
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class PlayerDashInformation
public class PlayerInformation
{
/// <summary>
/// 视频清晰度.
Expand Down Expand Up @@ -48,11 +48,17 @@ public class PlayerDashInformation
public int CodecId { get; set; }

/// <summary>
/// 视频播放信息.
/// Dash视频播放信息.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "dash", Required = Required.Default)]
public DashVideo VideoInformation { get; set; }

/// <summary>
/// Flv视频播放信息.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "durl", Required = Required.Default)]
public List<FlvItem> FlvInformation { get; set; }

/// <summary>
/// 支持的视频格式列表.
/// </summary>
Expand Down Expand Up @@ -192,6 +198,7 @@ public class SegmentBase
/// <summary>
/// 支持的格式.
/// </summary>
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class VideoFormat
{
/// <summary>
Expand All @@ -218,4 +225,41 @@ public class VideoFormat
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "superscript", Required = Required.Default)]
public string Superscript { get; set; }
}

/// <summary>
/// FLV视频条目.
/// </summary>
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class FlvItem
{
/// <summary>
/// 序号.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "order", Required = Required.Default)]
public int Order { get; set; }

/// <summary>
/// 时长.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "length", Required = Required.Default)]
public int Length { get; set; }

/// <summary>
/// 大小.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "size", Required = Required.Default)]
public int Size { get; set; }

/// <summary>
/// 播放地址.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "url", Required = Required.Default)]
public string Url { get; set; }

/// <summary>
/// 备用地址列表.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "backup_url", Required = Required.Default)]
public List<string> BackupUrls { get; set; }
}
}
5 changes: 5 additions & 0 deletions src/Models/Models.Enums/App/PreferCodec.cs
Expand Up @@ -16,5 +16,10 @@ public enum PreferCodec
/// H264高清.
/// </summary>
H264,

/// <summary>
/// FLV.
/// </summary>
Flv,
}
}
Expand Up @@ -61,7 +61,7 @@ private async Task InitializeOnlineDashVideoAsync()

mpdStr = mpdStr.Replace("{video}", videoStr)
.Replace("{audio}", audioStr)
.Replace("{bufferTime}", $"PT{_dashInformation.VideoInformation.MinBufferTime}S");
.Replace("{bufferTime}", $"PT{_playerInformation.VideoInformation.MinBufferTime}S");

var stream = new MemoryStream(Encoding.UTF8.GetBytes(mpdStr)).AsInputStream();
var soure = await AdaptiveMediaSource.CreateFromStreamAsync(stream, new Uri(_currentVideo.BaseUrl), "application/dash+xml", httpClient);
Expand All @@ -79,27 +79,65 @@ private async Task InitializeOnlineDashVideoAsync()
_currentVideoPlayer = InitializeMediaPlayer();
}

var position = TimeSpan.Zero;
if (_currentVideoPlayer.PlaybackSession != null)
{
position = _currentVideoPlayer.PlaybackSession.Position;
}
else if (_initializeProgress != TimeSpan.Zero)
{
position = _initializeProgress;
_initializeProgress = _currentVideoPlayer.PlaybackSession.Position;
}

var mediaSource = MediaSource.CreateFromAdaptiveMediaSource(soure.MediaSource);
_currentPlaybackItem = new MediaPlaybackItem(mediaSource);
_currentVideoPlayer.Source = _currentPlaybackItem;

IsClassicPlayer = false;
BiliPlayer.SetMediaPlayer(_currentVideoPlayer);
MediaPlayerUpdated?.Invoke(this, EventArgs.Empty);
_currentVideoPlayer.PlaybackSession.Position = position;
}
catch (Exception)
{
// Show error.
IsPlayInformationError = true;
PlayInformationErrorText = _resourceToolkit.GetLocaleString(Models.Enums.LanguageNames.RequestVideoFailed);
}
}

private async Task InitializeFlvVideoAsync()
{
try
{
var playList = new SYEngine.Playlist(SYEngine.PlaylistTypes.NetworkHttp);
var config = default(SYEngine.PlaylistNetworkConfigs);
config.DownloadRetryOnFail = true;
config.HttpCookie = string.Empty;
config.UniqueId = string.Empty;
config.HttpUserAgent = ServiceConstants.DefaultUserAgentString;
if (IsPgc && CurrentPgcEpisode != null)
{
config.HttpReferer = $"https://www.bilibili.com/bangumi/play/ep{CurrentPgcEpisode.Id}";
}
else
{
config.HttpReferer = string.Empty;
}

playList.NetworkConfigs = config;
foreach (var item in _flvList)
{
playList.Append(item.Url, item.Size, float.Parse((item.Length / 1000.0).ToString()));
}

if (ClassicPlayer != null)
{
_initializeProgress = ClassicPlayer.Position;
}

IsClassicPlayer = true;
ClassicPlayer.AutoPlay = IsAutoPlay;
ClassicPlayer.Source = await playList.SaveAndGetFileUriAsync();
MediaPlayerUpdated?.Invoke(this, EventArgs.Empty);
}
catch (Exception)
{
IsPlayInformationError = true;
PlayInformationErrorText = _resourceToolkit.GetLocaleString(Models.Enums.LanguageNames.RequestVideoFailed);
}
}

Expand Down

0 comments on commit d15c958

Please sign in to comment.