예제 #1
0
        protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
        {
            if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0)
            {
                Logger.Debug("Cannot stream copy video due to missing keyframe info");
                return(false);
            }

            var previousSegment = 0;

            foreach (var frame in videoStream.KeyFrames)
            {
                var length = frame - previousSegment;

                // Don't allow really long segments because this could result in long download times
                if (length > 10000)
                {
                    Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length);
                    return(false);
                }
                previousSegment = frame;
            }

            return(base.CanStreamCopyVideo(request, videoStream));
        }
예제 #2
0
        /// <summary>
        /// Gets the video bitrate to specify on the command line
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="videoCodec">The video codec.</param>
        /// <returns>System.String.</returns>
        private string GetVideoQualityParam(VideoStreamRequest request, string videoCodec)
        {
            var args = string.Empty;

            // webm
            if (videoCodec.Equals("libvpx", StringComparison.OrdinalIgnoreCase))
            {
                args = "-quality realtime -profile:v 1 -slices 4";
            }

            // asf/wmv
            else if (videoCodec.Equals("wmv2", StringComparison.OrdinalIgnoreCase))
            {
                args = "-g 100 -qmax 15";
            }

            else if (videoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase))
            {
                args = "-preset superfast";
            }

            if (request.VideoBitRate.HasValue)
            {
                args += " -b:v " + request.VideoBitRate;
            }

            return(args.Trim());
        }
예제 #3
0
    public override bool Equals(object other)
    {
        if (!(other is VideoStreamRequest))
        {
            return(false);
        }
        VideoStreamRequest vsr = other as VideoStreamRequest;

        if (Stream != vsr.Stream)
        {
            return(false);
        }
        if (Format != vsr.Format)
        {
            return(false);
        }
        if (Width != vsr.Width)
        {
            return(false);
        }
        if (Height != vsr.Height)
        {
            return(false);
        }
        if (Framerate != vsr.Framerate)
        {
            return(false);
        }
        if (StreamIndex != vsr.StreamIndex)
        {
            return(false);
        }
        return(true);
    }
예제 #4
0
 public bool HasConflict(VideoStreamRequest other)
 {
     if (Stream != Stream.Any && Stream != other.Stream)
     {
         return(true);
     }
     if (Format != Format.Any && Format != other.Format)
     {
         return(true);
     }
     if (Width != 0 && Width != other.Width)
     {
         return(true);
     }
     if (Height != 0 && Height != other.Height)
     {
         return(true);
     }
     if (Framerate != 0 && Framerate != other.Framerate)
     {
         return(true);
     }
     if (StreamIndex != 0 && StreamIndex != other.StreamIndex)
     {
         return(true);
     }
     return(false);
 }
 virtual protected void Awake()
 {
     threadId           = Thread.CurrentThread.ManagedThreadId;
     _videoStreamFilter = new VideoStreamRequest()
     {
         Stream = _stream, Format = _format, StreamIndex = _streamIndex
     };
     _currVideoStreamFilter = _videoStreamFilter.Clone();
 }
예제 #6
0
        private int GetStartNumber(VideoStreamRequest request)
        {
            var segmentId = "0";

            if (request is GetHlsVideoSegment segmentRequest)
            {
                segmentId = segmentRequest.SegmentId;
            }

            return(int.Parse(segmentId, NumberStyles.Integer, CultureInfo.InvariantCulture));
        }
예제 #7
0
        private int GetStartNumber(VideoStreamRequest request)
        {
            var segmentId = "0";

            var segmentRequest = request as GetHlsVideoSegment;
            if (segmentRequest != null)
            {
                segmentId = segmentRequest.SegmentId;
            }

            return int.Parse(segmentId, NumberStyles.Integer, UsCulture);
        }
    private void ResetTexture(VideoStreamRequest vsr)
    {
        if (texture != null)
        {
            Destroy(texture);
        }

        texture = new Texture2D(vsr.Width, vsr.Height, Convert(vsr.Format), false, true)
        {
            wrapMode   = TextureWrapMode.Clamp,
            filterMode = filterMode
        };

        _currVideoStreamFilter = vsr.Clone();

        texture.Apply();
        textureBinding.Invoke(texture);
    }
예제 #9
0
        private int GetStartNumber(VideoStreamRequest request)
        {
            var segmentId = "0";

            var segmentRequest = request as GetDashSegment;

            if (segmentRequest != null)
            {
                segmentId = segmentRequest.SegmentId;
            }

            if (string.Equals(segmentId, "init", StringComparison.OrdinalIgnoreCase))
            {
                return(-1);
            }

            return(int.Parse(segmentId, NumberStyles.Integer, UsCulture));
        }
