Ejemplo n.º 1
0
        public async Task <object> Post(OpenMediaSource request)
        {
            var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);

            var result = await _mediaSourceManager.OpenLiveStream(request, false, CancellationToken.None).ConfigureAwait(false);

            var profile = request.DeviceProfile;

            if (profile == null)
            {
                var caps = _deviceManager.GetCapabilities(authInfo.DeviceId);
                if (caps != null)
                {
                    profile = caps.DeviceProfile;
                }
            }

            if (profile != null)
            {
                var item = _libraryManager.GetItemById(request.ItemId);

                SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate,
                                      request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex,
                                      request.SubtitleStreamIndex, request.PlaySessionId, request.UserId);
            }
            else
            {
                if (!string.IsNullOrWhiteSpace(result.MediaSource.TranscodingUrl))
                {
                    result.MediaSource.TranscodingUrl += "&LiveStreamId=" + result.MediaSource.LiveStreamId;
                }
            }

            return(ToOptimizedResult(result));
        }
Ejemplo n.º 2
0
        public async Task <object> Post(GetPostedPlaybackInfo request)
        {
            var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);

            var profile = request.DeviceProfile;

            //Logger.Info("GetPostedPlaybackInfo profile: {0}", _json.SerializeToString(profile));

            if (profile == null)
            {
                var caps = _deviceManager.GetCapabilities(authInfo.DeviceId);
                if (caps != null)
                {
                    profile = caps.DeviceProfile;
                }
            }

            var info = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }, request.MediaSourceId, request.LiveStreamId).ConfigureAwait(false);

            if (profile != null)
            {
                var mediaSourceId = request.MediaSourceId;

                SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.UserId);
            }

            return(ToOptimizedResult(info));
        }
        public async Task <object> Post(CreateCollection request)
        {
            var userId = AuthorizationContext.GetAuthorizationInfo(Request).UserId;

            var parentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);

            var item = await _collectionManager.CreateCollection(new CollectionCreationOptions
            {
                IsLocked   = request.IsLocked,
                Name       = request.Name,
                ParentId   = parentId,
                ItemIdList = (request.Ids ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList(),
                UserIds    = new List <Guid> {
                    new Guid(userId)
                }
            }).ConfigureAwait(false);

            var dtoOptions = GetDtoOptions(request);

            var dto = _dtoService.GetBaseItemDto(item, dtoOptions);

            return(ToOptimizedResult(new CollectionCreationResult
            {
                Id = dto.Id
            }));
        }
Ejemplo n.º 4
0
        public async Task PostAsync(UpdateUserPassword request)
        {
            AssertCanUpdateUser(_userManager, request.Id);

            var user = _userManager.GetUserById(request.Id);

            if (user == null)
            {
                throw new ResourceNotFoundException("User not found");
            }

            if (request.ResetPassword)
            {
                await _userManager.ResetPassword(user).ConfigureAwait(false);
            }
            else
            {
                var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPassword, Request.RemoteIp).ConfigureAwait(false);

                if (!success)
                {
                    throw new ArgumentException("Invalid user or password entered.");
                }

                await _userManager.ChangePassword(user, request.NewPassword).ConfigureAwait(false);

                var currentToken = AuthorizationContext.GetAuthorizationInfo(Request).Token;

                await _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), currentToken).ConfigureAwait(false);
            }
        }
Ejemplo n.º 5
0
        public async Task <object> Post(AuthenticateUserByName request)
        {
            var auth = AuthorizationContext.GetAuthorizationInfo(Request);

            if (string.IsNullOrWhiteSpace(auth.Client))
            {
                auth.Client = "Unknown app";
            }
            if (string.IsNullOrWhiteSpace(auth.Device))
            {
                auth.Device = "Unknown device";
            }
            if (string.IsNullOrWhiteSpace(auth.Version))
            {
                auth.Version = "Unknown version";
            }
            if (string.IsNullOrWhiteSpace(auth.DeviceId))
            {
                auth.DeviceId = "Unknown device id";
            }

            var result = await _sessionMananger.AuthenticateNewSession(new AuthenticationRequest
            {
                App            = auth.Client,
                AppVersion     = auth.Version,
                DeviceId       = auth.DeviceId,
                DeviceName     = auth.Device,
                PasswordSha1   = request.Password,
                PasswordMd5    = request.PasswordMd5,
                RemoteEndPoint = Request.RemoteIp,
                Username       = request.Username
            }).ConfigureAwait(false);

            return(ToOptimizedResult(result));
        }
Ejemplo n.º 6
0
        private void AddSubtitles(StreamState state, IEnumerable <MediaStream> subtitles, StringBuilder builder)
        {
            var selectedIndex = state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Hls ? (int?)null : state.SubtitleStream.Index;

            foreach (var stream in subtitles)
            {
                const string format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},AUTOSELECT=YES,URI=\"{3}\",LANGUAGE=\"{4}\"";

                var name = stream.DisplayTitle;

                var isDefault = selectedIndex.HasValue && selectedIndex.Value == stream.Index;
                var isForced  = stream.IsForced;

                var url = string.Format("{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}&api_key={3}",
                                        state.Request.MediaSourceId,
                                        stream.Index.ToString(CultureInfo.InvariantCulture),
                                        30.ToString(CultureInfo.InvariantCulture),
                                        AuthorizationContext.GetAuthorizationInfo(Request).Token);

                var line = string.Format(format,
                                         name,
                                         isDefault ? "YES" : "NO",
                                         isForced ? "YES" : "NO",
                                         url,
                                         stream.Language ?? "Unknown");

                builder.AppendLine(line);
            }
        }
