detect anamorphic video

This commit is contained in:
Luke Pulverenti 2014-06-22 12:25:47 -04:00
parent 414b1251c7
commit 3b30a2aee0
23 changed files with 182 additions and 37 deletions

View File

@ -1788,7 +1788,8 @@ namespace MediaBrowser.Api.Playback
state.TargetVideoLevel, state.TargetVideoLevel,
state.TargetFramerate, state.TargetFramerate,
state.TargetPacketLength, state.TargetPacketLength,
state.TargetTimestamp); state.TargetTimestamp,
state.IsTargetAnamorphic);
if (mediaProfile != null) if (mediaProfile != null)
{ {
@ -1885,7 +1886,8 @@ namespace MediaBrowser.Api.Playback
state.TargetVideoLevel, state.TargetVideoLevel,
state.TargetFramerate, state.TargetFramerate,
state.TargetPacketLength, state.TargetPacketLength,
state.TranscodeSeekInfo state.TranscodeSeekInfo,
state.IsTargetAnamorphic
); );
} }

View File

@ -313,7 +313,7 @@ namespace MediaBrowser.Api.Playback.Hls
// See if we can save come cpu cycles by avoiding encoding // See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{ {
return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf h264_mp4toannexb" : "-codec:v:0 copy"; return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf h264_mp4toannexb -bsf dump_extra" : "-codec:v:0 copy";
} }
var keyFrameArg = state.ReadInputAtNativeFramerate ? var keyFrameArg = state.ReadInputAtNativeFramerate ?

View File

@ -160,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Hls
// See if we can save come cpu cycles by avoiding encoding // See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{ {
return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf h264_mp4toannexb" : "-codec:v:0 copy"; return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf h264_mp4toannexb -bsf dump_extra" : "-codec:v:0 copy";
} }
var keyFrameArg = state.ReadInputAtNativeFramerate ? var keyFrameArg = state.ReadInputAtNativeFramerate ?

View File

@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback.Progressive
// See if we can save come cpu cycles by avoiding encoding // See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{ {
return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf h264_mp4toannexb" : args; return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf h264_mp4toannexb -bsf dump_extra" : args;
} }
const string keyFrameArg = " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+5))"; const string keyFrameArg = " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+5))";

View File

@ -2,7 +2,6 @@
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -330,5 +329,17 @@ namespace MediaBrowser.Api.Playback
} }
} }
public bool? IsTargetAnamorphic
{
get
{
if (Request.Static)
{
return VideoStream == null ? null : VideoStream.IsAnamorphic;
}
return false;
}
}
} }
} }

View File

@ -126,6 +126,9 @@ namespace MediaBrowser.Controller.MediaEncoding
stream.RealFrameRate = GetFrameRate(streamInfo.r_frame_rate); stream.RealFrameRate = GetFrameRate(streamInfo.r_frame_rate);
stream.BitDepth = GetBitDepth(stream.PixelFormat); stream.BitDepth = GetBitDepth(stream.PixelFormat);
stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1",
StringComparison.OrdinalIgnoreCase);
} }
else else
{ {

View File

@ -6,13 +6,11 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
namespace MediaBrowser.Dlna.ConnectionManager namespace MediaBrowser.Dlna.ConnectionManager
{ {
public class ControlHandler : BaseControlHandler public class ControlHandler : BaseControlHandler
{ {
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly DeviceProfile _profile; private readonly DeviceProfile _profile;
public ControlHandler(ILogger logger, DeviceProfile profile, IServerConfigurationManager config) public ControlHandler(ILogger logger, DeviceProfile profile, IServerConfigurationManager config)

View File

@ -179,7 +179,8 @@ namespace MediaBrowser.Dlna.Didl
streamInfo.TargetVideoLevel, streamInfo.TargetVideoLevel,
streamInfo.TargetFramerate, streamInfo.TargetFramerate,
streamInfo.TargetPacketLength, streamInfo.TargetPacketLength,
streamInfo.TargetTimestamp); streamInfo.TargetTimestamp,
streamInfo.IsTargetAnamorphic);
var filename = url.Substring(0, url.IndexOf('?')); var filename = url.Substring(0, url.IndexOf('?'));
@ -203,7 +204,8 @@ namespace MediaBrowser.Dlna.Didl
streamInfo.TargetVideoLevel, streamInfo.TargetVideoLevel,
streamInfo.TargetFramerate, streamInfo.TargetFramerate,
streamInfo.TargetPacketLength, streamInfo.TargetPacketLength,
streamInfo.TranscodeSeekInfo); streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic);
res.SetAttribute("protocolInfo", String.Format( res.SetAttribute("protocolInfo", String.Format(
"http-get:*:{0}:{1}", "http-get:*:{0}:{1}",

View File

@ -514,7 +514,8 @@ namespace MediaBrowser.Dlna.PlayTo
streamInfo.TargetVideoLevel, streamInfo.TargetVideoLevel,
streamInfo.TargetFramerate, streamInfo.TargetFramerate,
streamInfo.TargetPacketLength, streamInfo.TargetPacketLength,
streamInfo.TranscodeSeekInfo); streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic);
} }
return null; return null;

View File

@ -83,7 +83,7 @@ namespace MediaBrowser.Dlna.Profiles
Container = "ts", Container = "ts",
Type = DlnaProfileType.Video, Type = DlnaProfileType.Video,
VideoCodec = "mpeg1video,mpeg2video,h264,vc1", VideoCodec = "mpeg1video,mpeg2video,h264,vc1",
AudioCodec = "ac3,dca,mp2,mp3" AudioCodec = "ac3,dca,mp2,mp3,aac"
}, },
new DirectPlayProfile new DirectPlayProfile