예제 #10
0
    void OnEnable()
    {
        m_pipeline = new Pipeline();

        using (var cfg = DeviceConfiguration.ToPipelineConfig())
        {
            ActiveProfile = m_pipeline.Start(cfg);
        }

        using (var activeStreams = ActiveProfile.Streams)
        {
            DeviceConfiguration.Profiles = new VideoStreamRequest[activeStreams.Count];
            for (int i = 0; i < DeviceConfiguration.Profiles.Length; i++)
            {
                var s = activeStreams[i];
                var p = new VideoStreamRequest()
                {
                    Stream      = s.Stream,
                    Format      = s.Format,
                    Framerate   = s.Framerate,
                    StreamIndex = s.Index,
                };
                var vs = s as VideoStreamProfile;
                if (vs != null)
                {
                    p.Width  = vs.Width;
                    p.Height = vs.Height;
                }
                DeviceConfiguration.Profiles[i] = p;
            }
        }


        if (processMode == ProcessMode.Multithread)
        {
            stopEvent.Reset();
            worker = new Thread(WaitForFrames);
            worker.IsBackground = true;
            worker.Start();
        }

        StartCoroutine(WaitAndStart());
    }
예제 #11
0
        private async Task <object> GetPlaylistAsync(VideoStreamRequest request, string name)
        {
            var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);

            var builder = new StringBuilder();

            builder.AppendLine("#EXTM3U");
            builder.AppendLine("#EXT-X-VERSION:3");
            builder.AppendLine("#EXT-X-TARGETDURATION:" + state.SegmentLength.ToString(UsCulture));
            builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
            builder.AppendLine("#EXT-X-ALLOW-CACHE:NO");

            var queryStringIndex = Request.RawUrl.IndexOf('?');
            var queryString      = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);

            var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;

            var index = 0;

            while (seconds > 0)
            {
                var length = seconds >= state.SegmentLength ? state.SegmentLength : seconds;

                builder.AppendLine("#EXTINF:" + length.ToString(UsCulture) + ",");

                builder.AppendLine(string.Format("hlsdynamic/{0}/{1}.ts{2}",

                                                 name,
                                                 index.ToString(UsCulture),
                                                 queryString));

                seconds -= state.SegmentLength;
                index++;
            }

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

            var playlistText = builder.ToString();

            return(ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary <string, string>()));
        }
예제 #12
0
        private async Task<object> GetPlaylistAsync(VideoStreamRequest request, string name)
        {
            var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);

            var builder = new StringBuilder();

            builder.AppendLine("#EXTM3U");
            builder.AppendLine("#EXT-X-VERSION:3");
            builder.AppendLine("#EXT-X-TARGETDURATION:" + state.SegmentLength.ToString(UsCulture));
            builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
            builder.AppendLine("#EXT-X-ALLOW-CACHE:NO");

            var queryStringIndex = Request.RawUrl.IndexOf('?');
            var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);

            var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;

            var index = 0;

            while (seconds > 0)
            {
                var length = seconds >= state.SegmentLength ? state.SegmentLength : seconds;

                builder.AppendLine("#EXTINF:" + length.ToString(UsCulture) + ",");

                builder.AppendLine(string.Format("hlsdynamic/{0}/{1}.ts{2}",

                    name,
                    index.ToString(UsCulture),
                    queryString));

                seconds -= state.SegmentLength;
                index++;
            }

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

            var playlistText = builder.ToString();

            return ResultFactory.GetResult(playlistText, Common.Net.MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
        }