Ejemplo n.º 7
0
        private async Task UpdateUserPolicy(UpdateUserPolicy request)
        {
            var user = _userManager.GetUserById(request.Id);

            // If removing admin access
            if (!request.IsAdministrator && user.Policy.IsAdministrator)
            {
                if (_userManager.Users.Count(i => i.Policy.IsAdministrator) == 1)
                {
                    throw new ArgumentException("There must be at least one user in the system with administrative access.");
                }
            }

            // If disabling
            if (request.IsDisabled && user.Policy.IsAdministrator)
            {
                throw new ArgumentException("Administrators cannot be disabled.");
            }

            // If disabling
            if (request.IsDisabled && !user.Policy.IsDisabled)
            {
                if (_userManager.Users.Count(i => !i.Policy.IsDisabled) == 1)
                {
                    throw new ArgumentException("There must be at least one enabled user in the system.");
                }

                var currentToken = AuthorizationContext.GetAuthorizationInfo(Request).Token;
                await _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), currentToken).ConfigureAwait(false);
            }

            await _userManager.UpdateUserPolicy(request.Id, request).ConfigureAwait(false);
        }
Ejemplo n.º 8
0
        public async Task <object> Get(GetRecording request)
        {
            var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId);

            var options = new DtoOptions();

            options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;

            var result = await _liveTvManager.GetRecording(request.Id, options, CancellationToken.None, user).ConfigureAwait(false);

            return(ToOptimizedSerializedResultUsingCache(result));
        }
Ejemplo n.º 9
0
        public object Get(GetSyncDialogOptions request)
        {
            var result = new SyncDialogOptions();

            result.Targets = _syncManager.GetSyncTargets(request.UserId)
                             .ToList();

            if (!string.IsNullOrWhiteSpace(request.TargetId))
            {
                result.Targets = result.Targets
                                 .Where(i => string.Equals(i.Id, request.TargetId, StringComparison.OrdinalIgnoreCase))
                                 .ToList();

                result.QualityOptions = _syncManager
                                        .GetQualityOptions(request.TargetId)
                                        .ToList();

                result.ProfileOptions = _syncManager
                                        .GetProfileOptions(request.TargetId)
                                        .ToList();
            }

            if (request.Category.HasValue)
            {
                result.Options = SyncHelper.GetSyncOptions(request.Category.Value);
            }
            else
            {
                var dtoOptions = new DtoOptions
                {
                    Fields = new List <ItemFields>
                    {
                        ItemFields.SyncInfo
                    }
                };

                var auth = AuthorizationContext.GetAuthorizationInfo(Request);

                var authenticatedUser = _userManager.GetUserById(auth.UserId);

                var items = request.ItemIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                            .Select(_libraryManager.GetItemById)
                            .Where(i => i != null);

                var dtos = _dtoService.GetBaseItemDtos(items, dtoOptions, authenticatedUser)
                           .ToList();

                result.Options = SyncHelper.GetSyncOptions(dtos);
            }

            return(ToOptimizedResult(result));
        }
Ejemplo n.º 10
0
        public void Post(ReportPlaybackStopped request)
        {
            if (!string.IsNullOrWhiteSpace(request.PlaySessionId))
            {
                ApiEntryPoint.Instance.KillTranscodingJobs(AuthorizationContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true);
            }

            request.SessionId = GetSession().Result.Id;

            var task = _sessionManager.OnPlaybackStopped(request);

            Task.WaitAll(task);
        }
Ejemplo n.º 11
0
        public void Post(ValidatePinRequest request)
        {
            MyPinStatus status;

            if (!_activeRequests.TryGetValue(request.Pin, out status))
            {
                throw new ResourceNotFoundException();
            }

            EnsureValid(status);

            status.IsConfirmed = true;
            status.UserId      = AuthorizationContext.GetAuthorizationInfo(Request).UserId;
        }
Ejemplo n.º 12
0
        public async Task <object> Get(GetSubtitlePlaylist request)
        {
            var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));

            var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, null, false, CancellationToken.None).ConfigureAwait(false);

            var builder = new StringBuilder();

            var runtime = mediaSource.RunTimeTicks ?? -1;

            if (runtime <= 0)
            {
                throw new ArgumentException("HLS Subtitles are not supported for this media.");
            }

            builder.AppendLine("#EXTM3U");
            builder.AppendLine("#EXT-X-TARGETDURATION:" + request.SegmentLength.ToString(CultureInfo.InvariantCulture));
            builder.AppendLine("#EXT-X-VERSION:3");
            builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
            builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");

            long positionTicks      = 0;
            var  segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks;

            var accessToken = AuthorizationContext.GetAuthorizationInfo(Request).Token;

            while (positionTicks < runtime)
            {
                var remaining   = runtime - positionTicks;
                var lengthTicks = Math.Min(remaining, segmentLengthTicks);

                builder.AppendLine("#EXTINF:" + TimeSpan.FromTicks(lengthTicks).TotalSeconds.ToString(CultureInfo.InvariantCulture) + ",");

                var endPositionTicks = Math.Min(runtime, positionTicks + segmentLengthTicks);

                var url = string.Format("stream.vtt?CopyTimestamps=true&AddVttTimeMap=true&StartPositionTicks={0}&EndPositionTicks={1}&api_key={2}",
                                        positionTicks.ToString(CultureInfo.InvariantCulture),
                                        endPositionTicks.ToString(CultureInfo.InvariantCulture),
                                        accessToken);

                builder.AppendLine(url);

                positionTicks += segmentLengthTicks;
            }

            builder.AppendLine("#EXT-X-ENDLIST");

            return(ResultFactory.GetResult(builder.ToString(), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary <string, string>()));
        }
        public async Task <object> Get(GetUserViews request)
        {
            var query = new UserViewQuery
            {
                UserId = request.UserId
            };

            if (request.IncludeExternalContent.HasValue)
            {
                query.IncludeExternalContent = request.IncludeExternalContent.Value;
            }

            if (!string.IsNullOrWhiteSpace(request.PresetViews))
            {
                query.PresetViews = request.PresetViews.Split(',');
            }

            var app = AuthorizationContext.GetAuthorizationInfo(Request).Client ?? string.Empty;

            if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1)
            {
                query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows };
            }
            //query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows };

            var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);

            var dtoOptions = GetDtoOptions(request);

            dtoOptions.Fields = new List <ItemFields>();
            dtoOptions.Fields.Add(ItemFields.PrimaryImageAspectRatio);
            dtoOptions.Fields.Add(ItemFields.DisplayPreferencesId);

            var user = _userManager.GetUserById(request.UserId);

            var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
                       .ToArray();

            var result = new QueryResult <BaseItemDto>
            {
                Items            = dtos,
                TotalRecordCount = dtos.Length
            };

            return(ToOptimizedResult(result));
        }
