update keyframe setting

This commit is contained in:
Luke Pulverenti 2015-12-12 01:49:03 -05:00
parent 3060b0c5e9
commit 02938e7bcb
10 changed files with 15 additions and 224 deletions

View File

@ -2178,6 +2178,8 @@ namespace MediaBrowser.Api.Playback
inputModifier += " " + videoDecoder; inputModifier += " " + videoDecoder;
} }
//inputModifier += " -noaccurate_seek";
return inputModifier; return inputModifier;
} }

View File

@ -291,25 +291,6 @@ namespace MediaBrowser.Api.Playback.Hls
private double[] GetSegmentLengths(StreamState state) private double[] GetSegmentLengths(StreamState state)
{ {
var result = new List<double>(); var result = new List<double>();
if (state.VideoRequest != null)
{
var encoder = GetVideoEncoder(state);
if (string.Equals(encoder, "copy", StringComparison.OrdinalIgnoreCase))
{
var videoStream = state.VideoStream;
if (videoStream.KeyFrames != null && videoStream.KeyFrames.Count > 0)
{
foreach (var frame in videoStream.KeyFrames)
{
var seconds = TimeSpan.FromMilliseconds(frame).TotalSeconds;
seconds -= result.Sum();
result.Add(seconds);
}
return result.ToArray();
}
}
}
var ticks = state.RunTimeTicks ?? 0; var ticks = state.RunTimeTicks ?? 0;
@ -936,27 +917,8 @@ namespace MediaBrowser.Api.Playback.Hls
protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
{ {
if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0)
{
Logger.Debug("Cannot stream copy video due to missing keyframe info");
return false; return false;
} //return base.CanStreamCopyVideo(request, videoStream);
var previousSegment = 0;
foreach (var frame in videoStream.KeyFrames)
{
var length = frame - previousSegment;
// Don't allow really long segments because this could result in long download times
if (length > 10000)
{
Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length);
return false;
}
previousSegment = frame;
}
return base.CanStreamCopyVideo(request, videoStream);
} }
} }
} }

View File

@ -100,6 +100,7 @@ namespace MediaBrowser.Api.Playback.Progressive
{ {
// Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
format = " -f mp4 -movflags frag_keyframe+empty_moov"; format = " -f mp4 -movflags frag_keyframe+empty_moov";
//format = " -avoid_negative_ts disabled -start_at_zero -copyts -f mp4 -movflags frag_keyframe+empty_moov";
} }
var threads = GetNumberOfThreads(state, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)); var threads = GetNumberOfThreads(state, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase));

View File

