Exemple #1
0
        /// <summary>
        /// Utility method to write a single block into a buffer.
        /// </summary>
        /// <param name="block">Block to write.</param>
        /// <param name="builder">String builder buffer to write into.</param>
        private static void WriteBlock(
            BaseBlock block,
            StringBuilder builder)
        {
            Cue cue = block as Cue;

            if (cue != null)
            {
                WebVttSerializer.WriteCue(cue, builder);
                return;
            }

            RegionDefinition region = block as RegionDefinition;

            if (region != null)
            {
                WebVttSerializer.WriteRegion(region, builder);
                return;
            }

            Style style = block as Style;

            if (style != null)
            {
                WebVttSerializer.WrteStyle(style, builder);
                return;
            }

            Comment comment = block as Comment;

            if (comment != null)
            {
                WebVttSerializer.WriteComment(comment, builder);
                return;
            }

            throw new ArgumentOutOfRangeException(
                      string.Format("Unknown block type '{0}'.", block.GetType().FullName));
        }
Exemple #2
0
        /// <summary>
        /// Writes captions into a text in WebVTT format.
        /// </summary>
        /// <param name="captions">Caption blocks to serialize.</param>
        /// <param name="writer">Text writer to write into.</param>
        /// <remarks>See http://www.w3.org/TR/webvtt1/ for more details.</remarks>
        public static async Task SerializeAsync(
            IEnumerable <BaseBlock> captions,
            TextWriter writer)
        {
            if (writer == null)
            {
                throw new ArgumentNullException("writer");
            }

            StringBuilder builder = new StringBuilder(1024);

            builder.AppendLine(Constants.WebVttHeaderToken);

            if (captions != null)
            {
                Cue lastSeenCue = null;
                foreach (var block in captions)
                {
                    if (block == null)
                    {
                        throw new ArgumentException("Caption block cannot be null.", "captions");
                    }

                    Cue cue = block as Cue;

                    if (lastSeenCue != null)
                    {
                        if (block is RegionDefinition || block is Style)
                        {
                            throw new ArgumentException(
                                      string.Format("{0} is not allowed after Cue.", block.GetType().Name),
                                      "captions");
                        }

                        if (cue != null &&
                            cue.Start < lastSeenCue.Start)
                        {
                            throw new ArgumentException(
                                      string.Format("Cue start time '{0}' must be greater than or equal to previous cue start time '{1}'.",
                                                    cue.Start.ToString("g"),
                                                    lastSeenCue.Start.ToString("g")),
                                      "captions");
                        }
                    }

                    if (cue != null)
                    {
                        lastSeenCue = cue;
                    }

                    WebVttSerializer.WriteBlock(block, builder);

                    if (builder.Length >= WebVttSerializer.MaxBufferSize)
                    {
                        await writer.WriteAsync(builder.ToString())
                        .ConfigureAwait(false);

                        builder.Clear();
                    }
                }
            }

            if (builder.Length > 0)
            {
                await writer.WriteAsync(builder.ToString())
                .ConfigureAwait(false);
            }

            await writer.FlushAsync()
            .ConfigureAwait(false);
        }