Ejemplo n.º 14
0
        public async Task <object> Post(AuthenticateUserByName request)
        {
            var auth = AuthorizationContext.GetAuthorizationInfo(Request);

            var result = await _sessionMananger.AuthenticateNewSession(new AuthenticationRequest
            {
                App            = auth.Client,
                AppVersion     = auth.Version,
                DeviceId       = auth.DeviceId,
                DeviceName     = auth.Device,
                PasswordSha1   = request.Password,
                PasswordMd5    = request.PasswordMd5,
                RemoteEndPoint = Request.RemoteIp,
                Username       = request.Username
            }).ConfigureAwait(false);

            return(ToOptimizedResult(result));
        }
Ejemplo n.º 15
0
        public async Task <object> Get(GetRecordings request)
        {
            var options = GetDtoOptions(request);

            options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;

            var result = await _liveTvManager.GetRecordings(new RecordingQuery
            {
                ChannelId     = request.ChannelId,
                UserId        = request.UserId,
                GroupId       = request.GroupId,
                StartIndex    = request.StartIndex,
                Limit         = request.Limit,
                Status        = request.Status,
                SeriesTimerId = request.SeriesTimerId,
                IsInProgress  = request.IsInProgress
            }, options, CancellationToken.None).ConfigureAwait(false);

            return(ToOptimizedResult(result));
        }
        public async Task <object> Get(GetLocalUser request)
        {
            var user = await _connectManager.GetLocalUser(request.ConnectUserId).ConfigureAwait(false);

            if (user == null)
            {
                throw new ResourceNotFoundException();
            }

            var auth = AuthorizationContext.GetAuthorizationInfo(Request);

            if (string.IsNullOrWhiteSpace(auth.Client))
            {
                return(ToOptimizedResult(new ConnectAuthenticationExchangeResult
                {
                    AccessToken = user.ConnectAccessKey,
                    LocalUserId = user.Id.ToString("N")
                }));
            }

            var session = await _sessionManager.CreateNewSession(new AuthenticationRequest
            {
                App            = auth.Client,
                AppVersion     = auth.Version,
                DeviceId       = auth.DeviceId,
                DeviceName     = auth.Device,
                RemoteEndPoint = Request.RemoteIp,
                Username       = user.Name,
                UserId         = user.Id.ToString("N")
            }).ConfigureAwait(false);

            return(ToOptimizedResult(new ConnectAuthenticationExchangeResult
            {
                AccessToken = session.AccessToken,
                LocalUserId = session.User.Id
            }));
        }
Ejemplo n.º 17
0
        private object Get(GetUsers request, bool filterByDevice)
        {
            var users = _userManager.Users;

            if (request.IsDisabled.HasValue)
            {
                users = users.Where(i => i.Policy.IsDisabled == request.IsDisabled.Value);
            }

            if (request.IsHidden.HasValue)
            {
                users = users.Where(i => i.Policy.IsHidden == request.IsHidden.Value);
            }

            if (request.IsGuest.HasValue)
            {
                users = users.Where(i => (i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.Guest) == request.IsGuest.Value);
            }

            if (filterByDevice)
            {
                var deviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;

                if (!string.IsNullOrWhiteSpace(deviceId))
                {
                    users = users.Where(i => _deviceManager.CanAccessDevice(i.Id.ToString("N"), deviceId));
                }
            }

            var result = users
                         .OrderBy(u => u.Name)
                         .Select(i => _userManager.GetUserDto(i, Request.RemoteIp))
                         .ToList();

            return(ToOptimizedResult(result));
        }