예제 #13
0
        private async Task<object> GetDynamicSegment(VideoStreamRequest request, string segmentId)
        {
            if ((request.StartTimeTicks ?? 0) > 0)
            {
                throw new ArgumentException("StartTimeTicks is not allowed.");
            }

            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken = cancellationTokenSource.Token;

            var index = int.Parse(segmentId, NumberStyles.Integer, UsCulture);

            var state = await GetState(request, cancellationToken).ConfigureAwait(false);

            var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");

            var segmentPath = GetSegmentPath(playlistPath, index);
            var segmentLength = state.SegmentLength;

            TranscodingJob job = null;

            if (File.Exists(segmentPath))
            {
                job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
                return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
            }

            await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
            try
            {
                if (File.Exists(segmentPath))
                {
                    job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
                    return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath);

                    if (currentTranscodingIndex == null || index < currentTranscodingIndex.Value || (index - currentTranscodingIndex.Value) > 4)
                    {
                        // If the playlist doesn't already exist, startup ffmpeg
                        try
                        {
                            await ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType.Hls && string.Equals(j.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase), false).ConfigureAwait(false);

                            if (currentTranscodingIndex.HasValue)
                            {
                                DeleteLastFile(playlistPath, 0);
                            }

                            var startSeconds = index * state.SegmentLength;
                            request.StartTimeTicks = TimeSpan.FromSeconds(startSeconds).Ticks;

                            job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
                        }
                        catch
                        {
                            state.Dispose();
                            throw;
                        }

                        await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
                    }
                }
            }
            finally
            {
                ApiEntryPoint.Instance.TranscodingStartLock.Release();
            }

            Logger.Info("waiting for {0}", segmentPath);
            while (!File.Exists(segmentPath))
            {
                await Task.Delay(50, cancellationToken).ConfigureAwait(false);
            }

            Logger.Info("returning {0}", segmentPath);
            job = job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType.Hls);
            return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
        }
예제 #14
0
 protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
 {
     return(false);
     //return base.CanStreamCopyVideo(request, videoStream);
 }
예제 #15
0
        private async Task <object> GetDynamicSegment(VideoStreamRequest request, string segmentId)
        {
            if ((request.StartTimeTicks ?? 0) > 0)
            {
                throw new ArgumentException("StartTimeTicks is not allowed.");
            }

            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var requestedIndex = int.Parse(segmentId, NumberStyles.Integer, UsCulture);

            var state = await GetState(request, cancellationToken).ConfigureAwait(false);

            var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");

            var segmentPath   = GetSegmentPath(playlistPath, requestedIndex);
            var segmentLength = state.SegmentLength;

            var segmentExtension = GetSegmentFileExtension(state);

            TranscodingJob job = null;

            if (File.Exists(segmentPath))
            {
                job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
                return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false));
            }

            await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);

            try
            {
                if (File.Exists(segmentPath))
                {
                    job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
                    return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false));
                }
                else
                {
                    var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
                    var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength;
                    if (currentTranscodingIndex == null || requestedIndex < currentTranscodingIndex.Value || (requestedIndex - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange)
                    {
                        // If the playlist doesn't already exist, startup ffmpeg
                        try
                        {
                            ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId ?? request.ClientTime, p => false);

                            if (currentTranscodingIndex.HasValue)
                            {
                                DeleteLastFile(playlistPath, segmentExtension, 0);
                            }

                            request.StartTimeTicks = GetSeekPositionTicks(state, requestedIndex);

                            job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
                        }
                        catch
                        {
                            state.Dispose();
                            throw;
                        }

                        await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
                    }
                }
            }
            finally
            {
                ApiEntryPoint.Instance.TranscodingStartLock.Release();
            }

            Logger.Info("waiting for {0}", segmentPath);
            while (!File.Exists(segmentPath))
            {
                await Task.Delay(50, cancellationToken).ConfigureAwait(false);
            }

            Logger.Info("returning {0}", segmentPath);
            job = job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
            return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false));
        }
예제 #16
0
        private int GetStartNumber(VideoStreamRequest request)
        {
            var segmentId = "0";

            var segmentRequest = request as GetDashSegment;
            if (segmentRequest != null)
            {
                segmentId = segmentRequest.SegmentId;
            }

            if (string.Equals(segmentId, "init", StringComparison.OrdinalIgnoreCase))
            {
                return -1;
            }

            return int.Parse(segmentId, NumberStyles.Integer, UsCulture);
        }
예제 #17
0
 protected override bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
 {
     return false;
 }
