using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.IO; using ServiceStack; using System; using System.IO; namespace MediaBrowser.Api.Playback.Progressive { /// /// Class GetAudioStream /// [Route("/Videos/{Id}/stream.ts", "GET")] [Route("/Videos/{Id}/stream.webm", "GET")] [Route("/Videos/{Id}/stream.asf", "GET")] [Route("/Videos/{Id}/stream.wmv", "GET")] [Route("/Videos/{Id}/stream.ogv", "GET")] [Route("/Videos/{Id}/stream.mp4", "GET")] [Route("/Videos/{Id}/stream.m4v", "GET")] [Route("/Videos/{Id}/stream.mkv", "GET")] [Route("/Videos/{Id}/stream.mpeg", "GET")] [Route("/Videos/{Id}/stream.avi", "GET")] [Route("/Videos/{Id}/stream.m2ts", "GET")] [Route("/Videos/{Id}/stream.3gp", "GET")] [Route("/Videos/{Id}/stream.wmv", "GET")] [Route("/Videos/{Id}/stream.wtv", "GET")] [Route("/Videos/{Id}/stream", "GET")] [Route("/Videos/{Id}/stream.ts", "HEAD")] [Route("/Videos/{Id}/stream.webm", "HEAD")] [Route("/Videos/{Id}/stream.asf", "HEAD")] [Route("/Videos/{Id}/stream.wmv", "HEAD")] [Route("/Videos/{Id}/stream.ogv", "HEAD")] [Route("/Videos/{Id}/stream.mp4", "HEAD")] [Route("/Videos/{Id}/stream.m4v", "HEAD")] [Route("/Videos/{Id}/stream.mkv", "HEAD")] [Route("/Videos/{Id}/stream.mpeg", "HEAD")] [Route("/Videos/{Id}/stream.avi", "HEAD")] [Route("/Videos/{Id}/stream.3gp", "HEAD")] [Route("/Videos/{Id}/stream.wmv", "HEAD")] [Route("/Videos/{Id}/stream.wtv", "HEAD")] [Route("/Videos/{Id}/stream.m2ts", "HEAD")] [Route("/Videos/{Id}/stream", "HEAD")] [Api(Description = "Gets a video stream")] public class GetVideoStream : VideoStreamRequest { } /// /// Class VideoService /// public class VideoService : BaseProgressiveStreamingService { public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, httpClient, imageProcessor) { } /// /// Gets the specified request. /// /// The request. /// System.Object. public object Get(GetVideoStream request) { return ProcessRequest(request, false); } /// /// Heads the specified request. /// /// The request. /// System.Object. public object Head(GetVideoStream request) { return ProcessRequest(request, true); } /// /// Gets the command line arguments. /// /// The output path. /// The state. /// if set to true [perform subtitle conversions]. /// System.String. protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions) { // Get the output codec name var videoCodec = GetVideoCodec(state.VideoRequest); var format = string.Empty; var keyFrame = string.Empty; if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase)) { format = " -f mp4 -movflags frag_keyframe+empty_moov"; } var threads = GetNumberOfThreads(state, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)); var inputModifier = GetInputModifier(state); return string.Format("{0} -i {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} \"{8}\"", inputModifier, GetInputArgument(state), keyFrame, GetMapArgs(state), GetVideoArguments(state, videoCodec, performSubtitleConversions), threads, GetAudioArguments(state), format, outputPath ).Trim(); } /// /// Gets video arguments to pass to ffmpeg /// /// The state. /// The video codec. /// if set to true [perform subtitle conversion]. /// System.String. private string GetVideoArguments(StreamState state, string codec, bool performSubtitleConversion) { var args = "-vcodec " + codec; if (state.EnableMpegtsM2TsMode) { args += " -mpegts_m2ts_mode 1"; } // 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; } const string keyFrameArg = " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+5))"; args += keyFrameArg; var hasGraphicalSubs = state.SubtitleStream != null && state.SubtitleStream.IsGraphicalSubtitleStream; var request = state.VideoRequest; // Add resolution params, if specified if (!hasGraphicalSubs) { if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue) { args += GetOutputSizeParam(state, codec, performSubtitleConversion); } } var qualityParam = GetVideoQualityParam(state, codec, false); if (!string.IsNullOrEmpty(qualityParam)) { args += " " + qualityParam.Trim(); } // This is for internal graphical subs if (hasGraphicalSubs) { args += GetInternalGraphicalSubtitleParam(state, codec); } return args; } /// /// Gets audio arguments to pass to ffmpeg /// /// The state. /// System.String. private string GetAudioArguments(StreamState state) { // If the video doesn't have an audio stream, return a default. if (state.AudioStream == null && state.HasMediaStreams) { return string.Empty; } var request = state.Request; // Get the output codec name var codec = GetAudioCodec(request); if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { return "-acodec copy"; } var args = "-acodec " + codec; // Add the number of audio channels var channels = state.OutputAudioChannels; if (channels.HasValue) { args += " -ac " + channels.Value; } var bitrate = state.OutputAudioBitrate; if (bitrate.HasValue) { args += " -ab " + bitrate.Value.ToString(UsCulture); } args += " " + GetAudioFilterParam(state, true); return args; } } }