Ejemplo n.º 18
0
        private async Task <object> GetUniversalStream(GetUniversalAudioStream request, bool isHeadRequest)
        {
            var deviceProfile = GetDeviceProfile(request);

            AuthorizationContext.GetAuthorizationInfo(Request).DeviceId = request.DeviceId;

            var mediaInfoService = new MediaInfoService(MediaSourceManager, DeviceManager, LibraryManager, ServerConfigurationManager, NetworkManager, MediaEncoder, UserManager, JsonSerializer, AuthorizationContext, _loggerFactory)
            {
                Request = Request
            };

            var playbackInfoResult = await mediaInfoService.GetPlaybackInfo(new GetPostedPlaybackInfo
            {
                Id = request.Id,
                MaxAudioChannels    = request.MaxAudioChannels,
                MaxStreamingBitrate = request.MaxStreamingBitrate,
                StartTimeTicks      = request.StartTimeTicks,
                UserId        = request.UserId,
                DeviceProfile = deviceProfile,
                MediaSourceId = request.MediaSourceId
            }).ConfigureAwait(false);

            var mediaSource = playbackInfoResult.MediaSources[0];

            if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == MediaProtocol.Http)
            {
                if (request.EnableRedirection)
                {
                    if (mediaSource.IsRemote && request.EnableRemoteMedia)
                    {
                        return(ResultFactory.GetRedirectResult(mediaSource.Path));
                    }
                }
            }

            var isStatic = mediaSource.SupportsDirectStream;

            if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
            {
                var service = new DynamicHlsService(ServerConfigurationManager,
                                                    UserManager,
                                                    LibraryManager,
                                                    IsoManager,
                                                    MediaEncoder,
                                                    FileSystem,
                                                    DlnaManager,
                                                    SubtitleEncoder,
                                                    DeviceManager,
                                                    MediaSourceManager,
                                                    JsonSerializer,
                                                    AuthorizationContext,
                                                    NetworkManager)
                {
                    Request = Request
                };

                var transcodingProfile = deviceProfile.TranscodingProfiles[0];

                var newRequest = new GetMasterHlsAudioPlaylist
                {
                    AudioBitRate        = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
                    AudioCodec          = transcodingProfile.AudioCodec,
                    Container           = ".m3u8",
                    DeviceId            = request.DeviceId,
                    Id                  = request.Id,
                    MaxAudioChannels    = request.MaxAudioChannels,
                    MediaSourceId       = mediaSource.Id,
                    PlaySessionId       = playbackInfoResult.PlaySessionId,
                    StartTimeTicks      = request.StartTimeTicks,
                    Static              = isStatic,
                    SegmentContainer    = request.TranscodingContainer,
                    AudioSampleRate     = request.MaxAudioSampleRate,
                    MaxAudioBitDepth    = request.MaxAudioBitDepth,
                    BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames,
                    TranscodeReasons    = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray())
                };

                if (isHeadRequest)
                {
                    return(await service.Head(newRequest).ConfigureAwait(false));
                }
                return(await service.Get(newRequest).ConfigureAwait(false));
            }
            else
            {
                var service = new AudioService(HttpClient,
                                               ServerConfigurationManager,
                                               UserManager,
                                               LibraryManager,
                                               IsoManager,
                                               MediaEncoder,
                                               FileSystem,
                                               DlnaManager,
                                               SubtitleEncoder,
                                               DeviceManager,
                                               MediaSourceManager,
                                               JsonSerializer,
                                               AuthorizationContext,
                                               EnvironmentInfo)
                {
                    Request = Request
                };

                var newRequest = new GetAudioStream
                {
                    AudioBitRate     = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
                    AudioCodec       = request.AudioCodec,
                    Container        = isStatic ? null : ("." + mediaSource.TranscodingContainer),
                    DeviceId         = request.DeviceId,
                    Id               = request.Id,
                    MaxAudioChannels = request.MaxAudioChannels,
                    MediaSourceId    = mediaSource.Id,
                    PlaySessionId    = playbackInfoResult.PlaySessionId,
                    StartTimeTicks   = request.StartTimeTicks,
                    Static           = isStatic,
                    AudioSampleRate  = request.MaxAudioSampleRate,
                    MaxAudioBitDepth = request.MaxAudioBitDepth,
                    TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray())
                };

                if (isHeadRequest)
                {
                    return(await service.Head(newRequest).ConfigureAwait(false));
                }
                return(await service.Get(newRequest).ConfigureAwait(false));
            }
        }