예제 #18
0
        private async Task<object> GetDynamicSegment(VideoStreamRequest request, string segmentId, string representationId)
        {
            if ((request.StartTimeTicks ?? 0) > 0)
            {
                throw new ArgumentException("StartTimeTicks is not allowed.");
            }

            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken = cancellationTokenSource.Token;

            var requestedIndex = string.Equals(segmentId, "init", StringComparison.OrdinalIgnoreCase) ?
                -1 :
                int.Parse(segmentId, NumberStyles.Integer, UsCulture);

            var state = await GetState(request, cancellationToken).ConfigureAwait(false);

            var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".mpd");

            var segmentExtension = GetSegmentFileExtension(state);

            var segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex);
            var segmentLength = state.SegmentLength;

            TranscodingJob job = null;

            if (!string.IsNullOrWhiteSpace(segmentPath))
            {
                job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
                return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
            }

            await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
            try
            {
                segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex);
                if (!string.IsNullOrWhiteSpace(segmentPath))
                {
                    job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
                    return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    if (string.Equals(representationId, "0", StringComparison.OrdinalIgnoreCase))
                    {
                        job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
                        var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
                        var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength;
                        Logger.Debug("Current transcoding index is {0}. requestedIndex={1}. segmentGapRequiringTranscodingChange={2}", currentTranscodingIndex ?? -2, requestedIndex, segmentGapRequiringTranscodingChange);
                        if (currentTranscodingIndex == null || requestedIndex < currentTranscodingIndex.Value || (requestedIndex - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange)
                        {
                            // If the playlist doesn't already exist, startup ffmpeg
                            try
                            {
                                ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, p => false);

                                if (currentTranscodingIndex.HasValue)
                                {
                                    DeleteLastTranscodedFiles(playlistPath, 0);
                                }

                                var positionTicks = GetPositionTicks(state, requestedIndex);
                                request.StartTimeTicks = positionTicks;

                                var startNumber = GetStartNumber(state);

                                var workingDirectory = Path.Combine(Path.GetDirectoryName(playlistPath), (startNumber == -1 ? 0 : startNumber).ToString(CultureInfo.InvariantCulture));
                                state.WaitForPath = Path.Combine(workingDirectory, Path.GetFileName(playlistPath));
                                Directory.CreateDirectory(workingDirectory);
                                job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, workingDirectory).ConfigureAwait(false);
                                await WaitForMinimumDashSegmentCount(Path.Combine(workingDirectory, Path.GetFileName(playlistPath)), 1, cancellationTokenSource.Token).ConfigureAwait(false);
                            }
                            catch
                            {
                                state.Dispose();
                                throw;
                            }
                        }
                    }
                }
            }
            finally
            {
                ApiEntryPoint.Instance.TranscodingStartLock.Release();
            }

            while (string.IsNullOrWhiteSpace(segmentPath))
            {
                segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex);
                await Task.Delay(50, cancellationToken).ConfigureAwait(false);
            }

            Logger.Info("returning {0}", segmentPath);
            return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType), cancellationToken).ConfigureAwait(false);
        }
예제 #19
0
 protected override bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List <string> supportedAudioCodecs)
 {
     return(false);
 }
예제 #20
0
        private async Task <object> GetDynamicSegment(VideoStreamRequest request, string segmentId, string representationId)
        {
            if ((request.StartTimeTicks ?? 0) > 0)
            {
                throw new ArgumentException("StartTimeTicks is not allowed.");
            }

            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var requestedIndex = string.Equals(segmentId, "init", StringComparison.OrdinalIgnoreCase) ?
                                 -1 :
                                 int.Parse(segmentId, NumberStyles.Integer, UsCulture);

            var state = await GetState(request, cancellationToken).ConfigureAwait(false);

            var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".mpd");

            var segmentExtension = GetSegmentFileExtension(state);

            var segmentPath   = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex);
            var segmentLength = state.SegmentLength;

            TranscodingJob job = null;

            if (!string.IsNullOrWhiteSpace(segmentPath))
            {
                job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
                return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false));
            }

            await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);

            try
            {
                segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex);
                if (!string.IsNullOrWhiteSpace(segmentPath))
                {
                    job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
                    return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false));
                }
                else
                {
                    if (string.Equals(representationId, "0", StringComparison.OrdinalIgnoreCase))
                    {
                        job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
                        var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
                        var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength;
                        Logger.Debug("Current transcoding index is {0}. requestedIndex={1}. segmentGapRequiringTranscodingChange={2}", currentTranscodingIndex ?? -2, requestedIndex, segmentGapRequiringTranscodingChange);
                        if (currentTranscodingIndex == null || requestedIndex < currentTranscodingIndex.Value || (requestedIndex - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange)
                        {
                            // If the playlist doesn't already exist, startup ffmpeg
                            try
                            {
                                ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, p => false);

                                if (currentTranscodingIndex.HasValue)
                                {
                                    DeleteLastTranscodedFiles(playlistPath, 0);
                                }

                                var positionTicks = GetPositionTicks(state, requestedIndex);
                                request.StartTimeTicks = positionTicks;

                                var startNumber = GetStartNumber(state);

                                var workingDirectory = Path.Combine(Path.GetDirectoryName(playlistPath), (startNumber == -1 ? 0 : startNumber).ToString(CultureInfo.InvariantCulture));
                                state.WaitForPath = Path.Combine(workingDirectory, Path.GetFileName(playlistPath));
                                Directory.CreateDirectory(workingDirectory);
                                job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, workingDirectory).ConfigureAwait(false);
                                await WaitForMinimumDashSegmentCount(Path.Combine(workingDirectory, Path.GetFileName(playlistPath)), 1, cancellationTokenSource.Token).ConfigureAwait(false);
                            }
                            catch
                            {
                                state.Dispose();
                                throw;
                            }
                        }
                    }
                }
            }
            finally
            {
                ApiEntryPoint.Instance.TranscodingStartLock.Release();
            }

            while (string.IsNullOrWhiteSpace(segmentPath))
            {
                segmentPath = FindSegment(playlistPath, representationId, segmentExtension, requestedIndex);
                await Task.Delay(50, cancellationToken).ConfigureAwait(false);
            }

            Logger.Info("returning {0}", segmentPath);
            return(await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType), cancellationToken).ConfigureAwait(false));
        }
