fixes #859 - Support adaptive bitrate streaming

This commit is contained in:
Luke Pulverenti
2014-06-30 13:40:46 -04:00
parent f526a07edd
commit 8ae316a2f3
11 changed files with 122 additions and 98 deletions

View File

@@ -1417,7 +1417,6 @@ namespace MediaBrowser.Api.Playback
List<MediaStream> mediaStreams = null;
state.ItemType = item.GetType().Name;
state.ReadInputAtNativeFramerate = true;
if (item is ILiveTvRecording)
{
@@ -1479,6 +1478,7 @@ namespace MediaBrowser.Api.Playback
state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
mediaStreams = new List<MediaStream>();
state.ReadInputAtNativeFramerate = true;
state.OutputAudioSync = "1000";
state.DeInterlace = true;
state.InputVideoSync = "-1";
@@ -1489,13 +1489,13 @@ namespace MediaBrowser.Api.Playback
}
else if (item is IChannelMediaItem)
{
var source = await GetChannelMediaInfo(request.Id, request.MediaSourceId, cancellationToken).ConfigureAwait(false);
var mediaSource = await GetChannelMediaInfo(request.Id, request.MediaSourceId, cancellationToken).ConfigureAwait(false);
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
state.InputProtocol = source.Protocol;
state.MediaPath = source.Path;
state.InputProtocol = mediaSource.Protocol;
state.MediaPath = mediaSource.Path;
state.RunTimeTicks = item.RunTimeTicks;
state.RemoteHttpHeaders = source.RequiredHttpHeaders;
mediaStreams = source.MediaStreams;
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
mediaStreams = mediaSource.MediaStreams;
}
else
{
@@ -1539,6 +1539,11 @@ namespace MediaBrowser.Api.Playback
state.DeInterlace = true;
}
if (state.InputProtocol == MediaProtocol.Rtmp)
{
state.ReadInputAtNativeFramerate = true;
}
var videoRequest = request as VideoStreamRequest;
AttachMediaStreamInfo(state, mediaStreams, videoRequest, url);

View File

@@ -119,9 +119,8 @@ namespace MediaBrowser.Api.Playback.Hls
}
}
int audioBitrate;
int videoBitrate;
GetPlaylistBitrates(state, out audioBitrate, out videoBitrate);
var audioBitrate = state.OutputAudioBitrate ?? 0;
var videoBitrate = state.OutputVideoBitrate ?? 0;
var appendBaselineStream = false;
var baselineStreamBitrate = 64000;
@@ -162,37 +161,6 @@ namespace MediaBrowser.Api.Playback.Hls
return minimumSegmentCount;
}
/// <summary>
/// Gets the playlist bitrates.
/// </summary>
/// <param name="state">The state.</param>
/// <param name="audioBitrate">The audio bitrate.</param>
/// <param name="videoBitrate">The video bitrate.</param>
protected void GetPlaylistBitrates(StreamState state, out int audioBitrate, out int videoBitrate)
{
var audioBitrateParam = state.OutputAudioBitrate;
var videoBitrateParam = state.OutputVideoBitrate;
if (!audioBitrateParam.HasValue)
{
if (state.AudioStream != null)
{
audioBitrateParam = state.AudioStream.BitRate;
}
}
if (!videoBitrateParam.HasValue)
{
if (state.VideoStream != null)
{
videoBitrateParam = state.VideoStream.BitRate;
}
}
audioBitrate = audioBitrateParam ?? 0;
videoBitrate = videoBitrateParam ?? 0;
}
private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, bool includeBaselineStream, int baselineStreamBitrate)
{
var builder = new StringBuilder();

View File

@@ -304,45 +304,79 @@ namespace MediaBrowser.Api.Playback.Hls
{
var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
int audioBitrate;
int videoBitrate;
GetPlaylistBitrates(state, out audioBitrate, out videoBitrate);
var audioBitrate = state.OutputAudioBitrate ?? 0;
var videoBitrate = state.OutputVideoBitrate ?? 0;
var appendBaselineStream = false;
var baselineStreamBitrate = 64000;
var hlsVideoRequest = state.VideoRequest as GetMasterHlsVideoStream;
if (hlsVideoRequest != null)
{
appendBaselineStream = hlsVideoRequest.AppendBaselineStream;
baselineStreamBitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? baselineStreamBitrate;
}
var playlistText = GetMasterPlaylistFileText(videoBitrate + audioBitrate);
var playlistText = GetMasterPlaylistFileText(state, videoBitrate + audioBitrate);
return ResultFactory.GetResult(playlistText, Common.Net.MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
}
private string GetMasterPlaylistFileText(int bitrate)
private string GetMasterPlaylistFileText(StreamState state, int totalBitrate)
{
var builder = new StringBuilder();
builder.AppendLine("#EXTM3U");
// Pad a little to satisfy the apple hls validator
var paddedBitrate = Convert.ToInt32(bitrate * 1.05);
var queryStringIndex = Request.RawUrl.IndexOf('?');
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
// Main stream
builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + paddedBitrate.ToString(UsCulture));
var playlistUrl = "main.m3u8" + queryString;
builder.AppendLine(playlistUrl);
AppendPlaylist(builder, playlistUrl, totalBitrate);
if (state.VideoRequest.VideoBitRate.HasValue)
{
var requestedVideoBitrate = state.VideoRequest.VideoBitRate.Value;
// By default, vary by just 200k
var variation = GetBitrateVariation(totalBitrate);
var newBitrate = totalBitrate - variation;
AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate);
newBitrate = totalBitrate - (2 * variation);
AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - (2 * variation)).ToString(UsCulture)), newBitrate);
}
return builder.ToString();
}
private void AppendPlaylist(StringBuilder builder, string url, int bitrate)
{
builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture));
builder.AppendLine(url);
}
private int GetBitrateVariation(int bitrate)
{
// By default, vary by just 200k
var variation = 200000;
if (bitrate >= 10000000)
{
variation = 2000000;
}
else if (bitrate >= 5000000)
{
variation = 1500000;
}
else if (bitrate >= 3000000)
{
variation = 1000000;
}
else if (bitrate >= 2000000)
{
variation = 500000;
}
else if (bitrate >= 1000000)
{
variation = 300000;
}
return variation;
}
public object Get(GetMainHlsVideoStream request)
{
var result = GetPlaylistAsync(request, "main").Result;