Ejemplo n.º 19
0
        private async Task <object> GetUniversalStream(GetUniversalAudioStream request, bool isHeadRequest)
        {
            var deviceProfile = GetDeviceProfile(request);

            AuthorizationContext.GetAuthorizationInfo(Request).DeviceId = request.DeviceId;

            var mediaInfoService = new MediaInfoService(MediaSourceManager, DeviceManager, LibraryManager, ServerConfigurationManager, NetworkManager, MediaEncoder, UserManager, JsonSerializer, AuthorizationContext)
            {
                Request = Request
            };

            var playbackInfoResult = await mediaInfoService.GetPlaybackInfo(new GetPostedPlaybackInfo
            {
                Id = request.Id,
                MaxAudioChannels    = request.MaxAudioChannels,
                MaxStreamingBitrate = request.MaxStreamingBitrate,
                StartTimeTicks      = request.StartTimeTicks,
                UserId        = request.UserId,
                DeviceProfile = deviceProfile,
                MediaSourceId = request.MediaSourceId
            }).ConfigureAwait(false);

            var mediaSource = playbackInfoResult.MediaSources[0];

            var isStatic = mediaSource.SupportsDirectStream;

            if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
            {
                var service = new DynamicHlsService(ServerConfigurationManager,
                                                    UserManager,
                                                    LibraryManager,
                                                    IsoManager,
                                                    MediaEncoder,
                                                    FileSystem,
                                                    DlnaManager,
                                                    SubtitleEncoder,
                                                    DeviceManager,
                                                    MediaSourceManager,
                                                    ZipClient,
                                                    JsonSerializer,
                                                    AuthorizationContext,
                                                    NetworkManager)
                {
                    Request = Request
                };

                var transcodingProfile = deviceProfile.TranscodingProfiles[0];

                var newRequest = new GetMasterHlsAudioPlaylist
                {
                    AudioBitRate     = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
                    AudioCodec       = transcodingProfile.AudioCodec,
                    Container        = ".m3u8",
                    DeviceId         = request.DeviceId,
                    Id               = request.Id,
                    MaxAudioChannels = request.MaxAudioChannels,
                    MediaSourceId    = mediaSource.Id,
                    PlaySessionId    = playbackInfoResult.PlaySessionId,
                    StartTimeTicks   = request.StartTimeTicks,
                    Static           = isStatic
                };

                if (isHeadRequest)
                {
                    return(await service.Head(newRequest).ConfigureAwait(false));
                }
                return(await service.Get(newRequest).ConfigureAwait(false));
            }
            else
            {
                var service = new AudioService(ServerConfigurationManager,
                                               UserManager,
                                               LibraryManager,
                                               IsoManager,
                                               MediaEncoder,
                                               FileSystem,
                                               DlnaManager,
                                               SubtitleEncoder,
                                               DeviceManager,
                                               MediaSourceManager,
                                               ZipClient,
                                               JsonSerializer,
                                               AuthorizationContext,
                                               ImageProcessor)
                {
                    Request = Request
                };

                var newRequest = new GetAudioStream
                {
                    AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
                    //AudioCodec = request.AudioCodec,
                    Container        = isStatic ? null : ("." + mediaSource.TranscodingContainer),
                    DeviceId         = request.DeviceId,
                    Id               = request.Id,
                    MaxAudioChannels = request.MaxAudioChannels,
                    MediaSourceId    = mediaSource.Id,
                    PlaySessionId    = playbackInfoResult.PlaySessionId,
                    StartTimeTicks   = request.StartTimeTicks,
                    Static           = isStatic
                };

                if (isHeadRequest)
                {
                    return(await service.Head(newRequest).ConfigureAwait(false));
                }
                return(await service.Get(newRequest).ConfigureAwait(false));
            }
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Gets the state.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>StreamState.</returns>
        protected async Task <StreamState> GetState(StreamRequest request, CancellationToken cancellationToken)
        {
            ParseDlnaHeaders(request);

            if (!string.IsNullOrWhiteSpace(request.Params))
            {
                ParseParams(request);
            }

            var url = Request.PathInfo;

            if (string.IsNullOrEmpty(request.AudioCodec))
            {
                request.AudioCodec = EncodingHelper.InferAudioCodec(url);
            }

            var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType)
            {
                Request      = request,
                RequestedUrl = url,
                UserAgent    = Request.UserAgent
            };

            var auth = AuthorizationContext.GetAuthorizationInfo(Request);

            if (!string.IsNullOrWhiteSpace(auth.UserId))
            {
                state.User = UserManager.GetUserById(auth.UserId);
            }

            //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
            //    (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
            //    (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
            //{
            //    state.SegmentLength = 6;
            //}

            if (state.VideoRequest != null)
            {
                if (!string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec))
                {
                    state.SupportedVideoCodecs    = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
                    state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
                }
            }

            if (!string.IsNullOrWhiteSpace(request.AudioCodec))
            {
                state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
                state.Request.AudioCodec   = state.SupportedAudioCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToAudioCodec(i))
                                             ?? state.SupportedAudioCodecs.FirstOrDefault();
            }

            if (!string.IsNullOrWhiteSpace(request.SubtitleCodec))
            {
                state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
                state.Request.SubtitleCodec   = state.SupportedSubtitleCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToSubtitleCodec(i))
                                                ?? state.SupportedSubtitleCodecs.FirstOrDefault();
            }

            var item = LibraryManager.GetItemById(request.Id);

            state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);

            MediaSourceInfo mediaSource = null;

            if (string.IsNullOrWhiteSpace(request.LiveStreamId))
            {
                TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ?
                                            ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId)
                    : null;

                if (currentJob != null)
                {
                    mediaSource = currentJob.MediaSource;
                }

                if (mediaSource == null)
                {
                    var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList();

                    mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
                       ? mediaSources.First()
                       : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));

                    if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase))
                    {
                        mediaSource = mediaSources.First();
                    }
                }
            }
            else
            {
                var liveStreamInfo = await MediaSourceManager.GetLiveStreamWithDirectStreamProvider(request.LiveStreamId, cancellationToken).ConfigureAwait(false);

                mediaSource = liveStreamInfo.Item1;
                state.DirectStreamProvider = liveStreamInfo.Item2;
            }

            var videoRequest = request as VideoStreamRequest;

            EncodingHelper.AttachMediaSourceInfo(state, mediaSource, url);

            var container = Path.GetExtension(state.RequestedUrl);

            if (string.IsNullOrEmpty(container))
            {
                container = request.Static ?
                            state.InputContainer :
                            (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.');
            }

            state.OutputContainer = (container ?? string.Empty).TrimStart('.');

            state.OutputAudioBitrate    = EncodingHelper.GetAudioBitrateParam(state.Request, state.AudioStream);
            state.OutputAudioSampleRate = request.AudioSampleRate;

            state.OutputAudioCodec = state.Request.AudioCodec;

            state.OutputAudioChannels = EncodingHelper.GetNumAudioChannelsParam(state.Request, state.AudioStream, state.OutputAudioCodec);

            if (videoRequest != null)
            {
                state.OutputVideoCodec   = state.VideoRequest.VideoCodec;
                state.OutputVideoBitrate = EncodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);

                if (videoRequest != null)
                {
                    EncodingHelper.TryStreamCopy(state);
                }

                if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
                {
                    var resolution = ResolutionNormalizer.Normalize(
                        state.VideoStream == null ? (int?)null : state.VideoStream.BitRate,
                        state.OutputVideoBitrate.Value,
                        state.VideoStream == null ? null : state.VideoStream.Codec,
                        state.OutputVideoCodec,
                        videoRequest.MaxWidth,
                        videoRequest.MaxHeight);

                    videoRequest.MaxWidth  = resolution.MaxWidth;
                    videoRequest.MaxHeight = resolution.MaxHeight;
                }

                ApplyDeviceProfileSettings(state);
            }
            else
            {
                ApplyDeviceProfileSettings(state);
            }

            state.OutputFilePath = GetOutputFilePath(state);

            return(state);
        }
