Tutorial: Moderação de vídeos e transcriçõesTutorial: Video and transcript moderation

Neste tutorial, você aprenderá a construir uma solução completa de moderação de vídeo e transcrição com moderação assistida por máquinas e integração de revisão humana.In this tutorial, you will learn how to build a complete video and transcript moderation solution with machine-assisted moderation and human review integration.

Este tutorial mostrar-lhe como:This tutorial shows you how to:

  • Comprimir o(s) vídeo(s) de entrada para acelerar o processamentoCompress the input video(s) for faster processing
  • Moderar o vídeo para obter imagens e fotogramas com informaçõesModerate the video to get shots and frames with insights
  • Utilizar os carimbos de data/hora de fotogramas para criar miniaturas (imagens)Use the frame timestamps to create thumbnails (images)
  • Submeter carimbos de data/hora e miniaturas para criar revisões de vídeoSubmit timestamps and thumbnails to create video reviews
  • Converter voz em texto (transcrição) com a API do Media IndexerConvert the video speech to text (transcript) with the Media Indexer API
  • Moderar a transcrição com o serviço de moderação de textosModerate the transcript with the text moderation service
  • Adicionar a transcrição moderada à revisão de vídeoAdd the moderated transcript to the video review

Pré-requisitosPrerequisites

  • Inscreva-se no web site da ferramenta Content Moderator Review e crie etiquetas personalizadas.Sign up for the Content Moderator Review tool web site and create custom tags. Consulte as etiquetas use se precisar de ajuda com este passo.See Use tags if you need help with this step.

    screenshot de etiquetas personalizadas de moderação de vídeo

  • Para executar a aplicação da amostra, precisa de uma conta Azure, um recurso Azure Media Services, um recurso Azure Content Moderador e credenciais de Diretório Ativo Azure.To run the sample application, you need an Azure account, an Azure Media Services resource, an Azure Content Moderator resource, and Azure Active Directory credentials. Para obter instruções sobre como obter estes recursos, consulte o guia da API de Moderação de Vídeo.For instructions on how to get these resources, see the Video Moderation API guide.

  • Descarregue a aplicação de consola de revisão de vídeo do GitHub.Download the Video review console application From GitHub.

Introduzir credenciaisEnter credentials

Editar App.config o ficheiro e adicionar o nome do inquilino do Diretório Ativo, pontos finais de serviço e chaves de subscrição indicados por #####.Edit the App.config file and add the Active Directory tenant name, service endpoints, and subscription keys indicated by #####. Precisará das seguintes informações:You need the following information:

ChaveKey DescriçãoDescription
AzureMediaServiceRestApiEndpoint Ponto final da API dos Serviços de Multimédia do Azure (AMS)Endpoint for the Azure Media Services (AMS) API
ClientSecret Chave de subscrição dos Serviços de Multimédia do AzureSubscription key for Azure Media Services
ClientId ID de cliente dos Serviços de Multimédia do AzureClient ID for Azure Media Services
AzureAdTenantName Nome de inquilino do Active Directory que representa a sua organizaçãoActive Directory tenant name representing your organization
ContentModeratorReviewApiSubscriptionKey Chave de subscrição da API de revisão do Content ModeratorSubscription key for the Content Moderator review API
ContentModeratorApiEndpoint Ponto final da API do Content ModeratorEndpoint for the Content Moderator API
ContentModeratorTeamId ID de equipa do Content ModeratorContent moderator team ID

Examinar o código principalExamine the main code

A classe Program em Program.cs é o principal ponto de entrada da aplicação de moderação de vídeos.The class Program in Program.cs is the main entry point to the video moderation application.

Métodos de classe programaMethods of Program class

MétodoMethod DescriçãoDescription
Main Analisa a linha de comandos, reúne as introduções dos utilizadores e dá início ao processamento.Parses command line, gathers user input, and starts processing.
ProcessVideo Comprime, carrega, modera e cria revisões de vídeo.Compresses, uploads, moderates, and creates video reviews.
CreateVideoStreamingRequest Cria um fluxo para carregar um vídeoCreates a stream to upload a video
GetUserInputs Reúne as introduções dos utilizadores; utilizado quando não estão presentes opções de linha de comandosGathers user input; used when no command-line options are present
Initialize Inicializa os objetos necessários para o processo de moderaçãoInitializes objects needed for the moderation process

O método MainThe Main method

Main() é onde começa a execução, pelo que é o lugar onde se deve começar a compreender o processo de moderação de vídeos.Main() is where execution starts, so it's the place to start understanding the video moderation process.