@ -15,7 +15,6 @@ namespace MediaBrowser.Controller.MediaEncoding
public IIsoMount MountedIso { get; set; } public IIsoMount MountedIso { get; set; }
public VideoType VideoType { get; set; } public VideoType VideoType { get; set; }
public List<string> PlayableStreamFileNames { get; set; } public List<string> PlayableStreamFileNames { get; set; }
public bool ExtractKeyFrameInterval { get; set; }
public MediaInfoRequest() public MediaInfoRequest()
{ {

View File

@ -135,9 +135,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames);
var extractKeyFrameInterval = request.ExtractKeyFrameInterval && request.Protocol == MediaProtocol.File && request.VideoType == VideoType.VideoFile; return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters,
return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, extractKeyFrameInterval,
GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken);
} }
@ -171,18 +169,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <param name="primaryPath">The primary path.</param> /// <param name="primaryPath">The primary path.</param>
/// <param name="protocol">The protocol.</param> /// <param name="protocol">The protocol.</param>
/// <param name="extractChapters">if set to <c>true</c> [extract chapters].</param> /// <param name="extractChapters">if set to <c>true</c> [extract chapters].</param>
/// <param name="extractKeyFrameInterval">if set to <c>true</c> [extract key frame interval].</param>
/// <param name="probeSizeArgument">The probe size argument.</param> /// <param name="probeSizeArgument">The probe size argument.</param>
/// <param name="isAudio">if set to <c>true</c> [is audio].</param> /// <param name="isAudio">if set to <c>true</c> [is audio].</param>
/// <param name="videoType">Type of the video.</param> /// <param name="videoType">Type of the video.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{MediaInfoResult}.</returns> /// <returns>Task{MediaInfoResult}.</returns>
/// <exception cref="System.ApplicationException"></exception> /// <exception cref="System.ApplicationException">ffprobe failed - streams and format are both null.</exception>
private async Task<Model.MediaInfo.MediaInfo> GetMediaInfoInternal(string inputPath, private async Task<Model.MediaInfo.MediaInfo> GetMediaInfoInternal(string inputPath,
string primaryPath, string primaryPath,
MediaProtocol protocol, MediaProtocol protocol,
bool extractChapters, bool extractChapters,
bool extractKeyFrameInterval,
string probeSizeArgument, string probeSizeArgument,
bool isAudio, bool isAudio,
VideoType videoType, VideoType videoType,
@ -262,31 +258,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol); var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
if (extractKeyFrameInterval && mediaInfo.RunTimeTicks.HasValue)
{
if (ConfigurationManager.Configuration.EnableVideoFrameByFrameAnalysis && mediaInfo.Size.HasValue)
{
foreach (var stream in mediaInfo.MediaStreams)
{
if (EnableKeyframeExtraction(mediaInfo, stream))
{
try
{
stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
_logger.ErrorException("Error getting key frame interval", ex);
}
}
}
}
}
return mediaInfo; return mediaInfo;
} }
catch catch
@ -300,132 +271,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
_ffProbeResourcePool.Release(); _ffProbeResourcePool.Release();
} }
} }
throw new ApplicationException(string.Format("FFProbe failed for {0}", inputPath));
} }
private bool EnableKeyframeExtraction(MediaSourceInfo mediaSource, MediaStream videoStream)
{
if (videoStream.Type == MediaStreamType.Video && string.Equals(videoStream.Codec, "h264", StringComparison.OrdinalIgnoreCase) &&
!videoStream.IsInterlaced &&
!(videoStream.IsAnamorphic ?? false))
{
var audioStreams = mediaSource.MediaStreams.Where(i => i.Type == MediaStreamType.Audio).ToList();
// If it has aac audio then it will probably direct stream anyway, so don't bother with this
if (audioStreams.Count == 1 && string.Equals(audioStreams[0].Codec, "aac", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return true;
}
return false;
}
private async Task<List<int>> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken)
{
inputPath = inputPath.Split(new[] { ':' }, 2).Last().Trim('"');
const string args = "-show_packets -print_format compact -select_streams v:{1} -show_entries packet=flags -show_entries packet=pts_time \"{0}\"";
var process = new Process
{
StartInfo = new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
// Must consume both or ffmpeg may hang due to deadlocks. See comments below.
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = FFProbePath,
Arguments = string.Format(args, inputPath, videoStreamIndex.ToString(CultureInfo.InvariantCulture)).Trim(),
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false
},
EnableRaisingEvents = true
};
_logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
using (process)
{
var start = DateTime.UtcNow;
process.Start();
var lines = new List<int>();
try
{
process.BeginErrorReadLine();
await StartReadingOutput(process.StandardOutput.BaseStream, lines, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
if (cancellationToken.IsCancellationRequested)
{
throw;
}
}
process.WaitForExit();
_logger.Info("Keyframe extraction took {0} seconds", (DateTime.UtcNow - start).TotalSeconds);
//_logger.Debug("Found keyframes {0}", string.Join(",", lines.ToArray()));
return lines;
}
}
private async Task StartReadingOutput(Stream source, List<int> keyframes, CancellationToken cancellationToken)
{
try
{
using (var reader = new StreamReader(source))
{
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
var lines = StringHelper.RegexSplit(text, "[\r\n]+");
foreach (var line in lines)
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
var values = line.Split('|')
.Where(i => !string.IsNullOrWhiteSpace(i))
.Select(i => i.Split('='))
.Where(i => i.Length == 2)
.ToDictionary(i => i[0], i => i[1]);
string flags;
if (values.TryGetValue("flags", out flags) && string.Equals(flags, "k", StringComparison.OrdinalIgnoreCase))
{
string pts_time;
double frameSeconds;
if (values.TryGetValue("pts_time", out pts_time) && double.TryParse(pts_time, NumberStyles.Any, CultureInfo.InvariantCulture, out frameSeconds))
{
var ms = frameSeconds * 1000;
keyframes.Add(Convert.ToInt32(ms));
}
}
}
}
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
_logger.ErrorException("Error reading ffprobe output", ex);
}
}
/// <summary> /// <summary>
/// The us culture /// The us culture
/// </summary> /// </summary>

