Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the tool ready to hook a list of fragblobs for vtt file generati… #126

Merged
merged 1 commit into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion contracts/ClientManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,9 @@ public class ClientManifest
{
foreach (var stream in Streams)
{
if (stream.Type == track.Type)
var streamName = stream.Name ?? stream.Type.ToString();

if (stream.Type == track.Type && streamName == track.TrackName)
{
for (var i = 0; i < stream.TrackCount; ++i)
{
Expand Down
2 changes: 2 additions & 0 deletions contracts/Manifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public Track(StreamType type)
public bool IsMultiFile => string.IsNullOrEmpty(Path.GetExtension(Source));

public uint TrackID => uint.Parse(Parameters.Single(p => p.Name == "trackID").Value);

public string TrackName => Parameters.SingleOrDefault(p => p.Name == "trackName")?.Value ?? Type.ToString();
}

public class VideoTrack : Track
Expand Down
61 changes: 55 additions & 6 deletions pipes/MultiFileStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class MultiFileStream
private readonly ILogger _logger;
private readonly MediaStream _track;
private readonly string _trackPrefix;
private readonly bool _isCloseCaption;
private readonly StorageEncryptedAssetDecryptionInfo? _decryptInfo;

public MultiFileStream(
Expand All @@ -30,6 +31,8 @@ public MultiFileStream(
(_track, _) = manifest.GetStream(track);
_trackPrefix = track.Source;
_decryptInfo = decryptInfo;

_isCloseCaption = _track.Type == StreamType.Text && _track.SubType == "SUBT";
}

public async Task DownloadAsync(Stream stream, CancellationToken cancellationToken)
Expand All @@ -38,9 +41,16 @@ public async Task DownloadAsync(Stream stream, CancellationToken cancellationTok
try
{
_logger.LogDebug("Begin downloading track: {name}", _trackPrefix);
chunkName = $"{_trackPrefix}/header";
var blob = _container.GetBlockBlobClient(chunkName);
await DownloadClearBlobContent(blob, stream, cancellationToken);

BlockBlobClient blob;

if (!_isCloseCaption)
{
// Header blob is needed only for audio/video cmaf.
chunkName = $"{_trackPrefix}/header";
blob = _container.GetBlockBlobClient(chunkName);
await DownloadClearBlobContent(blob, stream, cancellationToken);
}

// Report progress every 10%.
var i = 0;
Expand Down Expand Up @@ -133,6 +143,38 @@ private void GenerateCmafFragment(IList<Box> inputBoxes, MP4Writer mp4Writer)
}
}

/// <summary>
/// A helper function to generate VTT text for a specific fragblob
/// </summary>
/// <param name="inputBoxes">A list of boxes for an close caption fragblob.</param>
/// <param name="mp4Writer">The writer for the output stream. </param>
private void GenerateVttContent(IList<Box> inputBoxes, MP4Writer mp4Writer)
{
if (inputBoxes.Count != 2)
{
throw new ArgumentException("A live fragment for close caption must contain two mp4-boxes.", nameof(inputBoxes));
}

var moofBox = inputBoxes[0] as moofBox;
var mdatBox = inputBoxes[1] as mdatBox;

if (moofBox == null || mdatBox == null)
{
throw new ArgumentException("A live fragment must contain moof box and mdat box.", nameof(inputBoxes));
}

var ttmlText = mdatBox.SampleData;

byte[] vttText = { 0 };

// Call API to convert ttmlText to VTT text.

// Uncomment this line, it was put here to pass the compiler.
vttText = ttmlText!;

mp4Writer.Write(vttText);
}

private async Task DownloadClearBlobContent(BlockBlobClient sourceBlob, Stream outputStream, CancellationToken cancellationToken)
{
using var tmpStream = new MemoryStream();
Expand Down Expand Up @@ -177,9 +219,16 @@ private async Task DownloadClearBlobContent(BlockBlobClient sourceBlob, Stream o
}
else
{
// It is for a fragment generated by a live channel.
// Generate cmaf fragment from the input stream.
GenerateCmafFragment(boxes, writer);
if (_isCloseCaption)
{
GenerateVttContent(boxes, writer);
}
else
{
// It is for a fragment generated by a live channel.
// Generate cmaf fragment from the input stream.
GenerateCmafFragment(boxes, writer);
}
}

writer.Flush();
Expand Down
28 changes: 25 additions & 3 deletions transform/BasePackager.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using AMSMigrate.Contracts;
using AMSMigrate.Pipes;
using Azure.Storage.Blobs.Specialized;
using FFMpegCore.Pipes;
using Microsoft.Extensions.Logging;
using System.Diagnostics;

Expand Down Expand Up @@ -54,7 +53,23 @@ public BasePackager(AssetDetails assetDetails, TransMuxer transMuxer, ILogger lo
SelectedTracks = manifest.Tracks.Where(t => {
if (t is TextTrack)
{
return !TransmuxedDownload && !t.IsMultiFile && (t.Source.EndsWith(VTT_FILE) || t.Parameters.Any(t => t.Name == TRANSCRIPT_SOURCE));
bool pickThisTextTrack = !TransmuxedDownload && !t.IsMultiFile && (t.Source.EndsWith(VTT_FILE) || t.Parameters.Any(t => t.Name == TRANSCRIPT_SOURCE));

if (manifest.IsLiveArchive)
{
pickThisTextTrack = false;

if (t.IsMultiFile)
{
// Choose the text track with a list of fragblobs for close captions.
pickThisTextTrack = assetDetails.ClientManifest!.Streams.Any(
stream => (stream.Type == StreamType.Text &&
stream.SubType == "SUBT") &&
stream.Name == t.TrackName);
}
}

return pickThisTextTrack;
}
return true;
}).ToList();
Expand All @@ -66,7 +81,14 @@ public BasePackager(AssetDetails assetDetails, TransMuxer transMuxer, ILogger lo
string input;
if (track is TextTrack)
{
input = track.Source.EndsWith(VTT_FILE) ? track.Source : track.Parameters.Single(p => p.Name == TRANSCRIPT_SOURCE).Value;
if (manifest.IsLiveArchive)
{
input = $"{track.Source}{VTT_FILE}";
}
else
{
input = track.Source.EndsWith(VTT_FILE) ? track.Source : track.Parameters.Single(p => p.Name == TRANSCRIPT_SOURCE).Value;
}
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion transform/FfmpegPackager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public override async Task<bool> RunAsync(
{
foreach (var track in SelectedTracks)
{
var ext = track.IsMultiFile ? MEDIA_FILE : string.Empty;
var ext = track.IsMultiFile ? (track is TextTrack ? VTT_FILE : MEDIA_FILE) : string.Empty;
var index = Inputs.IndexOf($"{track.Source}{ext}");
options.SelectStream(0, index, track is VideoTrack ? Channel.Video : Channel.Audio);
}
Expand Down
2 changes: 1 addition & 1 deletion transform/ShakaPackager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private IEnumerable<string> GetArguments(IList<string> inputs, IList<string> out

List<string> arguments = new(SelectedTracks.Select((t, i) =>
{
var ext = t.IsMultiFile ? MEDIA_FILE : string.Empty;
var ext = t.IsMultiFile ? (t is TextTrack ? VTT_FILE : MEDIA_FILE) : string.Empty;
var file = $"{t.Source}{ext}";
var index = Inputs.IndexOf(file);
var multiTrack = TransmuxedDownload && FileToTrackMap[file].Count > 1;
Expand Down
Loading