View File

@ -93,6 +93,20 @@ namespace MediaBrowser.Dlna.Profiles
CodecProfiles = new[] CodecProfiles = new[]
{ {
new CodecProfile
{
Type = CodecType.Video,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.NotEquals,
Property = ProfileConditionValue.IsAnamorphic,
Value = "true"
}
}
},
new CodecProfile new CodecProfile
{ {
Type = CodecType.Video, Type = CodecType.Video,

View File

@ -34,7 +34,7 @@
<DirectPlayProfile container="avi" audioCodec="ac3,dca,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" /> <DirectPlayProfile container="avi" audioCodec="ac3,dca,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
<DirectPlayProfile container="mpeg" audioCodec="ac3,dca,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video" type="Video" /> <DirectPlayProfile container="mpeg" audioCodec="ac3,dca,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video" type="Video" />
<DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" /> <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
<DirectPlayProfile container="ts" audioCodec="ac3,dca,mp2,mp3" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" /> <DirectPlayProfile container="ts" audioCodec="ac3,dca,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" />
<DirectPlayProfile container="mp4,mov" audioCodec="ac3,aac,mp2,mp3" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="mp4,mov" audioCodec="ac3,aac,mp2,mp3" videoCodec="h264,mpeg4" type="Video" />
<DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="vc1" type="Video" /> <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="vc1" type="Video" />
<DirectPlayProfile container="asf" audioCodec="mp2,ac3" videoCodec="mpeg2video" type="Video" /> <DirectPlayProfile container="asf" audioCodec="mp2,ac3" videoCodec="mpeg2video" type="Video" />

View File

@ -92,7 +92,9 @@ namespace MediaBrowser.Dlna.Service
Xml = xml, Xml = xml,
IsSuccessful = true IsSuccessful = true
}; };
Logger.Debug(xml);
//Logger.Debug(xml);
controlResponse.Headers.Add("EXT", string.Empty); controlResponse.Headers.Add("EXT", string.Empty);
return controlResponse; return controlResponse;

View File

@ -17,7 +17,8 @@ namespace MediaBrowser.Model.Dlna
double? videoLevel, double? videoLevel,
double? videoFramerate, double? videoFramerate,
int? packetLength, int? packetLength,
TransportStreamTimestamp? timestamp) TransportStreamTimestamp? timestamp,
bool? isAnamorphic)
{ {
switch (condition.Property) switch (condition.Property)
{ {
@ -27,6 +28,8 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.Has64BitOffsets:
// TODO: Implement // TODO: Implement
return true; return true;
case ProfileConditionValue.IsAnamorphic:
return IsConditionSatisfied(condition, isAnamorphic);
case ProfileConditionValue.VideoFramerate: case ProfileConditionValue.VideoFramerate:
return IsConditionSatisfied(condition, videoFramerate); return IsConditionSatisfied(condition, videoFramerate);
case ProfileConditionValue.VideoLevel: case ProfileConditionValue.VideoLevel:
@ -147,6 +150,31 @@ namespace MediaBrowser.Model.Dlna
throw new InvalidOperationException("Unexpected ProfileConditionType"); throw new InvalidOperationException("Unexpected ProfileConditionType");
} }
} }
private bool IsConditionSatisfied(ProfileCondition condition, bool? currentValue)
{
if (!currentValue.HasValue)
{
// If the value is unknown, it satisfies if not marked as required
return !condition.IsRequired;
}
bool expected;
if (BoolHelper.TryParseCultureInvariant(condition.Value, out expected))
{
switch (condition.Condition)
{
case ProfileConditionType.Equals:
return currentValue.Value == expected;
case ProfileConditionType.NotEquals:
return currentValue.Value != expected;
default:
throw new InvalidOperationException("Unexpected ProfileConditionType");
}
}
return false;
}
private bool IsConditionSatisfied(ProfileCondition condition, double? currentValue) private bool IsConditionSatisfied(ProfileCondition condition, double? currentValue)
{ {

View File

@ -108,7 +108,8 @@ namespace MediaBrowser.Model.Dlna
double? videoLevel, double? videoLevel,
double? videoFramerate, double? videoFramerate,
int? packetLength, int? packetLength,
TranscodeSeekInfo transcodeSeekInfo) TranscodeSeekInfo transcodeSeekInfo,
bool? isAnamorphic)
{ {
// first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo); string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo);
@ -145,7 +146,8 @@ namespace MediaBrowser.Model.Dlna
videoLevel, videoLevel,
videoFramerate, videoFramerate,
packetLength, packetLength,
timestamp); timestamp,
isAnamorphic);
string orgPn = mediaProfile == null ? null : mediaProfile.OrgPn; string orgPn = mediaProfile == null ? null : mediaProfile.OrgPn;