View File

@ -209,8 +209,6 @@ namespace MediaBrowser.Model.Configuration
public bool EnableWindowsShortcuts { get; set; } public bool EnableWindowsShortcuts { get; set; }
public bool EnableVideoFrameByFrameAnalysis { get; set; }
public bool EnableDateLastRefresh { get; set; } public bool EnableDateLastRefresh { get; set; }
public string[] Migrations { get; set; } public string[] Migrations { get; set; }

View File

@ -233,8 +233,5 @@ namespace MediaBrowser.Model.Entities
/// </summary> /// </summary>
/// <value><c>null</c> if [is cabac] contains no value, <c>true</c> if [is cabac]; otherwise, <c>false</c>.</value> /// <value><c>null</c> if [is cabac] contains no value, <c>true</c> if [is cabac]; otherwise, <c>false</c>.</value>
public bool? IsCabac { get; set; } public bool? IsCabac { get; set; }
[IgnoreDataMember]
public List<int> KeyFrames { get; set; }
} }
} }

View File

@ -170,8 +170,7 @@ namespace MediaBrowser.Providers.MediaInfo
VideoType = item.VideoType, VideoType = item.VideoType,
MediaType = DlnaProfileType.Video, MediaType = DlnaProfileType.Video,
InputPath = item.Path, InputPath = item.Path,
Protocol = protocol, Protocol = protocol
ExtractKeyFrameInterval = true
}, cancellationToken).ConfigureAwait(false); }, cancellationToken).ConfigureAwait(false);

View File

@ -127,7 +127,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false); _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
var createMediaStreamsTableCommand var createMediaStreamsTableCommand
= "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, KeyFrames TEXT NULL, CodecTag TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, CodecTag TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
string[] queries = { string[] queries = {
@ -385,7 +385,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
"IsAnamorphic", "IsAnamorphic",
"RefFrames", "RefFrames",
"IsCabac", "IsCabac",
"KeyFrames",
"CodecTag" "CodecTag"
}; };
@ -2683,15 +2682,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveStreamCommand.GetParameter(index++).Value = stream.RefFrames; _saveStreamCommand.GetParameter(index++).Value = stream.RefFrames;
_saveStreamCommand.GetParameter(index++).Value = stream.IsCabac; _saveStreamCommand.GetParameter(index++).Value = stream.IsCabac;
if (stream.KeyFrames == null || stream.KeyFrames.Count == 0)
{
_saveStreamCommand.GetParameter(index++).Value = null;
}
else
{
_saveStreamCommand.GetParameter(index++).Value = string.Join(",", stream.KeyFrames.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray());
}
_saveStreamCommand.GetParameter(index++).Value = stream.CodecTag; _saveStreamCommand.GetParameter(index++).Value = stream.CodecTag;
_saveStreamCommand.Transaction = transaction; _saveStreamCommand.Transaction = transaction;
@ -2848,16 +2838,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (!reader.IsDBNull(26)) if (!reader.IsDBNull(26))
{ {
var frames = reader.GetString(26); item.CodecTag = reader.GetString(26);
if (!string.IsNullOrWhiteSpace(frames))
{
item.KeyFrames = frames.Split(',').Select(i => int.Parse(i, CultureInfo.InvariantCulture)).ToList();
}
}
if (!reader.IsDBNull(27))
{
item.CodecTag = reader.GetString(27);
} }
return item; return item;

View File

@ -303,6 +303,11 @@ namespace MediaBrowser.WebDashboard.Api
var mode = request.Mode; var mode = request.Mode;
if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
{
_fileSystem.DeleteFile(Path.Combine(path, "scripts", "registrationservices.js"));
}
// Try to trim the output size a bit // Try to trim the output size a bit
var bowerPath = Path.Combine(path, "bower_components"); var bowerPath = Path.Combine(path, "bower_components");
DeleteFilesByExtension(bowerPath, ".log"); DeleteFilesByExtension(bowerPath, ".log");