Exemple #3
0
        /// <summary>
        /// Utility method to write a caption cue into a buffer.
        /// </summary>
        /// <param name="cue">Cue to write.</param>
        /// <param name="builder">String builder buffer to write into.</param>
        private static void WriteCue(
            Cue cue,
            StringBuilder builder)
        {
            if (cue.End <= cue.Start)
            {
                throw new ArgumentException(string.Format("Cue start time '{0}' must be less than cue end time '{1}'.", cue.Start, cue.End));
            }

            builder.AppendLine();
            if (false == string.IsNullOrWhiteSpace(cue.Id))
            {
                builder.AppendLine(cue.Id);
            }

            WebVttSerializer.WriteTimeSpanValue(cue.Start, builder);
            builder
            .Append(' ')
            .Append(Constants.ArrowToken)
            .Append(' ');
            WebVttSerializer.WriteTimeSpanValue(cue.End, builder);

            if (false == string.IsNullOrWhiteSpace(cue.Region))
            {
                if (WebVttSerializer.HasWhiteSpace(cue.Region))
                {
                    throw new ArgumentException("White space characters are not allowed in cue region id.");
                }

                builder
                .Append(' ')
                .Append(Constants.RegionName).Append(':').Append(cue.Region);
            }

            if (cue.Alignment.HasValue)
            {
                builder
                .Append(' ')
                .Append(Constants.AlignName).Append(':').Append(WebVttSerializer.GetAlignmentValue(cue.Alignment.Value));
            }

            if (cue.Line.HasValue)
            {
                builder
                .Append(' ')
                .Append(Constants.LineName).Append(':');

                if (cue.Line.Value.Percent.HasValue)
                {
                    builder.Append(WebVttSerializer.GetPercentValue(cue.Line.Value.Percent.Value));
                }
                else if (cue.Line.Value.LineNumber.HasValue)
                {
                    builder.Append(cue.Line.Value.LineNumber.Value);
                }
                else
                {
                    throw new ArgumentException("Cue line setting must specify either percent or line number value.");
                }

                if (cue.Line.Value.Alignment.HasValue)
                {
                    builder.Append(',').Append(WebVttSerializer.GetLineAlignmentValue(cue.Line.Value.Alignment.Value));
                }
            }

            if (cue.Position.HasValue)
            {
                builder
                .Append(' ')
                .Append(Constants.PositionName).Append(':')
                .Append(WebVttSerializer.GetPercentValue(
                            cue.Position.Value.PositionPercent.HasValue
                        ? cue.Position.Value.PositionPercent.Value
                        : 0.0));

                if (cue.Position.Value.Alignment.HasValue)
                {
                    builder
                    .Append(',')
                    .Append(WebVttSerializer.GetPositionAlignmentValue(cue.Position.Value.Alignment.Value));
                }
            }

            if (cue.SizePercent.HasValue)
            {
                builder
                .Append(' ')
                .Append(Constants.SizeName)
                .Append(":")
                .Append(WebVttSerializer.GetPercentValue(cue.SizePercent.Value));
            }

            if (cue.Vertical.HasValue)
            {
                builder
                .Append(' ')
                .Append(Constants.VerticalName)
                .Append(":")
                .Append(WebVttSerializer.GetVerticalAlignmentValue(cue.Vertical.Value));
            }

            builder.AppendLine();

            if (cue.Content != null && cue.Content.Length > 0)
            {
                Span previousSpan = null;
                foreach (var span in cue.Content)
                {
                    if (span == null)
                    {
                        throw new ArgumentException("Cue content cannot be null.");
                    }

                    if (WebVttSerializer.NeedNewLine(previousSpan, span))
                    {
                        builder.AppendLine();
                    }

                    WebVttSerializer.WriteSpan(span, builder);
                    previousSpan = span;
                }

                builder.AppendLine();
            }
        }