View File

@ -269,7 +269,8 @@ namespace MediaBrowser.Model.Dlna
double? videoLevel, double? videoLevel,
double? videoFramerate, double? videoFramerate,
int? packetLength, int? packetLength,
TransportStreamTimestamp timestamp) TransportStreamTimestamp timestamp,
bool? isAnamorphic)
{ {
container = (container ?? string.Empty).TrimStart('.'); container = (container ?? string.Empty).TrimStart('.');
@ -303,7 +304,7 @@ namespace MediaBrowser.Model.Dlna
var anyOff = false; var anyOff = false;
foreach (ProfileCondition c in i.Conditions) foreach (ProfileCondition c in i.Conditions)
{ {
if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp)) if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic))
{ {
anyOff = true; anyOff = true;
break; break;

View File

@ -2,18 +2,19 @@
{ {
public enum ProfileConditionValue public enum ProfileConditionValue
{ {
AudioChannels, AudioChannels = 0,
AudioBitrate, AudioBitrate = 1,
AudioProfile, AudioProfile = 2,
Width, Width = 3,
Height, Height = 4,
Has64BitOffsets, Has64BitOffsets = 5,
PacketLength, PacketLength = 6,
VideoBitDepth, VideoBitDepth = 7,
VideoBitrate, VideoBitrate = 8,
VideoFramerate, VideoFramerate = 9,
VideoLevel, VideoLevel = 10,
VideoProfile, VideoProfile = 11,
VideoTimestamp VideoTimestamp = 12,
IsAnamorphic = 13
} }
} }

View File

@ -370,6 +370,7 @@ namespace MediaBrowser.Model.Dlna
double? videoLevel = videoStream == null ? null : videoStream.Level; double? videoLevel = videoStream == null ? null : videoStream.Level;
string videoProfile = videoStream == null ? null : videoStream.Profile; string videoProfile = videoStream == null ? null : videoStream.Profile;
float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate; float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate;
bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic;
int? audioBitrate = audioStream == null ? null : audioStream.BitRate; int? audioBitrate = audioStream == null ? null : audioStream.BitRate;
int? audioChannels = audioStream == null ? null : audioStream.Channels; int? audioChannels = audioStream == null ? null : audioStream.Channels;
@ -381,7 +382,7 @@ namespace MediaBrowser.Model.Dlna
// Check container conditions // Check container conditions
foreach (ProfileCondition i in conditions) foreach (ProfileCondition i in conditions)
{ {
if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp)) if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic))
{ {
return null; return null;
} }
@ -403,7 +404,7 @@ namespace MediaBrowser.Model.Dlna
foreach (ProfileCondition i in conditions) foreach (ProfileCondition i in conditions)
{ {
if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp)) if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic))
{ {
return null; return null;
} }
@ -520,6 +521,7 @@ namespace MediaBrowser.Model.Dlna
break; break;
} }
case ProfileConditionValue.AudioProfile: case ProfileConditionValue.AudioProfile:
case ProfileConditionValue.IsAnamorphic:
case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.Has64BitOffsets:
case ProfileConditionValue.PacketLength: case ProfileConditionValue.PacketLength:
case ProfileConditionValue.VideoTimestamp: case ProfileConditionValue.VideoTimestamp:

View File

@ -348,6 +348,19 @@ namespace MediaBrowser.Model.Dlna
} }
} }
public bool? IsTargetAnamorphic
{
get
{
if (IsDirectStream)
{
return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic;
}
return false;
}
}
public int? TargetWidth public int? TargetWidth
{ {
get get

View File

@ -33,7 +33,7 @@ namespace MediaBrowser.Model.Entities
/// </summary> /// </summary>
/// <value>The channel layout.</value> /// <value>The channel layout.</value>
public string ChannelLayout { get; set; } public string ChannelLayout { get; set; }
/// <summary> /// <summary>
/// Gets or sets the bit rate. /// Gets or sets the bit rate.
/// </summary> /// </summary>
@ -155,11 +155,17 @@ namespace MediaBrowser.Model.Entities
/// </summary> /// </summary>
/// <value>The pixel format.</value> /// <value>The pixel format.</value>
public string PixelFormat { get; set; } public string PixelFormat { get; set; }
/// <summary> /// <summary>
/// Gets or sets the level. /// Gets or sets the level.
/// </summary> /// </summary>
/// <value>The level.</value> /// <value>The level.</value>
public double? Level { get; set; } public double? Level { get; set; }
/// <summary>
/// Gets a value indicating whether this instance is anamorphic.
/// </summary>
/// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value>
public bool? IsAnamorphic { get; set; }
} }
} }

View File

@ -18,4 +18,18 @@ namespace MediaBrowser.Model.Extensions
return double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out result); return double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out result);
} }
} }
public static class BoolHelper
{
/// <summary>
/// Tries the parse culture invariant.
/// </summary>
/// <param name="s">The s.</param>
/// <param name="result">The result.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
public static bool TryParseCultureInvariant(string s, out bool result)
{
return bool.TryParse(s, out result);
}
}
} }

View File

@ -116,6 +116,12 @@ namespace MediaBrowser.Server.Implementations.Library
return true; return true;
} }
} }
// Ignore samples
if (filename.IndexOf(".sample.", StringComparison.OrdinalIgnoreCase) != -1)
{
return true;
}
} }
return false; return false;

View File

@ -58,6 +58,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
AddPixelFormatColumnCommand(); AddPixelFormatColumnCommand();
AddBitDepthCommand(); AddBitDepthCommand();
AddIsAnamorphicColumn();
PrepareStatements(); PrepareStatements();
@ -126,6 +127,37 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.RunQueries(new[] { builder.ToString() }, _logger); _connection.RunQueries(new[] { builder.ToString() }, _logger);
} }
private void AddIsAnamorphicColumn()
{
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "PRAGMA table_info(mediastreams)";
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
{
while (reader.Read())
{
if (!reader.IsDBNull(1))
{
var name = reader.GetString(1);
if (string.Equals(name, "IsAnamorphic", StringComparison.OrdinalIgnoreCase))
{
return;
}
}
}
}
}
var builder = new StringBuilder();
builder.AppendLine("alter table mediastreams");
builder.AppendLine("add column IsAnamorphic BIT NULL");
_connection.RunQueries(new[] { builder.ToString() }, _logger);
}
private readonly string[] _saveColumns = private readonly string[] _saveColumns =
{ {
"ItemId", "ItemId",
@ -150,7 +182,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
"RealFrameRate", "RealFrameRate",
"Level", "Level",
"PixelFormat", "PixelFormat",
"BitDepth" "BitDepth",
"IsAnamorphic"
}; };
/// <summary> /// <summary>
@ -319,6 +352,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
item.BitDepth = reader.GetInt32(22); item.BitDepth = reader.GetInt32(22);
} }
if (!reader.IsDBNull(23))
{
item.IsAnamorphic = reader.GetBoolean(23);
}
return item; return item;
} }
@ -382,6 +420,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveStreamCommand.GetParameter(20).Value = stream.Level; _saveStreamCommand.GetParameter(20).Value = stream.Level;
_saveStreamCommand.GetParameter(21).Value = stream.PixelFormat; _saveStreamCommand.GetParameter(21).Value = stream.PixelFormat;
_saveStreamCommand.GetParameter(22).Value = stream.BitDepth; _saveStreamCommand.GetParameter(22).Value = stream.BitDepth;
_saveStreamCommand.GetParameter(23).Value = stream.IsAnamorphic;
_saveStreamCommand.Transaction = transaction; _saveStreamCommand.Transaction = transaction;
_saveStreamCommand.ExecuteNonQuery(); _saveStreamCommand.ExecuteNonQuery();