diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 9fd233d8f..b6e77188b 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1788,7 +1788,8 @@ namespace MediaBrowser.Api.Playback state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, - state.TargetTimestamp); + state.TargetTimestamp, + state.IsTargetAnamorphic); if (mediaProfile != null) { @@ -1885,7 +1886,8 @@ namespace MediaBrowser.Api.Playback state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, - state.TranscodeSeekInfo + state.TranscodeSeekInfo, + state.IsTargetAnamorphic ); } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 8ade68028..aa39b8c9d 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -313,7 +313,7 @@ namespace MediaBrowser.Api.Playback.Hls // See if we can save come cpu cycles by avoiding encoding 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 ? diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 17683c267..e5b9d0985 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -160,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Hls // See if we can save come cpu cycles by avoiding encoding 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 ? diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index bad592e31..11ae21e64 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback.Progressive // See if we can save come cpu cycles by avoiding encoding 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))"; diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 8a9de6edc..cf3b63efb 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -2,7 +2,6 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; -using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; 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; + } + } } } diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs index 37dd06da9..17ab0f31c 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs @@ -126,6 +126,9 @@ namespace MediaBrowser.Controller.MediaEncoding stream.RealFrameRate = GetFrameRate(streamInfo.r_frame_rate); stream.BitDepth = GetBitDepth(stream.PixelFormat); + + stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", + StringComparison.OrdinalIgnoreCase); } else { diff --git a/MediaBrowser.Dlna/ConnectionManager/ControlHandler.cs b/MediaBrowser.Dlna/ConnectionManager/ControlHandler.cs index d41b4a5bf..958d71a2b 100644 --- a/MediaBrowser.Dlna/ConnectionManager/ControlHandler.cs +++ b/MediaBrowser.Dlna/ConnectionManager/ControlHandler.cs @@ -6,13 +6,11 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; -using System.Globalization; namespace MediaBrowser.Dlna.ConnectionManager { public class ControlHandler : BaseControlHandler { - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly DeviceProfile _profile; public ControlHandler(ILogger logger, DeviceProfile profile, IServerConfigurationManager config) diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index a8f7c2e7e..ecaeb6399 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -179,7 +179,8 @@ namespace MediaBrowser.Dlna.Didl streamInfo.TargetVideoLevel, streamInfo.TargetFramerate, streamInfo.TargetPacketLength, - streamInfo.TargetTimestamp); + streamInfo.TargetTimestamp, + streamInfo.IsTargetAnamorphic); var filename = url.Substring(0, url.IndexOf('?')); @@ -203,7 +204,8 @@ namespace MediaBrowser.Dlna.Didl streamInfo.TargetVideoLevel, streamInfo.TargetFramerate, streamInfo.TargetPacketLength, - streamInfo.TranscodeSeekInfo); + streamInfo.TranscodeSeekInfo, + streamInfo.IsTargetAnamorphic); res.SetAttribute("protocolInfo", String.Format( "http-get:*:{0}:{1}", diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index ccf12dc33..e8e681cb7 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -514,7 +514,8 @@ namespace MediaBrowser.Dlna.PlayTo streamInfo.TargetVideoLevel, streamInfo.TargetFramerate, streamInfo.TargetPacketLength, - streamInfo.TranscodeSeekInfo); + streamInfo.TranscodeSeekInfo, + streamInfo.IsTargetAnamorphic); } return null; diff --git a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs index 4c9ee84fe..992692b03 100644 --- a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs +++ b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs @@ -83,7 +83,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "ts", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,h264,vc1", - AudioCodec = "ac3,dca,mp2,mp3" + AudioCodec = "ac3,dca,mp2,mp3,aac" }, new DirectPlayProfile diff --git a/MediaBrowser.Dlna/Profiles/WindowsPhoneProfile.cs b/MediaBrowser.Dlna/Profiles/WindowsPhoneProfile.cs index 80cc8ad71..66949c533 100644 --- a/MediaBrowser.Dlna/Profiles/WindowsPhoneProfile.cs +++ b/MediaBrowser.Dlna/Profiles/WindowsPhoneProfile.cs @@ -93,6 +93,20 @@ namespace MediaBrowser.Dlna.Profiles CodecProfiles = new[] { + new CodecProfile + { + Type = CodecType.Video, + Conditions = new [] + { + new ProfileCondition + { + Condition = ProfileConditionType.NotEquals, + Property = ProfileConditionValue.IsAnamorphic, + Value = "true" + } + } + }, + new CodecProfile { Type = CodecType.Video, diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml index 84d5885c6..5f57fa9ed 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml @@ -34,7 +34,7 @@ - + diff --git a/MediaBrowser.Dlna/Service/BaseControlHandler.cs b/MediaBrowser.Dlna/Service/BaseControlHandler.cs index 2d5f4edb3..9f7e87088 100644 --- a/MediaBrowser.Dlna/Service/BaseControlHandler.cs +++ b/MediaBrowser.Dlna/Service/BaseControlHandler.cs @@ -92,7 +92,9 @@ namespace MediaBrowser.Dlna.Service Xml = xml, IsSuccessful = true }; - Logger.Debug(xml); + + //Logger.Debug(xml); + controlResponse.Headers.Add("EXT", string.Empty); return controlResponse; diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index 64ce2aaba..61428e39b 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -17,7 +17,8 @@ namespace MediaBrowser.Model.Dlna double? videoLevel, double? videoFramerate, int? packetLength, - TransportStreamTimestamp? timestamp) + TransportStreamTimestamp? timestamp, + bool? isAnamorphic) { switch (condition.Property) { @@ -27,6 +28,8 @@ namespace MediaBrowser.Model.Dlna case ProfileConditionValue.Has64BitOffsets: // TODO: Implement return true; + case ProfileConditionValue.IsAnamorphic: + return IsConditionSatisfied(condition, isAnamorphic); case ProfileConditionValue.VideoFramerate: return IsConditionSatisfied(condition, videoFramerate); case ProfileConditionValue.VideoLevel: @@ -147,6 +150,31 @@ namespace MediaBrowser.Model.Dlna 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) { diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index c97c06d34..55418e13b 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -108,7 +108,8 @@ namespace MediaBrowser.Model.Dlna double? videoLevel, double? videoFramerate, 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 string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo); @@ -145,7 +146,8 @@ namespace MediaBrowser.Model.Dlna videoLevel, videoFramerate, packetLength, - timestamp); + timestamp, + isAnamorphic); string orgPn = mediaProfile == null ? null : mediaProfile.OrgPn; diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 3cb3b383e..fb670a5de 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -269,7 +269,8 @@ namespace MediaBrowser.Model.Dlna double? videoLevel, double? videoFramerate, int? packetLength, - TransportStreamTimestamp timestamp) + TransportStreamTimestamp timestamp, + bool? isAnamorphic) { container = (container ?? string.Empty).TrimStart('.'); @@ -303,7 +304,7 @@ namespace MediaBrowser.Model.Dlna var anyOff = false; 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; break; diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs index 56a322f5a..544a01184 100644 --- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs +++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs @@ -2,18 +2,19 @@ { public enum ProfileConditionValue { - AudioChannels, - AudioBitrate, - AudioProfile, - Width, - Height, - Has64BitOffsets, - PacketLength, - VideoBitDepth, - VideoBitrate, - VideoFramerate, - VideoLevel, - VideoProfile, - VideoTimestamp + AudioChannels = 0, + AudioBitrate = 1, + AudioProfile = 2, + Width = 3, + Height = 4, + Has64BitOffsets = 5, + PacketLength = 6, + VideoBitDepth = 7, + VideoBitrate = 8, + VideoFramerate = 9, + VideoLevel = 10, + VideoProfile = 11, + VideoTimestamp = 12, + IsAnamorphic = 13 } } \ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 5828ded99..5e8ba3ff1 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -370,6 +370,7 @@ namespace MediaBrowser.Model.Dlna double? videoLevel = videoStream == null ? null : videoStream.Level; string videoProfile = videoStream == null ? null : videoStream.Profile; float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate; + bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic; int? audioBitrate = audioStream == null ? null : audioStream.BitRate; int? audioChannels = audioStream == null ? null : audioStream.Channels; @@ -381,7 +382,7 @@ namespace MediaBrowser.Model.Dlna // Check container 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; } @@ -403,7 +404,7 @@ namespace MediaBrowser.Model.Dlna 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; } @@ -520,6 +521,7 @@ namespace MediaBrowser.Model.Dlna break; } case ProfileConditionValue.AudioProfile: + case ProfileConditionValue.IsAnamorphic: case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.PacketLength: case ProfileConditionValue.VideoTimestamp: diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index b5aded684..9d23597fa 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -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 { get diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 838095832..7be8a64b7 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Model.Entities /// /// The channel layout. public string ChannelLayout { get; set; } - + /// /// Gets or sets the bit rate. /// @@ -155,11 +155,17 @@ namespace MediaBrowser.Model.Entities /// /// The pixel format. public string PixelFormat { get; set; } - + /// /// Gets or sets the level. /// /// The level. public double? Level { get; set; } + + /// + /// Gets a value indicating whether this instance is anamorphic. + /// + /// true if this instance is anamorphic; otherwise, false. + public bool? IsAnamorphic { get; set; } } } diff --git a/MediaBrowser.Model/Extensions/DoubleHelper.cs b/MediaBrowser.Model/Extensions/DoubleHelper.cs index bcaf2d780..00e3bc624 100644 --- a/MediaBrowser.Model/Extensions/DoubleHelper.cs +++ b/MediaBrowser.Model/Extensions/DoubleHelper.cs @@ -18,4 +18,18 @@ namespace MediaBrowser.Model.Extensions return double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out result); } } + + public static class BoolHelper + { + /// + /// Tries the parse culture invariant. + /// + /// The s. + /// The result. + /// true if XXXX, false otherwise. + public static bool TryParseCultureInvariant(string s, out bool result) + { + return bool.TryParse(s, out result); + } + } } diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index c830c13b8..17118f4b4 100644 --- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -116,6 +116,12 @@ namespace MediaBrowser.Server.Implementations.Library return true; } } + + // Ignore samples + if (filename.IndexOf(".sample.", StringComparison.OrdinalIgnoreCase) != -1) + { + return true; + } } return false; diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs index 58ca432bb..1e7d9fe82 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs @@ -58,6 +58,7 @@ namespace MediaBrowser.Server.Implementations.Persistence AddPixelFormatColumnCommand(); AddBitDepthCommand(); + AddIsAnamorphicColumn(); PrepareStatements(); @@ -126,6 +127,37 @@ namespace MediaBrowser.Server.Implementations.Persistence _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 = { "ItemId", @@ -150,7 +182,8 @@ namespace MediaBrowser.Server.Implementations.Persistence "RealFrameRate", "Level", "PixelFormat", - "BitDepth" + "BitDepth", + "IsAnamorphic" }; /// @@ -319,6 +352,11 @@ namespace MediaBrowser.Server.Implementations.Persistence item.BitDepth = reader.GetInt32(22); } + if (!reader.IsDBNull(23)) + { + item.IsAnamorphic = reader.GetBoolean(23); + } + return item; } @@ -382,6 +420,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveStreamCommand.GetParameter(20).Value = stream.Level; _saveStreamCommand.GetParameter(21).Value = stream.PixelFormat; _saveStreamCommand.GetParameter(22).Value = stream.BitDepth; + _saveStreamCommand.GetParameter(23).Value = stream.IsAnamorphic; _saveStreamCommand.Transaction = transaction; _saveStreamCommand.ExecuteNonQuery();