Ejemplo n.º 21
0
        /// <summary>
        /// Starts the FFMPEG.
        /// </summary>
        /// <param name="state">The state.</param>
        /// <param name="outputPath">The output path.</param>
        /// <param name="cancellationTokenSource">The cancellation token source.</param>
        /// <param name="workingDirectory">The working directory.</param>
        /// <returns>Task.</returns>
        protected async Task <TranscodingJob> StartFfMpeg(StreamState state,
                                                          string outputPath,
                                                          CancellationTokenSource cancellationTokenSource,
                                                          string workingDirectory = null)
        {
            FileSystem.CreateDirectory(FileSystem.GetDirectoryName(outputPath));

            await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);

            if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
            {
                var auth = AuthorizationContext.GetAuthorizationInfo(Request);
                if (!string.IsNullOrWhiteSpace(auth.UserId))
                {
                    var user = UserManager.GetUserById(auth.UserId);
                    if (!user.Policy.EnableVideoPlaybackTranscoding)
                    {
                        ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);

                        throw new ArgumentException("User does not have access to video transcoding");
                    }
                }
            }

            var transcodingId   = Guid.NewGuid().ToString("N");
            var commandLineArgs = GetCommandLineArguments(outputPath, state, true);

            var process = ApiEntryPoint.Instance.ProcessFactory.Create(new ProcessOptions
            {
                CreateNoWindow  = true,
                UseShellExecute = false,

                // Must consume both stdout and stderr or deadlocks may occur
                //RedirectStandardOutput = true,
                RedirectStandardError = true,
                RedirectStandardInput = true,

                FileName  = MediaEncoder.EncoderPath,
                Arguments = commandLineArgs,

                IsHidden            = true,
                ErrorDialog         = false,
                EnableRaisingEvents = true,
                WorkingDirectory    = !string.IsNullOrWhiteSpace(workingDirectory) ? workingDirectory : null
            });

            var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
                                                                             state.Request.PlaySessionId,
                                                                             state.MediaSource.LiveStreamId,
                                                                             transcodingId,
                                                                             TranscodingJobType,
                                                                             process,
                                                                             state.Request.DeviceId,
                                                                             state,
                                                                             cancellationTokenSource);

            var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;

            Logger.Info(commandLineLogMessage);

            var logFilePrefix = "ffmpeg-transcode";

            if (state.VideoRequest != null && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
            {
                logFilePrefix = "ffmpeg-directstream";
            }
            else if (state.VideoRequest != null && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
            {
                logFilePrefix = "ffmpeg-remux";
            }

            var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt");

            FileSystem.CreateDirectory(FileSystem.GetDirectoryName(logFilePath));

            // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
            state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);

            var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(Request.AbsoluteUri + Environment.NewLine + Environment.NewLine + JsonSerializer.SerializeToString(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
            await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);

            process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);

            try
            {
                process.Start();
            }
            catch (Exception ex)
            {
                Logger.ErrorException("Error starting ffmpeg", ex);

                ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);

                throw;
            }

            // MUST read both stdout and stderr asynchronously or a deadlock may occurr
            //process.BeginOutputReadLine();

            state.TranscodingJob = transcodingJob;

            // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
            new JobLogger(Logger).StartStreamingLog(state, process.StandardError.BaseStream, state.LogFileStream);

            // Wait for the file to exist before proceeeding
            while (!FileSystem.FileExists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
            {
                await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
            }

            if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive && !transcodingJob.HasExited)
            {
                await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false);

                if (state.ReadInputAtNativeFramerate && !transcodingJob.HasExited)
                {
                    await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
                }
            }

            if (!transcodingJob.HasExited)
            {
                StartThrottler(state, transcodingJob);
            }

            return(transcodingJob);
        }