Exemple #4
0
        /// <summary>
        /// Utility method to return all parts of media captions are a sequence of blocks.
        /// </summary>
        /// <param name="captions">Captions to process.</param>
        /// <returns>Sequence of blocks.</returns>
        private static IEnumerable <BaseBlock> GetMediaCaptionBlocks(
            MediaCaptions captions)
        {
            if (captions != null)
            {
                if (captions.Styles != null && captions.Styles.Length > 0)
                {
                    foreach (var style in captions.Styles)
                    {
                        if (style != null)
                        {
                            yield return(style);
                        }
                    }
                }

                if (captions.Regions != null && captions.Regions.Length > 0)
                {
                    foreach (var region in captions.Regions)
                    {
                        if (region != null)
                        {
                            yield return(region);
                        }
                    }
                }

                if (captions.Cues != null && captions.Cues.Length > 0)
                {
                    bool needSort = false;
                    Cue  previous = null;

                    foreach (var cue in captions.Cues)
                    {
                        if (cue != null)
                        {
                            needSort = previous != null && cue.Start < previous.Start;
                            previous = cue;
                        }

                        if (needSort)
                        {
                            break;
                        }
                    }

                    if (needSort)
                    {
                        var tmp = new List <Cue>(captions.Cues.Length);
                        foreach (var cue in captions.Cues)
                        {
                            if (cue != null)
                            {
                                tmp.Add(cue);
                            }
                        }

                        if (tmp.Count > 0)
                        {
                            tmp.Sort((c1, c2) => c1.Start < c2.Start
                                ? -1
                                : (c1.Start > c2.Start ? 1 : c1.End.CompareTo(c2.End)));

                            foreach (var cue in tmp)
                            {
                                yield return(cue);
                            }
                        }
                    }
                    else
                    {
                        foreach (var cue in captions.Cues)
                        {
                            yield return(cue);
                        }
                    }
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// Reads WebVTT media captions from a given <see cref="TextReader"/>.
        /// </summary>
        /// <param name="reader">Text reader to read the captions.</param>
        /// <returns>A task that represents the asynchronous read operation.</returns>
        public static async Task <MediaCaptions> ReadMediaCaptionsAsync(
            TextReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            // Process stream header line
            var line = await reader.ReadLineAsync()
                       .ConfigureAwait(false);

            if (string.IsNullOrWhiteSpace(line) ||
                line.Length < 6 ||
                false == line.StartsWith(Constants.WebVttHeaderToken) ||
                line.Length >= 7 && line[6] != '\t' && line[6] != ' ')
            {
                throw new InvalidDataException("The stream does not start with the correct WebVTT file signature.");
            }

            var result = new MediaCaptions();

            // Process (skip over) optional headers from the stream
            while (false == string.IsNullOrEmpty(line))
            {
                line = await reader.ReadLineAsync()
                       .ConfigureAwait(false);
            }

            // Process media caption blocks.
            List <RegionDefinition> regions = new List <RegionDefinition>();
            List <Style>            styles  = new List <Style>();
            List <Cue> cues = new List <Cue>();

            BaseBlock block;

            do
            {
                block = await WebVttParser.ReadBlockAsync(reader)
                        .ConfigureAwait(false);

                RegionDefinition region = block as RegionDefinition;
                Style            style  = block as Style;
                Cue cue = block as Cue;

                if (cues.Count == 0)
                {
                    if (region != null)
                    {
                        regions.Add(region);
                    }
                    else if (style != null)
                    {
                        styles.Add(style);
                    }
                }
                else if (region != null || style != null)
                {
                    throw new InvalidDataException("Region or style blocks cannot be mixed with cue blocks.");
                }

                if (cue != null)
                {
                    cues.Add(cue);
                }
            }while (block != null);

            if (regions.Count > 0)
            {
                result.Regions = regions.ToArray();
            }

            if (styles.Count > 0)
            {
                result.Styles = styles.ToArray();
            }

            if (cues.Count > 0)
            {
                result.Cues = cues.ToArray();
            }

            return(result);
        }
Exemple #6
0
        /// <summary>
        /// Reads WebVTT Cue block from the reader.
        /// </summary>
        /// <param name="firstLine">First line of the block read from the reader.</param>
        /// <param name="reader">Text reader to read the style block.</param>
        /// <returns>A task that represents the asynchronous read operation.</returns>
        private static async Task <Cue> ReadCueAsync(
            string firstLine,
            TextReader reader)
        {
            string line    = firstLine;
            var    content = new StringBuilder(100);

            Cue result = new Cue();

            if (false == line.Contains(Constants.ArrowToken))
            {
                result.Id = line;
                line      = await reader.ReadLineAsync()
                            .ConfigureAwait(false);
            }

            int position = 0;

            result.Start = ParseTimeSpan(line, ref position);

            while (position < line.Length && char.IsWhiteSpace(line, position))
            {
                position++;
            }

            if (0 != string.CompareOrdinal(line, position, Constants.ArrowToken, 0, Constants.ArrowToken.Length))
            {
                throw new InvalidDataException(string.Format("Invalid characters found in cue timings '{0}' at position {1}.", line, position));
            }

            position += Constants.ArrowToken.Length;

            while (position < line.Length && char.IsWhiteSpace(line, position))
            {
                position++;
            }

            result.End = ParseTimeSpan(line, ref position);

            if (result.End < result.Start)
            {
                throw new InvalidDataException(string.Format("Cue start time is greater than end time in '{0}'.", line));
            }

            if (position < line.Length)
            {
                var settings = line.Substring(position).TrimStart('\t', ' ').TrimEnd('\t', ' ');
                if (false == string.IsNullOrWhiteSpace(settings))
                {
                    result.RawSettings = settings;

                    var parsedSettings = WebVttParser.ParseSettings(settings);
                    if (parsedSettings != null)
                    {
                        VerticalTextLayout vertical;
                        if (WebVttParser.TryGetVerticalTextLayoutSetting(Constants.VerticalName, parsedSettings, out vertical))
                        {
                            result.Vertical = vertical;
                        }

                        LineSettings lineSetting;
                        if (WebVttParser.TryGetLineSetting(Constants.LineName, parsedSettings, out lineSetting))
                        {
                            result.Line = lineSetting;
                        }

                        PositionSettings positionSetting;
                        if (WebVttParser.TryGetPositionSetting(Constants.PositionName, parsedSettings, out positionSetting))
                        {
                            result.Position = positionSetting;
                        }

                        double percent;
                        if (WebVttParser.TryGetPercentSetting(Constants.SizeName, parsedSettings, out percent))
                        {
                            result.SizePercent = percent;
                        }

                        TextAlignment alignment;
                        if (WebVttParser.TryGetAlignmentSetting(Constants.AlignName, parsedSettings, out alignment))
                        {
                            result.Alignment = alignment;
                        }

                        string name;
                        if (WebVttParser.TryGetStringSetting(Constants.RegionName, parsedSettings, out name))
                        {
                            result.Region = name;
                        }
                    }
                }
            }

            while (false == string.IsNullOrEmpty(line))
            {
                line = await reader.ReadLineAsync()
                       .ConfigureAwait(false);

                if (false == string.IsNullOrEmpty(line))
                {
                    if (line.Contains(Constants.ArrowToken))
                    {
                        throw new InvalidDataException(string.Format("Cue must not contain '{0}'.", Constants.ArrowToken));
                    }

                    content.SafeAppendLine(line);
                }
            }

            if (content.Length > 0)
            {
                result.RawContent = content.ToString();

                List <Span> spans;

                position = 0;
                if (WebVttParser.TryParseSpans(result.RawContent, ref position, out spans) &&
                    spans != null &&
                    spans.Count > 0)
                {
                    result.Content = spans.ToArray();
                }
            }

            return(result);
        }