/// <summary> /// Processes the media subtitles and finds and handles external subtitle files /// </summary> /// <param name="config">The <see cref="DashConfig"/></param> /// <param name="subtitleFiles">The subtitle stream files</param> /// <param name="startFileIndex">The index additional subtitles need to start at. This should be the max index of the ffmpeg pieces +1</param> protected virtual IEnumerable <SubtitleStreamCommand> ProcessSubtitles(DashConfig config, IEnumerable <SubtitleStreamCommand> subtitleFiles, int startFileIndex) { // Move subtitles found in media foreach (var subFile in subtitleFiles) { string oldPath = subFile.Path; subFile.Path = Path.Combine(config.OutputDirectory, Path.GetFileName(subFile.Path)); yield return(subFile); if (oldPath != subFile.Path) { if (File.Exists(subFile.Path)) { File.Delete(subFile.Path); } File.Move(oldPath, subFile.Path); } } // Add external subtitles string baseFilename = Path.GetFileNameWithoutExtension(config.InputFilePath); foreach (var vttFile in Directory.EnumerateFiles(Path.GetDirectoryName(config.InputFilePath), baseFilename + "*", SearchOption.TopDirectoryOnly)) { if (vttFile.EndsWith(".vtt")) { string vttFilename = Path.GetFileName(vttFile); string vttName = GetSubtitleName(vttFilename); string vttOutputPath = Path.Combine(config.OutputDirectory, $"{config.OutputFileName}_subtitle_{vttName}_{startFileIndex}.vtt"); var subFile = new SubtitleStreamCommand() { Index = startFileIndex, Path = vttOutputPath, Language = $"{vttName}_{startFileIndex}" }; startFileIndex++; File.Copy(vttFile, vttOutputPath, true); yield return(subFile); } } }
/// <summary> /// Generates and appends commands for the given subtitle streams to the internal command set. /// </summary> public virtual FFmpegCommandBuilder WithSubtitleCommands(IEnumerable <MediaStream> streams) { if (!streams.Any()) { return(this); } foreach (MediaStream subtitleStream in streams) { if (!Constants.SupportedSubtitleCodecs.Contains(subtitleStream.codec_name)) { continue; } string language = subtitleStream.tag .Where(x => x.key == "language") .Select(x => x.value) .FirstOrDefault(); if (language is null) { language = "und"; } string path = Path.Combine(OutputDirectory, $"{OutputBaseFilename}_subtitle_{language}_{subtitleStream.index}.vtt"); SubtitleStreamCommand command = new SubtitleStreamCommand() { Index = subtitleStream.index, Language = language, Path = path, Argument = string.Join(" ", new string[] { $"-map 0:{subtitleStream.index}", '"' + path + '"' }) }; subtitleFiles.Add(command); } return(this); }
/// <summary> /// Generates an MPD AdaptationSet representing a subtitle stream. /// </summary> /// <param name="representationId">A generated unique representation ID to use for this AdaptationSet's </param> /// <param name="subtitle">The subtitle stream, representing a VTT file on disk.</param> /// <param name="nextRepresentationId">Must be set to the next unused representation ID.</param> /// <returns>An adaptation set to add to the output MPD file. May be null to not add this subtitle.</returns> protected virtual AdaptationSet GenerateSubtitleAdaptationSet(int representationId, SubtitleStreamCommand subtitle, out int nextRepresentationId) { nextRepresentationId = representationId + 1; return(new AdaptationSet() { MimeType = "text/vtt", Lang = subtitle.Language, ContentType = "text", Representation = new List <Representation>() { new Representation() { Id = representationId.ToString(), Bandwidth = 256, BaseURL = new List <string>() { Path.GetFileName(subtitle.Path) } } }, Role = new DescriptorType() { SchemeIdUri = "urn:gpac:dash:role:2013", Value = $"{subtitle.Language} {representationId}" } }); }