예제 #21
0
 protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
 {
     return false;
     //return base.CanStreamCopyVideo(request, videoStream);
 }
예제 #22
0
        private int GetStartNumber(VideoStreamRequest request)
        {
            var segmentId = "0";

            var segmentRequest = request as GetHlsVideoSegment;
            if (segmentRequest != null)
            {
                segmentId = segmentRequest.SegmentId;
            }

            return int.Parse(segmentId, NumberStyles.Integer, UsCulture);
        }
예제 #23
0
        protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
        {
            if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0)
            {
                Logger.Debug("Cannot stream copy video due to missing keyframe info");
                return false;
            }

            var previousSegment = 0;
            foreach (var frame in videoStream.KeyFrames)
            {
                var length = frame - previousSegment;

                // Don't allow really long segments because this could result in long download times
                if (length > 10000)
                {
                    Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length);
                    return false;
                }
                previousSegment = frame;
            }

            return base.CanStreamCopyVideo(request, videoStream);
        }
예제 #24
0
        private async Task <object> GetDynamicSegment(VideoStreamRequest request, string segmentId)
        {
            if ((request.StartTimeTicks ?? 0) > 0)
            {
                throw new ArgumentException("StartTimeTicks is not allowed.");
            }

            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var index = int.Parse(segmentId, NumberStyles.Integer, UsCulture);

            var state = await GetState(request, cancellationToken).ConfigureAwait(false);

            var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");

            var segmentExtension = GetSegmentFileExtension(state);

            var segmentPath   = GetSegmentPath(playlistPath, segmentExtension, index);
            var segmentLength = state.SegmentLength;

            TranscodingJob job = null;

            if (File.Exists(segmentPath))
            {
                return(await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false));
            }

            await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);

            try
            {
                if (File.Exists(segmentPath))
                {
                    return(await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false));
                }
                else
                {
                    var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);

                    if (currentTranscodingIndex == null || index < currentTranscodingIndex.Value || (index - currentTranscodingIndex.Value) > 4)
                    {
                        // If the playlist doesn't already exist, startup ffmpeg
                        try
                        {
                            ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType && string.Equals(j.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase));

                            if (currentTranscodingIndex.HasValue)
                            {
                                DeleteLastFile(playlistPath, segmentExtension, 0);
                            }

                            var startSeconds = index * state.SegmentLength;
                            request.StartTimeTicks = TimeSpan.FromSeconds(startSeconds).Ticks;

                            job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, Path.GetDirectoryName(playlistPath)).ConfigureAwait(false);
                        }
                        catch
                        {
                            state.Dispose();
                            throw;
                        }

                        await WaitForMinimumSegmentCount(playlistPath, 2, cancellationTokenSource.Token).ConfigureAwait(false);
                    }
                }
            }
            finally
            {
                ApiEntryPoint.Instance.TranscodingStartLock.Release();
            }

            Logger.Info("waiting for {0}", segmentPath);
            while (!File.Exists(segmentPath))
            {
                await Task.Delay(50, cancellationToken).ConfigureAwait(false);
            }

            Logger.Info("returning {0}", segmentPath);
            job = job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
            return(await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false));
        }