Ejemplo n.º 22
0
        /// <summary>
        /// Gets the state.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>StreamState.</returns>
        protected async Task <StreamState> GetState(StreamRequest request, CancellationToken cancellationToken)
        {
            ParseDlnaHeaders(request);

            if (!string.IsNullOrWhiteSpace(request.Params))
            {
                ParseParams(request);
            }

            ParseStreamOptions(request);

            var url = Request.PathInfo;

            if (string.IsNullOrEmpty(request.AudioCodec))
            {
                request.AudioCodec = EncodingHelper.InferAudioCodec(url);
            }

            var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) ||
                                    string.Equals(GetHeader("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase);

            var state = new StreamState(MediaSourceManager, TranscodingJobType)
            {
                Request           = request,
                RequestedUrl      = url,
                UserAgent         = Request.UserAgent,
                EnableDlnaHeaders = enableDlnaHeaders
            };

            var auth = AuthorizationContext.GetAuthorizationInfo(Request);

            if (!auth.UserId.Equals(Guid.Empty))
            {
                state.User = UserManager.GetUserById(auth.UserId);
            }

            //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
            //    (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
            //    (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
            //{
            //    state.SegmentLength = 6;
            //}

            if (state.VideoRequest != null && !string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec))
            {
                state.SupportedVideoCodecs    = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
                state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
            }

            if (!string.IsNullOrWhiteSpace(request.AudioCodec))
            {
                state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
                state.Request.AudioCodec   = state.SupportedAudioCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToAudioCodec(i))
                                             ?? state.SupportedAudioCodecs.FirstOrDefault();
            }

            if (!string.IsNullOrWhiteSpace(request.SubtitleCodec))
            {
                state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
                state.Request.SubtitleCodec   = state.SupportedSubtitleCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToSubtitleCodec(i))
                                                ?? state.SupportedSubtitleCodecs.FirstOrDefault();
            }

            var item = LibraryManager.GetItemById(request.Id);

            state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);

            //var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ??
            //             item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null);
            //if (primaryImage != null)
            //{
            //    state.AlbumCoverPath = primaryImage.Path;
            //}

            MediaSourceInfo mediaSource = null;

            if (string.IsNullOrWhiteSpace(request.LiveStreamId))
            {
                var currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ?
                                 ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId)
                    : null;

                if (currentJob != null)
                {
                    mediaSource = currentJob.MediaSource;
                }

                if (mediaSource == null)
                {
                    var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList();

                    mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
                       ? mediaSources[0]
                       : mediaSources.Find(i => string.Equals(i.Id, request.MediaSourceId));

                    if (mediaSource == null && request.MediaSourceId.Equals(request.Id))
                    {
                        mediaSource = mediaSources[0];
                    }
                }
            }
            else
            {
                var liveStreamInfo = await MediaSourceManager.GetLiveStreamWithDirectStreamProvider(request.LiveStreamId, cancellationToken).ConfigureAwait(false);

                mediaSource = liveStreamInfo.Item1;
                state.DirectStreamProvider = liveStreamInfo.Item2;
            }

            var videoRequest = request as VideoStreamRequest;

            EncodingHelper.AttachMediaSourceInfo(state, mediaSource, url);

            var container = Path.GetExtension(state.RequestedUrl);

            if (string.IsNullOrEmpty(container))
            {
                container = request.Container;
            }

            if (string.IsNullOrEmpty(container))
            {
                container = request.Static ?
                            StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, state.MediaPath, null, DlnaProfileType.Audio) :
                            GetOutputFileExtension(state);
            }

            state.OutputContainer = (container ?? string.Empty).TrimStart('.');

            state.OutputAudioBitrate = EncodingHelper.GetAudioBitrateParam(state.Request, state.AudioStream);

            state.OutputAudioCodec = state.Request.AudioCodec;

            state.OutputAudioChannels = EncodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec);

            if (videoRequest != null)
            {
                state.OutputVideoCodec   = state.VideoRequest.VideoCodec;
                state.OutputVideoBitrate = EncodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);

                if (videoRequest != null)
                {
                    EncodingHelper.TryStreamCopy(state);
                }

                if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
                {
                    var resolution = ResolutionNormalizer.Normalize(
                        state.VideoStream?.BitRate,
                        state.VideoStream?.Width,
                        state.VideoStream?.Height,
                        state.OutputVideoBitrate.Value,
                        state.VideoStream?.Codec,
                        state.OutputVideoCodec,
                        videoRequest.MaxWidth,
                        videoRequest.MaxHeight);

                    videoRequest.MaxWidth  = resolution.MaxWidth;
                    videoRequest.MaxHeight = resolution.MaxHeight;
                }
            }

            ApplyDeviceProfileSettings(state);

            var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
                ? GetOutputFileExtension(state)
                : ('.' + state.OutputContainer);

            var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();

            state.OutputFilePath = GetOutputFilePath(state, encodingOptions, ext);

            return(state);
        }
Ejemplo n.º 23
0
        /// <summary>
        /// Starts the FFMPEG.
        /// </summary>
        /// <param name="state">The state.</param>
        /// <param name="outputPath">The output path.</param>
        /// <param name="cancellationTokenSource">The cancellation token source.</param>
        /// <param name="workingDirectory">The working directory.</param>
        /// <returns>Task.</returns>
        protected async Task <TranscodingJob> StartFfMpeg(
            StreamState state,
            string outputPath,
            CancellationTokenSource cancellationTokenSource,
            string workingDirectory = null)
        {
            Directory.CreateDirectory(Path.GetDirectoryName(outputPath));

            await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);

            if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
            {
                var auth = AuthorizationContext.GetAuthorizationInfo(Request);
                if (auth.User != null && !auth.User.Policy.EnableVideoPlaybackTranscoding)
                {
                    ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);

                    throw new ArgumentException("User does not have access to video transcoding");
                }
            }

            var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();

            var process = new Process()
            {
                StartInfo = new ProcessStartInfo()
                {
                    WindowStyle     = ProcessWindowStyle.Hidden,
                    CreateNoWindow  = true,
                    UseShellExecute = false,

                    // Must consume both stdout and stderr or deadlocks may occur
                    //RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    RedirectStandardInput = true,

                    FileName         = MediaEncoder.EncoderPath,
                    Arguments        = GetCommandLineArguments(outputPath, encodingOptions, state, true),
                    WorkingDirectory = string.IsNullOrWhiteSpace(workingDirectory) ? null : workingDirectory,

                    ErrorDialog = false
                },
                EnableRaisingEvents = true
            };

            var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
                                                                             state.Request.PlaySessionId,
                                                                             state.MediaSource.LiveStreamId,
                                                                             Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
                                                                             TranscodingJobType,
                                                                             process,
                                                                             state.Request.DeviceId,
                                                                             state,
                                                                             cancellationTokenSource);

            var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;

            Logger.LogInformation(commandLineLogMessage);

            var logFilePrefix = "ffmpeg-transcode";

            if (state.VideoRequest != null &&
                string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
            {
                if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
                {
                    logFilePrefix = "ffmpeg-directstream";
                }
                else
                {
                    logFilePrefix = "ffmpeg-remux";
                }
            }

            var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt");

            // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
            Stream logStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);

            var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(Request.AbsoluteUri + Environment.NewLine + Environment.NewLine + JsonSerializer.SerializeToString(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
            await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);

            process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);

            try
            {
                process.Start();
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Error starting ffmpeg");

                ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);

                throw;
            }

            Logger.LogDebug("Launched ffmpeg process");
            state.TranscodingJob = transcodingJob;

            // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
            _ = new JobLogger(Logger).StartStreamingLog(state, process.StandardError.BaseStream, logStream);

            // Wait for the file to exist before proceeeding
            var ffmpegTargetFile = state.WaitForPath ?? outputPath;

            Logger.LogDebug("Waiting for the creation of {0}", ffmpegTargetFile);
            while (!File.Exists(ffmpegTargetFile) && !transcodingJob.HasExited)
            {
                await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
            }

            Logger.LogDebug("File {0} created or transcoding has finished", ffmpegTargetFile);

            if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive && !transcodingJob.HasExited)
            {
                await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false);

                if (state.ReadInputAtNativeFramerate && !transcodingJob.HasExited)
                {
                    await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
                }
            }

            if (!transcodingJob.HasExited)
            {
                StartThrottler(state, transcodingJob);
            }
            Logger.LogDebug("StartFfMpeg() finished successfully");

            return(transcodingJob);
        }