static void Main(string[] args)
{
    if (args.Length == 0)
    {
        string videoPath = string.Empty;
            Initialize();
            GetUserInputs(out videoPath);
            AmsConfigurations.logFilePath = Path.Combine(Path.GetDirectoryName(videoPath), "log.txt");
            try
            {
                ProcessVideo(videoPath).Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
    }
    else
    {
        DirectoryInfo directoryInfo = new DirectoryInfo(args[0]);
        if (args.Length == 2) bool.TryParse(args[1], out generateVtt);
        Initialize();
        AmsConfigurations.logFilePath = Path.Combine(args[0], "log.txt");
        var files = directoryInfo.GetFiles("*.mp4", SearchOption.AllDirectories);
        foreach (var file in files)
        {
            try
            {
                ProcessVideo(file.FullName).Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

Main() lida com os seguintes argumentos de linha de comandos:Main() handles the following command-line arguments:

  • O caminho para um diretório que contém ficheiros de vídeo MPEG-4 que serão submetidos para moderação.The path to a directory containing MPEG-4 video files to be submitted for moderation. Todos os ficheiros *.mp4 neste diretório e respetivos subdiretórios são submetidos para moderação.All *.mp4 files in this directory and its subdirectories are submitted for moderation.
  • Opcionalmente, um sinalizador booleano (verdadeiro/falso) que indica se as transcrições de texto devem ser geradas para efeitos de moderação de áudio.Optionally, a Boolean (true/false) flag indicating whether text transcripts should be generated for the purpose of moderating audio.

Se não estiver presente nenhum argumento de linha de comandos, Main() chamará GetUserInputs().If no command-line arguments are present, Main() calls GetUserInputs(). Este método pede ao utilizador para introduzir o caminho para um único ficheiro de vídeo e para especificar se deve ser gerada uma transcrição de texto.This method prompts the user to enter the path to a single video file and to specify whether a text transcript should be generated.

Nota

A aplicação de consola utiliza a API do Indexer Azure Media para gerar transcrições a partir da faixa de áudio do vídeo carregado. Os resultados são fornecidos em formato WebVTT.The console application uses the Azure Media Indexer API to generate transcripts from the uploaded video's audio track. The results are provided in WebVTT format. Para obter mais informações sobre este formato, veja Formato de Faixas de Texto de Vídeo Web.For more information on this format, see Web Video Text Tracks Format.

Métodos Initialize e ProcessVideoInitialize and ProcessVideo methods

Independentemente de as opções do programa provirem da linha de comandos ou das introduções interativas dos utilizadores, Main() chama em seguida Initialize() para criar as seguintes instâncias:Regardless of whether the program's options came from the command line or from interactive user input, Main() next calls Initialize() to create the following instances:

ClasseClass DescriçãoDescription
AMSComponent Comprime ficheiros de vídeo antes de os submeter para moderação.Compresses video files before submitting them for moderation.
AMSconfigurations Interface para os dados de configuração da aplicação encontrados em App.config.Interface to the application's configuration data, found in App.config.
VideoModerator Carregamento, codificação, encriptação e moderação com o SDK dos Serviços de Multimédia do AzureUploading, encoding, encryption, and moderation using AMS SDK
VideoReviewApi Gere revisões de vídeo no serviço do Content ModeratorManages video reviews in the Content Moderator service

Estas classes (exceto AMSConfigurations, que é simples) são abordadas mais detalhadamente nas próximas secções deste tutorial.These classes (aside from AMSConfigurations, which is straightforward) are covered in more detail in upcoming sections of this tutorial.

Por fim, os ficheiros de vídeo são processados um de cada vez ao chamar ProcessVideo() para cada um.Finally, the video files are processed one at a time by calling ProcessVideo() for each.

private static async Task ProcessVideo(string videoPath)
{
    var watch = System.Diagnostics.Stopwatch.StartNew();
    Console.ForegroundColor = ConsoleColor.White;
    Console.WriteLine("\nVideo compression process started...");

    var compressedVideoPath = amsComponent.CompressVideo(videoPath);
    if (string.IsNullOrWhiteSpace(compressedVideoPath))
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("Video Compression failed.");
    }

    Console.WriteLine("\nVideo compression process completed...");

    UploadVideoStreamRequest uploadVideoStreamRequest = CreateVideoStreamingRequest(compressedVideoPath);
    UploadAssetResult uploadResult = new UploadAssetResult();

    if (generateVtt)
    {
        uploadResult.GenerateVTT = generateVtt;
    }
    Console.WriteLine("\nVideo moderation process started...");

    if (!videoModerator.CreateAzureMediaServicesJobToModerateVideo(uploadVideoStreamRequest, uploadResult))
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("\nVideo moderation process failed.");
    }

    Console.WriteLine("\nVideo moderation process completed...");
    Console.WriteLine("\nVideo review process started...");

    string reviewId = await videoReviewApi.CreateVideoReviewInContentModerator(uploadResult);

    watch.Stop();

    Console.WriteLine("\nVideo review successfully completed...");
    Console.WriteLine("\nTotal Elapsed Time: {0}", watch.Elapsed);
    Logger.Log("Video File Name: " + Path.GetFileName(videoPath));
    Logger.Log($"ReviewId: {reviewId}");
    Logger.Log($"Total Elapsed Time: {watch.Elapsed}");
}

O método ProcessVideo() é relativamente simples.The ProcessVideo() method is fairly straightforward. Permite realizar as seguintes operações pela ordem apresentada:It performs the following operations in the order:

  • Comprime o vídeoCompresses the video
  • Carrega o vídeo para um elemento dos Serviços de Multimédia do AzureUploads the video to an Azure Media Services asset
  • Cria uma tarefa dos Serviços de Multimédia do Azure para moderar o vídeoCreates an AMS job to moderate the video
  • Cria uma revisão de vídeo no Content ModeratorCreates a video review in Content Moderator

As seguintes secções abordam mais detalhadamente alguns dos processos individuais invocados por ProcessVideo().The following sections consider in more detail some of the individual processes invoked by ProcessVideo().

Comprima o vídeoCompress the video

Para minimizar o tráfego de rede, a aplicação converte os ficheiros de vídeo no formato H.264 (MPEG-4 AVC) e dimensiona-os até estes ficarem com uma largura máxima de 640 píxeis.To minimize network traffic, the application converts video files to H.264 (MPEG-4 AVC) format and scales them to a maximum width of 640 pixels. O codec H.264 é recomendado devido à sua elevada eficiência (taxa de compressão).The H.264 codec is recommended due to its high efficiency (compression rate). A compressão é realizada com recurso à ferramenta de linha de comandos ffmpeg gratuita que está incluída na pasta Lib da solução Visual Studio.The compression is done using the free ffmpeg command-line tool, which is included in the Lib folder of the Visual Studio solution. O formato dos ficheiros de entrada pode ser qualquer formato suportado por ffmpeg, incluindo os codecs e formatos de ficheiros de vídeo mais comuns.The input files may be of any format supported by ffmpeg, including most commonly used video file formats and codecs.

Nota

Quando iniciar o programa com as opções da linha de comandos, especificará um diretório que contém os ficheiros de vídeo que serão submetidos para moderação.When you start the program using command-line options, you specify a directory containing the video files to be submitted for moderation. Todos os ficheiros neste diretório com a extensão de nome de ficheiro .mp4 serão processados.All files in this directory having the .mp4 filename extension are processed. Para processar outras extensões de nome de ficheiro, atualize o método Main() em Program.cs de forma a incluir as extensões pretendidas.To process other filename extensions, update the Main() method in Program.cs to include the desired extensions.

O código que comprime um único ficheiro de vídeo é a classe AmsComponent em AMSComponent.cs.The code that compresses a single video file is the AmsComponent class in AMSComponent.cs. O método responsável por esta funcionalidade é CompressVideo(), apresentado aqui.The method responsible for this functionality is CompressVideo(), shown here.

public string CompressVideo(string videoPath)
{
    string ffmpegBlobUrl;
    if (!ValidatePreRequisites())
    {
        Console.WriteLine("Configurations check failed. Please cross check the configurations!");
        throw new Exception();
    }

    if (File.Exists(_configObj.FfmpegExecutablePath))
    {
        ffmpegBlobUrl = this._configObj.FfmpegExecutablePath;
    }
    else
    {
        Console.WriteLine("ffmpeg.exe is missing. Please check the Lib folder");
        throw new Exception();
    }

    string videoFilePathCom = videoPath.Split('.')[0] + "_c.mp4";
    ProcessStartInfo processStartInfo = new ProcessStartInfo();
    processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    processStartInfo.FileName = ffmpegBlobUrl;
    processStartInfo.Arguments = "-i \"" + videoPath + "\" -vcodec libx264 -n -crf 32 -preset veryfast -vf scale=640:-1 -c:a aac -aq 1 -ac 2 -threads 0 \"" + videoFilePathCom + "\"";
    var process = Process.Start(processStartInfo);
    process.WaitForExit();
    process.Close();
    return videoFilePathCom;
}

O código realiza os seguintes passos:The code performs the following steps:

  • Efetua verificações para garantir que a configuração em App.config contém todos os dados necessáriosChecks to make sure the configuration in App.config contains all necessary data
  • Efetua verificações para garantir que o binário ffmpeg está presenteChecks to make sure the ffmpeg binary is present
  • Compila o nome de ficheiro de saída ao anexar _c.mp4 ao nome de base do ficheiro (tal como Example.mp4 -> Example_c.mp4)Builds the output filename by appending _c.mp4 to the base name of the file (such as Example.mp4 -> Example_c.mp4)
  • Cria uma cadeia de linha de comandos para realizar a conversãoBuilds a command-line string to perform the conversion
  • Inicia um processo ffmpeg com recurso à linha de comandosStarts an ffmpeg process using the command line
  • Aguarda que o vídeo seja processadoWaits for the video to be processed

Nota

Se souber que os seus vídeos já foram comprimidos com o codec H.264 e têm as dimensões adequadas, pode reescrever CompressVideo() para ignorar a compressão.If you know your videos are already compressed using H.264 and have appropriate dimensions, you can rewrite CompressVideo() to skip the compression.

O método devolve o nome de ficheiro do ficheiro de saída comprimido.The method returns the filename of the compressed output file.

Faça upload e moderar o vídeoUpload and moderate the video

É necessário armazenar o vídeo nos Serviços de Multimédia do Azure antes de o serviço do Content Moderator o processar.The video must be stored in Azure Media Services before it can be processed by the Content Moderation service. A classe Program em Program.cs dispõe de um método CreateVideoStreamingRequest() curto que devolve um objeto que representa o pedido de transmissão em fluxo para carregar o vídeo.The Program class in Program.cs has a short method CreateVideoStreamingRequest() that returns an object representing the streaming request used to upload the video.

private static UploadVideoStreamRequest CreateVideoStreamingRequest(string compressedVideoFilePath)
{
    return
        new UploadVideoStreamRequest
        {
            VideoStream = File.ReadAllBytes(compressedVideoFilePath),
            VideoName = Path.GetFileName(compressedVideoFilePath),
            EncodingRequest = new EncodingRequest()
            {
                EncodingBitrate = AmsEncoding.AdaptiveStreaming
            },
            VideoFilePath = compressedVideoFilePath
        };
}

O objeto UploadVideoStreamRequest resultante é definido em UploadVideoStreamRequest.cs (e o respetivo principal UploadVideoRequest é definido em UploadVideoRequest.cs).The resulting UploadVideoStreamRequest object is defined in UploadVideoStreamRequest.cs (and its parent, UploadVideoRequest, in UploadVideoRequest.cs). Estas classes não são apresentadas aqui, dado serem curtas e servirem apenas para armazenar os dados de vídeo comprimidos e as informações sobre os mesmos.These classes aren't shown here; they're short and serve only to hold the compressed video data and information about it. UploadAssetResult (UploadAssetResult.cs) é outra classe apenas de dados que é utilizada para armazenar os resultados do processo de carregamento.Another data-only class, UploadAssetResult (UploadAssetResult.cs) is used to hold the results of the upload process. Agora é possível compreender estas linhas em ProcessVideo():Now it's possible to understand these lines in ProcessVideo():

UploadVideoStreamRequest uploadVideoStreamRequest = CreateVideoStreamingRequest(compressedVideoPath);
UploadAssetResult uploadResult = new UploadAssetResult();

if (generateVtt)
{
    uploadResult.GenerateVTT = generateVtt;
}
Console.WriteLine("\nVideo moderation process started...");

if (!videoModerator.CreateAzureMediaServicesJobToModerateVideo(uploadVideoStreamRequest, uploadResult))
{
    Console.ForegroundColor = ConsoleColor.Red;
    Console.WriteLine("\nVideo moderation process failed.");
}

Estas linhas realizam as seguintes tarefas:These lines perform the following tasks:

  • Criar um UploadVideoStreamRequest para carregar o vídeo comprimidoCreate a UploadVideoStreamRequest to upload the compressed video
  • Definir o sinalizador GenerateVTT do pedido se o utilizador tiver pedido uma transcrição de textoSet the request's GenerateVTT flag if the user has requested a text transcript
  • Chamar CreateAzureMediaServicesJobToModerateVideo() para realizar o carregamento e receber o resultadoCalls CreateAzureMediaServicesJobToModerateVideo() to perform the upload and receive the result

Examine o código de moderação de vídeoExamine video moderation code

O método CreateAzureMediaServicesJobToModerateVideo() está em VideoModerator.cs, que contém a maior parte do código que interage com os Serviços de Multimédia do Azure.The method CreateAzureMediaServicesJobToModerateVideo() is in VideoModerator.cs, which contains the bulk of the code that interacts with Azure Media Services. O código fonte do método é apresentado no seguinte excerto.The method's source code is shown in the following extract.

public bool CreateAzureMediaServicesJobToModerateVideo(UploadVideoStreamRequest uploadVideoRequest, UploadAssetResult uploadResult)
{
    asset = CreateAsset(uploadVideoRequest);
    uploadResult.VideoName = uploadVideoRequest.VideoName;
    // Encoding the asset , Moderating the asset, Generating transcript in parallel
    IAsset encodedAsset = null;
    //Creates the job for the tasks.
    IJob job = this._mediaContext.Jobs.Create("AMS Review Job");

    //Adding encoding task to job.
    ConfigureEncodeAssetTask(uploadVideoRequest.EncodingRequest, job);

    ConfigureContentModerationTask(job);

    //adding transcript task to job.
    if (uploadResult.GenerateVTT)
    {
        ConfigureTranscriptTask(job);
    }

    var watch = System.Diagnostics.Stopwatch.StartNew();
    //submit and execute job.
    job.Submit();
    job.GetExecutionProgressTask(new CancellationTokenSource().Token).Wait();
    watch.Stop();
    Logger.Log($"AMS Job Elapsed Time: {watch.Elapsed}");

    if (job.State == JobState.Error)
    {
        throw new Exception("Video moderation has failed due to AMS Job error.");
    }

    UploadAssetResult result = uploadResult;
    encodedAsset = job.OutputMediaAssets[0];
    result.ModeratedJson = GetCmDetail(job.OutputMediaAssets[1]);
    // Check for valid Moderated JSON
    var jsonModerateObject = JsonConvert.DeserializeObject<VideoModerationResult>(result.ModeratedJson);

    if (jsonModerateObject == null)
    {
        return false;
    }
    if (uploadResult.GenerateVTT)
    {
        GenerateTranscript(job.OutputMediaAssets.Last());
    }

    uploadResult.StreamingUrlDetails = PublishAsset(encodedAsset);
    string downloadUrl = GenerateDownloadUrl(asset, uploadVideoRequest.VideoName);
    uploadResult.StreamingUrlDetails.DownloadUri = downloadUrl;
    uploadResult.VideoName = uploadVideoRequest.VideoName;
    uploadResult.VideoFilePath = uploadVideoRequest.VideoFilePath;
    return true;
}

Este código realiza as seguintes tarefas:This code performs the following tasks:

  • Cria uma tarefa dos Serviços de Multimédia do Azure para concluir o processamentoCreates an AMS job for the processing to be done
  • Adiciona tarefas para codificar o ficheiro de vídeo, para o moderar e para gerar uma transcrição textoAdds tasks for encoding the video file, moderating it, and generating a text transcript
  • Submete a tarefa ao carregar o ficheiro e iniciar o processamentoSubmits the job, uploading the file and beginning processing
  • Obtém os resultados da moderação, a transcrição de texto (se for pedida) e outras informaçõesRetrieves the moderation results, the text transcript (if requested), and other information

Saída de moderação de vídeo de amostraSample video moderation output

O resultado da tarefa de moderação de vídeos (veja o guia de início rápido da moderação de vídeos) é uma estrutura de dados JSON que contém os resultados de moderação.The result of the video moderation job (See video moderation quickstart is a JSON data structure containing the moderation results. Estes resultados incluem uma discriminação dos fragmentos (imagens) que compõem o vídeo, cada um dos quais com eventos (clipes) que têm fotogramas chave que foram sinalizados para revisão.These results include a breakdown of the fragments (shots) within the video, each containing events (clips) with key frames that have been flagged for review. Cada fotograma chave é classificado em função da probabilidade de conter conteúdos inadequados ou para adultos.Each key frame is scored by the likelihood that it contains adult or racy content. O seguinte exemplo mostra uma resposta JSON:The following example shows a JSON response:

{
    "version": 2,
    "timescale": 90000,
    "offset": 0,
    "framerate": 50,
    "width": 1280,
    "height": 720,
    "totalDuration": 18696321,
    "fragments": [
    {
        "start": 0,
        "duration": 18000
    },
    {
        "start": 18000,
        "duration": 3600,
        "interval": 3600,
        "events": [
        [
        {
            "reviewRecommended": false,
            "adultScore": 0.00001,
            "racyScore": 0.03077,
            "index": 5,
            "timestamp": 18000,
            "shotIndex": 0
        }
        ]
    ]
    },
    {
        "start": 18386372,
        "duration": 119149,
        "interval": 119149,
        "events": [
        [
        {
            "reviewRecommended": true,
            "adultScore": 0.00000,
            "racyScore": 0.91902,
            "index": 5085,
            "timestamp": 18386372,
            "shotIndex": 62
        }
    ]
    ]
    }
]
}

Também é produzida uma transcrição de áudio a partir do vídeo quando o sinalizador GenerateVTT é definido.A transcription of the audio from the video is also produced when the GenerateVTT flag is set.

Nota

A aplicação de consola utiliza a API do Indexer Azure Media para gerar transcrições a partir da faixa de áudio do vídeo carregado. Os resultados são fornecidos em formato WebVTT.The console application uses the Azure Media Indexer API to generate transcripts from the uploaded video's audio track. The results are provided in WebVTT format. Para obter mais informações sobre este formato, veja Formato de Faixas de Texto de Vídeo Web.For more information on this format, see Web Video Text Tracks Format.

Criar uma revisão humanaCreate a human review

O processo de moderação devolve uma lista de fotogramas chave do vídeo, juntamente com uma transcrição das respetivas faixas de áudio.The moderation process returns a list of key frames from the video, along with a transcript of its audio tracks. O próximo passo é criar uma revisão na ferramenta De revisão de moderadores de conteúdo para moderadores humanos.The next step is to create a review in the Content Moderator Review tool for human moderators. Se regressar ao método ProcessVideo() em Program.cs, verá a chamada para o método CreateVideoReviewInContentModerator().Going back to the ProcessVideo() method in Program.cs, you see the call to the CreateVideoReviewInContentModerator() method. Este método está na classe videoReviewApi, que está em VideoReviewAPI.cs e é apresentada aqui.This method is in the videoReviewApi class, which is in VideoReviewAPI.cs, and is shown here.

public async Task<string> CreateVideoReviewInContentModerator(UploadAssetResult uploadAssetResult)
{
    string reviewId = string.Empty;
    List<ProcessedFrameDetails> frameEntityList = framegenerator.CreateVideoFrames(uploadAssetResult);
    string path = uploadAssetResult.GenerateVTT == true ? this._amsConfig.FfmpegFramesOutputPath + Path.GetFileNameWithoutExtension(uploadAssetResult.VideoName) + "_aud_SpReco.vtt" : "";
    TranscriptScreenTextResult screenTextResult = new TranscriptScreenTextResult();
    if (File.Exists(path))
    {
        screenTextResult = await GenerateTextScreenProfanity(reviewId, path, frameEntityList);
        uploadAssetResult.Category1TextScore = screenTextResult.Category1Score;
        uploadAssetResult.Category2TextScore = screenTextResult.Category2Score;
        uploadAssetResult.Category3TextScore = screenTextResult.Category3Score;
        uploadAssetResult.Category1TextTag = screenTextResult.Category1Tag;
        uploadAssetResult.Category2TextTag = screenTextResult.Category2Tag;
        uploadAssetResult.Category3TextTag = screenTextResult.Category3Tag;
    }
    var reviewVideoRequestJson = CreateReviewRequestObject(uploadAssetResult, frameEntityList);
    if (string.IsNullOrWhiteSpace(reviewVideoRequestJson))
    {
        throw new Exception("Video review process failed in CreateVideoReviewInContentModerator");
    }
    var reviewIds = await ExecuteCreateReviewApi(reviewVideoRequestJson);
    reviewId = reviewIds.FirstOrDefault();
    frameEntityList = framegenerator.GenerateFrameImages(frameEntityList, uploadAssetResult, reviewId);
    await CreateAndPublishReviewInContentModerator(uploadAssetResult, frameEntityList, reviewId, path, screenTextResult);

    return reviewId;
}

CreateVideoReviewInContentModerator() chama vários outros métodos para realizar as seguintes tarefas:CreateVideoReviewInContentModerator() calls several other methods to perform the following tasks:

Nota

A aplicação de consola utiliza a biblioteca FFmpeg para gerar miniaturas.The console application uses the FFmpeg library for generating thumbnails. Estas miniaturas (imagens) correspondem aos carimbos de data/hora dos fotogramas na saída de moderação de vídeos.These thumbnails (images) correspond to the frame timestamps in the video moderation output.

TarefaTask MétodosMethods FicheiroFile
Extrair os fotogramas chave do vídeo e criar imagens em miniatura dos mesmosExtract the key frames from the video and creates thumbnail images of them CreateVideoFrames()
GenerateFrameImages()
FrameGeneratorServices.cs
Analisar a transcrição de texto (se esta estiver disponível) para localizar áudio inadequado ou para adultosScan the text transcript, if available, to locate adult or racy audio GenerateTextScreenProfanity() VideoReviewAPI.cs
Preparar e submeter um pedido de revisão de vídeo para efeitos de inspeção manualPrepare and submits a video review request for human inspection CreateReviewRequestObject()
ExecuteCreateReviewApi()
CreateAndPublishReviewInContentModerator()
VideoReviewAPI.cs

O seguinte ecrã mostra os resultados dos passos anteriores.The following screen shows the results of the previous steps.

Vista predefinida de revisão de vídeo

Processar a transcriçãoProcess the transcript

Até agora, o código apresentado neste tutorial incidiu nos conteúdos visuais.Until now, the code presented in this tutorial has focused on the visual content. A revisão dos conteúdos de voz é um processo separado e opcional que, conforme mencionado anteriormente, utiliza uma transcrição gerada a partir do áudio.Review of speech content is a separate and optional process that, as mentioned, uses a transcript generated from the audio. Agora é altura de ver como é que as transcrições de texto são criadas e utilizadas no processo de revisão.It's time now to take a look at how text transcripts are created and used in the review process. É o serviço do Azure Media Indexer que realiza a tarefa de geração da transcrição.The task of generating the transcript falls to the Azure Media Indexer service.

A aplicação realiza as seguintes tarefas:The application performs the following tasks:

TarefaTask MétodosMethods FicheiroFile
Determinar se serão geradas transcrições de textoDetermine whether text transcripts are to be generated Main()
GetUserInputs()
Program.cs
Se forem, submeter uma tarefa de transcrição como parte da moderaçãoIf so, submit a transcription job as part of moderation ConfigureTranscriptTask() VideoModerator.cs
Obter uma cópia local da transcriçãoGet a local copy of the transcript GenerateTranscript() VideoModerator.cs
Sinalizar fotogramas do vídeo que contenham áudio inadequadoFlag frames of the video that contain inappropriate audio GenerateTextScreenProfanity()
TextScreen()
VideoReviewAPI.cs
Adicionar os resultados à revisãoAdd the results to the review UploadScreenTextResult()
ExecuteAddTranscriptSupportFile()
VideoReviewAPI.cs

Configuração das tarefasTask configuration

Vamos avançar para a submissão da tarefa de transcrição.Let's jump right into submitting the transcription job. CreateAzureMediaServicesJobToModerateVideo() (descrito anteriormente) chama ConfigureTranscriptTask().CreateAzureMediaServicesJobToModerateVideo() (already described) calls ConfigureTranscriptTask().

private void ConfigureTranscriptTask(IJob job)
{
    string mediaProcessorName = _amsConfigurations.MediaIndexer2MediaProcessor;
    IMediaProcessor processor = _mediaContext.MediaProcessors.GetLatestMediaProcessorByName(mediaProcessorName);

    string configuration = File.ReadAllText(_amsConfigurations.MediaIndexerConfigurationJson);
    ITask task = job.Tasks.AddNew("AudioIndexing Task", processor, configuration, TaskOptions.None);
    task.InputAssets.Add(asset);
    task.OutputAssets.AddNew("AudioIndexing Output Asset", AssetCreationOptions.None);
}

A configuração da tarefa de transcrição é lida a partir do ficheiro MediaIndexerConfig.json que se encontra na pasta Lib da solução.The configuration for the transcript task is read from the file MediaIndexerConfig.json in the solution's Lib folder. Os elementos dos Serviços de Multimédia do Azure são criados para o ficheiro de configuração e para a saída do processo de transcrição.AMS assets are created for the configuration file and for the output of the transcription process. Quando a tarefa dos Serviços de Multimédia do Azure é executada, cria uma transcrição de texto a partir da faixa de áudio do ficheiro de vídeo.When the AMS job runs, this task creates a text transcript from the video file's audio track.

Nota

O exemplo de aplicação só reconhece voz em inglês dos E.U.A.The sample application recognizes speech in US English only.

Geração de transcriçõesTranscript generation

A transcrição é publicada sob a forma de elemento dos Serviços de Multimédia do Azure.The transcript is published as an AMS asset. Para analisar a transcrição de forma a identificar eventuais conteúdos ofensivos, a aplicação transfere o elemento a partir dos Serviços de Multimédia do Azure.To scan the transcript for objectionable content, the application downloads the asset from Azure Media Services. CreateAzureMediaServicesJobToModerateVideo() chama GenerateTranscript(), conforme mostrado aqui, para obter o ficheiro.CreateAzureMediaServicesJobToModerateVideo() calls GenerateTranscript(), shown here, to retrieve the file.

public bool GenerateTranscript(IAsset asset)
{
    try
    {
        var outputFolder = this._amsConfigurations.FfmpegFramesOutputPath;
        IAsset outputAsset = asset;
        IAccessPolicy policy = null;
        ILocator locator = null;
        policy = _mediaContext.AccessPolicies.Create("My 30 days readonly policy", TimeSpan.FromDays(360), AccessPermissions.Read);
        locator = _mediaContext.Locators.CreateLocator(LocatorType.Sas, outputAsset, policy, DateTime.UtcNow.AddMinutes(-5));
        DownloadAssetToLocal(outputAsset, outputFolder);
        locator.Delete();
        return true;
    }
    catch
    {   //TODO:  Logging
        Console.WriteLine("Exception occured while generating index for video.");
        throw;
    }
}

Após alguma configuração necessária dos Serviços de Multimédia do Azure, a transferência efetiva é realizada ao chamar DownloadAssetToLocal(), uma função genérica que copia um elemento dos AMS para um ficheiro local.After some necessary AMS setup, the actual download is performed by calling DownloadAssetToLocal(), a generic function that copies an AMS asset to a local file.

Moderar a transcriçãoModerate the transcript

Com a transcrição à disposição, esta é analisada e utilizada na revisão.With the transcript close at hand, it is scanned and used in the review. Criar a revisão é da competência de CreateVideoReviewInContentModerator(), que chama GenerateTextScreenProfanity() para realizar a tarefa.Creating the review is the purview of CreateVideoReviewInContentModerator(), that calls GenerateTextScreenProfanity() to do the job. Este método, por sua vez, chama TextScreen(), que contém a maior parte da funcionalidade.In turn, this method calls TextScreen(), that contains most of the functionality.

TextScreen() realiza as seguintes tarefas:TextScreen() performs the following tasks:

  • Analisar a transcrição para identificar carimbos de data/hora e legendasParse the transcript for time tamps and captions
  • Submeter todas a legendas para efeitos de moderação de textosSubmit each caption for text moderation
  • Sinalizar os fotogramas com conteúdos de voz ofensivosFlag any frames that may have objectionable speech content

Vamos agora examinar cada uma destas tarefas de forma mais detalhada:Let's examine each these tasks in more detail:

Inicializar o códigoInitialize the code

Em primeiro lugar, deve inicializar todas as variáveis e coleções.First, initialize all variables and collections.

private async Task<TranscriptScreenTextResult> TextScreen(string filepath, List<ProcessedFrameDetails> frameEntityList)
{
    List<TranscriptProfanity> profanityList = new List<TranscriptProfanity>();
    bool category1Tag = false;
    bool category2Tag = false;
    bool category3Tag = false;
    double category1Score = 0;
    double category2Score = 0;
    double category3Score = 0;
    List<string> vttLines = File.ReadAllLines(filepath).Where(line => !line.Contains("NOTE Confidence:") && line.Length > 0).ToList();
    StringBuilder sb = new StringBuilder();
    List<CaptionScreentextResult> csrList = new List<CaptionScreentextResult>();
    CaptionScreentextResult captionScreentextResult = new CaptionScreentextResult() { Captions = new List<string>() };

Analisar a transcrição para identificar legendasParse the transcript for captions

Em seguida, deve analisar a transcrição em formato VTT para identificar legendas e carimbos de data/hora.Next, parse the VTT formatted transcript for captions and timestamps. A ferramenta Review apresenta estas legendas no Separador Transcrição no ecrã de revisão de vídeo.The Review tool displays these captions in the Transcript Tab on the video review screen. Os carimbos de data/hora são utilizados para sincronizar as legendas com os fotogramas do vídeo correspondentes.The timestamps are used to sync the captions with the corresponding video frames.

foreach (var line in vttLines.Skip(1))
{
    if (line.Contains("-->"))
    {
        if (sb.Length > 0)
        {
            captionScreentextResult.Captions.Add(sb.ToString());
            sb.Clear();
        }
        if (captionScreentextResult.Captions.Count > 0)
        {
            csrList.Add(captionScreentextResult);
            captionScreentextResult = new CaptionScreentextResult() { Captions = new List<string>() };
        }
        string[] times = line.Split(new string[] { "-->" }, StringSplitOptions.RemoveEmptyEntries);
        string startTimeString = times[0].Trim();
        string endTimeString = times[1].Trim();
        int startTime = (int)TimeSpan.ParseExact(startTimeString, @"hh\:mm\:ss\.fff", CultureInfo.InvariantCulture).TotalMilliseconds;
        int endTime = (int)TimeSpan.ParseExact(endTimeString, @"hh\:mm\:ss\.fff", CultureInfo.InvariantCulture).TotalMilliseconds;
        captionScreentextResult.StartTime = startTime;
        captionScreentextResult.EndTime = endTime;
    }
    else
    {
        sb.Append(line);
    }
    if (sb.Length + line.Length > 1024)
    {
        captionScreentextResult.Captions.Add(sb.ToString());
        sb.Clear();
    }
}
if (sb.Length > 0)
{
    captionScreentextResult.Captions.Add(sb.ToString());
}
if (captionScreentextResult.Captions.Count > 0)
{
    csrList.Add(captionScreentextResult);
}

Moderar legendas através do serviço de moderação de textosModerate captions with the text moderation service

Em seguida, é necessário analisar as legendas de texto analisadas com recurso à API de texto do Content Moderator.Next, we scan the parsed text captions with Content Moderator's text API.

Nota

A chave do serviço do Content Moderator tem um limite de velocidade de pedidos por segundo (RPS).Your Content Moderator service key has a requests per second (RPS) rate limit. Se ultrapassar o limite, o SDK emite uma exceção com o código de erro 429.If you exceed the limit, the SDK throws an exception with a 429 error code.

Uma chave de escalão gratuito tem um limite de velocidade de um RPS.A free tier key has a one RPS rate limit.

    int waitTime = 1000;
    foreach (var csr in csrList)
    {
        bool captionAdultTextTag = false;
        bool captionRacyTextTag = false;
        bool captionOffensiveTextTag = false;
        Screen screenRes = new Screen();
        bool retry = true;

        foreach (var caption in csr.Captions)
        {
            while (retry)
            {
                try
                {
                    System.Threading.Thread.Sleep(waitTime);
                    var lang = await CMClient.TextModeration.DetectLanguageAsync("text/plain", caption);
                    var res = await CMClient.TextModeration.ScreenTextWithHttpMessagesAsync(lang.DetectedLanguageProperty, caption, string.Empty, null, null, null, true);
                    screenRes = res.Body;
                    retry = false;
                }
                catch (Exception e)
                {
                    if (e.Message.Contains("429"))
                    {
                        Console.WriteLine($"Moderation API call failed. Message: {e.Message}");
                        waitTime = (int)(waitTime * 1.5);
                        Console.WriteLine($"wait time: {waitTime}");
                    }
                    else
                    {
                        retry = false;
                        Console.WriteLine($"Moderation API call failed. Message: {e.Message}");
                    }
                }
            }
             
            if (screenRes != null)
            {
                TranscriptProfanity transcriptProfanity = new TranscriptProfanity();
                transcriptProfanity.TimeStamp = "";
                List<Terms> transcriptTerm = new List<Terms>();
                if (screenRes.Terms != null)
                {
                    foreach (var term in screenRes.Terms)
                    {
                        var profanityobject = new Terms
                        {
                            Term = term.Term,
                            Index = term.Index.Value
                        };
                        transcriptTerm.Add(profanityobject);
                    }
                    transcriptProfanity.Terms = transcriptTerm;
                    profanityList.Add(transcriptProfanity);
                }
                if (screenRes.Classification.Category1.Score.Value > _amsConfig.Category1TextThreshold) captionAdultTextTag = true;
                if (screenRes.Classification.Category2.Score.Value > _amsConfig.Category2TextThreshold) captionRacyTextTag = true;
                if (screenRes.Classification.Category3.Score.Value > _amsConfig.Category3TextThreshold) captionOffensiveTextTag = true;
                if (screenRes.Classification.Category1.Score.Value > _amsConfig.Category1TextThreshold) category1Tag = true;
                if (screenRes.Classification.Category2.Score.Value > _amsConfig.Category2TextThreshold) category2Tag = true;
                if (screenRes.Classification.Category3.Score.Value > _amsConfig.Category3TextThreshold) category3Tag = true;
                category1Score = screenRes.Classification.Category1.Score.Value > category1Score ? screenRes.Classification.Category1.Score.Value : category1Score;
                category2Score = screenRes.Classification.Category2.Score.Value > category2Score ? screenRes.Classification.Category2.Score.Value : category2Score;
                category3Score = screenRes.Classification.Category3.Score.Value > category3Score ? screenRes.Classification.Category3.Score.Value : category3Score;
            }
            foreach (var frame in frameEntityList.Where(x => x.TimeStamp >= csr.StartTime && x.TimeStamp <= csr.EndTime))
            {
                frame.IsAdultTextContent = captionAdultTextTag;
                frame.IsRacyTextContent = captionRacyTextTag;
                frame.IsOffensiveTextContent = captionOffensiveTextTag;
            }
        }
    }
    TranscriptScreenTextResult screenTextResult = new TranscriptScreenTextResult()
    {
        TranscriptProfanity = profanityList,
        Category1Tag = category1Tag,
        Category2Tag = category2Tag,
        Category3Tag = category3Tag,
        Category1Score = category1Score,
        Category2Score = category2Score,
        Category3Score = category3Score
    };
    return screenTextResult;
}

Desagregação da moderação do textoText moderation breakdown

TextScreen() é um método substancial, por isso vamos analisá-lo por partes.TextScreen() is a substantial method, so let's break it down.

  1. Em primeiro lugar, o método lê o ficheiro de transcrição linha por linha.First, the method reads the transcript file line by line. O método ignora linhas em branco e as linhas que contêm uma NOTE com uma pontuação de confiança.It ignores blank lines and lines containing a NOTE with a confidence score. Extrai os carimbos de data/hora e os itens de texto a partir de pistas que constam no ficheiro.It extracts the time stamps and text items from the cues in the file. As pistas representam o texto da faixa de áudio e incluem horas de início e fim.A cue represents text from the audio track and includes start and end times. As pistas começam com a linha do carimbo de data/hora com a cadeia de caracteres -->.A cue begins with the time stamp line with the string -->. A isto segue-se uma ou mais linhas de texto.It is followed by one or more lines of text.

  2. As instâncias de CaptionScreentextResult (definido em TranscriptProfanity.cs) são utilizadas para armazenar as informações analisadas de cada pista.Instances of CaptionScreentextResult (defined in TranscriptProfanity.cs) are used to hold the information parsed from each cue. Quando se deteta uma nova linha de carimbo de data/hora ou se atinge um comprimento do texto máximo de 1024 carateres, é adicionado um novo CaptionScreentextResult a csrList.When a new time stamp line is detected, or a maximum text length of 1024 characters is reached, a new CaptionScreentextResult is added to the csrList.

  3. Em seguida, o método submete cada pista à API de Moderação de Textos.The method next submits each cue to the Text Moderation API. O método chama ContentModeratorClient.TextModeration.DetectLanguageAsync() e ContentModeratorClient.TextModeration.ScreenTextWithHttpMessagesAsync(), que são definidas na assemblagem Microsoft.Azure.CognitiveServices.ContentModerator.It calls both ContentModeratorClient.TextModeration.DetectLanguageAsync() and ContentModeratorClient.TextModeration.ScreenTextWithHttpMessagesAsync(), which are defined in the Microsoft.Azure.CognitiveServices.ContentModerator assembly. Para evitar o limite de velocidade, o método faz uma pausa durante um segundo antes de submeter cada pista.To avoid being rate-limited, the method pauses for a second before submitting each cue.

  4. Após receber os resultados do serviço de Moderação de Textos, o método analisa-os para verificar se cumprem limiares de confiança.After receiving results from the Text Moderation service, the method then analyzes them to see whether they meet confidence thresholds. Estes valores são estabelecidos em App.config como OffensiveTextThreshold, RacyTextThreshold e AdultTextThreshold.These values are established in App.config as OffensiveTextThreshold, RacyTextThreshold, and AdultTextThreshold. Por fim, os termos ofensivos também são armazenados.Finally, the objectionable terms themselves are also stored. Todos os fotogramas que se encontram dentro do intervalo de tempo da pista são sinalizados como contendo texto ofensivo, inadequado e/ou para adultos.All frames within the cue's time range are flagged as containing offensive, racy, and/or adult text.

  5. TextScreen() devolve uma instância TranscriptScreenTextResult que contém o resultado da moderação do texto do vídeo como um todo.TextScreen() returns a TranscriptScreenTextResult instance that contains the text moderation result from the video as a whole. Este objeto inclui sinalizadores e pontuações para os vários tipos de conteúdos ofensivos, juntamente com uma lista de todos os termos ofensivos.This object includes flags and scores for the various types of objectionable content, along with a list of all objectionable terms. O chamador, CreateVideoReviewInContentModerator(), chama UploadScreenTextResult() para anexar estas informações à revisão de modo a estarem disponível aos revisores humanos.The caller, CreateVideoReviewInContentModerator(), calls UploadScreenTextResult() to attach this information to the review so it is available to human reviewers.

O seguinte ecrã mostra o resultado dos passos de geração de transcrições e de moderação.The following screen shows the result of the transcript generation and moderation steps.

Vista de transcrição da moderação de vídeos

Saída do programaProgram output

A seguinte saída da linha de comandos do programa mostra as diversas tarefas à medida que são concluídas.The following command-line output from the program shows the various tasks as they are completed. Além disso, o resultado da moderação (em formato JSON) e a transcrição de voz estão disponíveis no mesmo diretório dos ficheiros de vídeo originais.Additionally, the moderation result (in JSON format) and the speech transcript are available in the same directory as the original video files.

Microsoft.ContentModerator.AMSComponentClient
Enter the fully qualified local path for Uploading the video :
"Your File Name.MP4"
Generate Video Transcript? [y/n] : y

Video compression process started...
Video compression process completed...

Video moderation process started...
Video moderation process completed...

Video review process started...
Video Frames Creation inprogress...
Frames(83) created successfully.
Review Created Successfully and the review Id 201801va8ec2108d6e043229ba7a9e6373edec5
Video review successfully completed...

Total Elapsed Time: 00:05:56.8420355

Passos seguintesNext steps

Neste tutorial, configura uma aplicação que—modera o—conteúdo de vídeo, incluindo conteúdo de transcrição e cria comentários na ferramenta Review.In this tutorial, you set up an application that moderates video content—including transcript content—and creates reviews in the Review tool. Em seguida, saiba mais sobre os detalhes da moderação do vídeo.Next, learn more about the details of video moderation.