Ejemplo n.º 24
0
        private async Task <object> GetUniversalStream(GetUniversalAudioStream request, bool isHeadRequest)
        {
            var deviceProfile = GetDeviceProfile(request);

            AuthorizationContext.GetAuthorizationInfo(Request).DeviceId = request.DeviceId;

            var mediaInfoService = new MediaInfoService(
                _loggerFactory.CreateLogger <MediaInfoService>(),
                ServerConfigurationManager,
                ResultFactory,
                MediaSourceManager,
                DeviceManager,
                LibraryManager,
                NetworkManager,
                MediaEncoder,
                UserManager,
                AuthorizationContext)
            {
                Request = Request
            };

            var playbackInfoResult = await mediaInfoService.GetPlaybackInfo(new GetPostedPlaybackInfo
            {
                Id = request.Id,
                MaxAudioChannels    = request.MaxAudioChannels,
                MaxStreamingBitrate = request.MaxStreamingBitrate,
                StartTimeTicks      = request.StartTimeTicks,
                UserId        = request.UserId,
                DeviceProfile = deviceProfile,
                MediaSourceId = request.MediaSourceId
            }).ConfigureAwait(false);

            var mediaSource = playbackInfoResult.MediaSources[0];

            if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == MediaProtocol.Http)
            {
                if (request.EnableRedirection)
                {
                    if (mediaSource.IsRemote && request.EnableRemoteMedia)
                    {
                        return(ResultFactory.GetRedirectResult(mediaSource.Path));
                    }
                }
            }

            var isStatic = mediaSource.SupportsDirectStream;

            if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
            {
                var service = new DynamicHlsService(
                    _loggerFactory.CreateLogger <DynamicHlsService>(),
                    ServerConfigurationManager,
                    ResultFactory,
                    UserManager,
                    LibraryManager,
                    IsoManager,
                    MediaEncoder,
                    FileSystem,
                    DlnaManager,
                    DeviceManager,
                    MediaSourceManager,
                    JsonSerializer,
                    AuthorizationContext,
                    NetworkManager,
                    _encodingHelper)
                {
                    Request = Request
                };

                var transcodingProfile = deviceProfile.TranscodingProfiles[0];

                // hls segment container can only be mpegts or fmp4 per ffmpeg documentation
                // TODO: remove this when we switch back to the segment muxer
                var supportedHLSContainers = new[] { "mpegts", "fmp4" };

                var newRequest = new GetMasterHlsAudioPlaylist
                {
                    AudioBitRate     = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
                    AudioCodec       = transcodingProfile.AudioCodec,
                    Container        = ".m3u8",
                    DeviceId         = request.DeviceId,
                    Id               = request.Id,
                    MaxAudioChannels = request.MaxAudioChannels,
                    MediaSourceId    = mediaSource.Id,
                    PlaySessionId    = playbackInfoResult.PlaySessionId,
                    StartTimeTicks   = request.StartTimeTicks,
                    Static           = isStatic,
                    // fallback to mpegts if device reports some weird value unsupported by hls
                    SegmentContainer    = Array.Exists(supportedHLSContainers, element => element == request.TranscodingContainer) ? request.TranscodingContainer : "mpegts",
                    AudioSampleRate     = request.MaxAudioSampleRate,
                    MaxAudioBitDepth    = request.MaxAudioBitDepth,
                    BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames,
                    TranscodeReasons    = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray())
                };

                if (isHeadRequest)
                {
                    return(await service.Head(newRequest).ConfigureAwait(false));
                }
                return(await service.Get(newRequest).ConfigureAwait(false));
            }
            else
            {
                var service = new AudioService(
                    _loggerFactory.CreateLogger <AudioService>(),
                    ServerConfigurationManager,
                    ResultFactory,
                    HttpClient,
                    UserManager,
                    LibraryManager,
                    IsoManager,
                    MediaEncoder,
                    FileSystem,
                    DlnaManager,
                    DeviceManager,
                    MediaSourceManager,
                    JsonSerializer,
                    AuthorizationContext,
                    _encodingHelper)
                {
                    Request = Request
                };

                var newRequest = new GetAudioStream
                {
                    AudioBitRate     = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
                    AudioCodec       = request.AudioCodec,
                    Container        = isStatic ? null : ("." + mediaSource.TranscodingContainer),
                    DeviceId         = request.DeviceId,
                    Id               = request.Id,
                    MaxAudioChannels = request.MaxAudioChannels,
                    MediaSourceId    = mediaSource.Id,
                    PlaySessionId    = playbackInfoResult.PlaySessionId,
                    StartTimeTicks   = request.StartTimeTicks,
                    Static           = isStatic,
                    AudioSampleRate  = request.MaxAudioSampleRate,
                    MaxAudioBitDepth = request.MaxAudioBitDepth,
                    TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray())
                };

                if (isHeadRequest)
                {
                    return(await service.Head(newRequest).ConfigureAwait(false));
                }

                return(await service.Get(newRequest).ConfigureAwait(false));
            }
        }