zoukankan      html  css  js  c++  java
  • 利用 Dolby® Digital Plus 提供优质音频体验

    John Deutscher Azure媒体服务首席项目经理

    随着媒体设备的增多,一项日益增长的需求是,视频流服务能够向用户提供超高音频质量和具有 5.1 环绕音响的优质内容。通过 Azure媒体服务使用 Dolby® Digital Plus多声道环绕音响给您的高清内容进行编码,现可交付到多个平台上,包括智能电视、XboxWindows 8 设备、移动设备等。

    Dolby®Digital Plus Enhanced AC-3(E-AC-3)是一种专为高质量音频而设计的高级环绕音响音频编解码器。此编解码器基于核心 Dolby Digital 技术,是用于影院、广播和家庭影院环绕音响的一种成熟标准,超过 21亿款产品均支持该技术。在本博文中,我将介绍如何通过媒体服务使用此编解码器使您的内容提供优质的音频体验。

    概述

    在此示例中,编码到 Dolby® Digital Plus 涉及下列步骤:

    1.    将源内容上传到您的 Azure媒体服务帐户并创建一个资源

    2.    构建自定义编码预设,并将其保存到文件中

    3.    提交使用自定义预设对上述资源进行编码的任务

    4.    发布输出资源,并创建一个 SAS URL

    5.    在应用程序中演示播放

    开始之前,让我描述下每个步骤并提供我使用的示例代码。

    运行示例代码后,您会获得一个标准 MP4 文件的 URL,文件包含 H.264 视频和 Dolby Digital Plus音频。接下来,您可以使用此 URL在支持 Dolby Digital Plus解码的设备上播放此视频流。Windows 8 Xbox都有内置的 Dolby Digital Plus解码器,但 Apple设备目前不内置支持此编解码器。我的建议是遵循关于如何使用 Windows 8 Player Framework测试环绕音响解码的指南。如果您想要在充分环绕音响下播放文件,则需要将 PC连接到能够进行 5.1播放的 AV接收器

    上传源内容

    开始之前,您需要首先将一些包含高清视频和多声道音频的源内容上传到您的媒体服务帐户。建议使用下列文件格式

    • MPEG-2传输流,使用 AC-3(也称为 Dolby® Digital)编码的 5.1音频流

    • ISO MPEG-4 (MP4)文件,包含使用 AAC编码的 5.1音频

    • WMV文件,包含使用 WMA Professional编码的 5.1音频

    参考CreateAssetAndUploadSingleFile()中的上传源文件的示例代码。

    构建自定义预设

    如果这是您第一次创建自定义预设,我鼓励您阅读我之前的文章高级编码功能,了解关于如何创建自定义预设的详细信息。

    接下来,我将介绍一种自定义预设,可将您的源转码为 MP4 文件,此 MP4文件包含以 4.5 Mbps传输率编码的 720p视频以及传输率为 512 kbps 5.1声道 Dolby Digital Plus音频。此自定义预设基于H264宽带 720p预设改编。此预设的<AudioProfile>部分经修改使用Dolby Digital Plus设置

    注意:有关如何调整音频编码设置(如此示例中,使用低于 512kbps的比特率)的详细信息,请参阅http://msdn.microsoft.com/zh-cn/library/dn296500.aspx

    完成的 XML自定义预设如下,且应保存到本地文件中以用于编码。我使用的名称为“Dolby Audio Preset.xml”

    <?xml version="1.0"encoding="utf-16"?>

    <Presets>

     <Preset

      Version="5.0">

      <MediaFile

        DeinterlaceMode="AutoPixelAdaptive"

        ResizeQuality="Super"

        VideoResizeMode="Stretch">

        <OutputFormat>

          <MP4OutputFormat

            StreamCompatibility="Standard">

            <VideoProfile>

              <MainH264VideoProfile

                BFrameCount="3"

                EntropyMode="Cabac"

                RDOptimizationMode="Speed"

                HadamardTransform="False"

                SubBlockMotionSearchMode="Speed"

                MultiReferenceMotionSearchMode="Balanced"

                ReferenceBFrames="False"

                AdaptiveBFrames="True"

                SceneChangeDetector="True"

                FastIntraDecisions="False"

                FastInterDecisions="False"

                SubPixelMode="Quarter"

                SliceCount="0"

                KeyFrameDistance="00:00:02"

                InLoopFilter="True"

                MEPartitionLevel="EightByEight"

                ReferenceFrames="4"

                SearchRange="64"

                AutoFit="True"

                Force16Pixels="False"

                FrameRate="0"

                SeparateFilesPerStream="True"

                SmoothStreaming="False"

                NumberOfEncoderThreads="0">

                <Streams

                  AutoSize="False"

                  FreezeSort="False">

                  <StreamInfo

                    Size="1280, 720">

                    <Bitrate>

                      <ConstantBitrate

                        Bitrate="4500"

                        IsTwoPass="False"

                        BufferWindow="00:00:05" />

                    </Bitrate>

                  </StreamInfo>

                </Streams>

              </MainH264VideoProfile>

            </VideoProfile>

             <AudioProfile>

               <DolbyDigitalPlusAudioProfile

                 Codec="DolbyDigitalPlus"

                 EncoderMode="DolbyDigitalPlus"

                 AudioCodingMode="Mode32"

                 LFEOn="True"

                 SamplesPerSecond="48000"

                 BandwidthLimitingLowpassFilter="True"

                 DialogNormalization="-31">

                 <Bitrate>

                   <ConstantBitrate

                     Bitrate="512"

                     IsTwoPass="False"

                     BufferWindow="00:00:00" />

                 </Bitrate>

               </DolbyDigitalPlusAudioProfile>

     

             </AudioProfile> 

     

          </MP4OutputFormat>

        </OutputFormat>

      </MediaFile>

     </Preset>

    </Presets>

    转码您的源内容

    您可以使用 Windows Azure MediaEncoder 转码您的源内容,将自定义预设内容作为配置字符串传输到任务。查看下文示例代码中的 Transcode() 方法,了解涉及的步骤。任务产生的输出资产包含一个 MP4文件,文件中包含与 H.264编码的视频交错的 Dolby Digital Plus音频。

    发布输出资

    内容转码之后,您就能为输出资源创建 SAS 定位。查看下文示例代码中的 CreateSASLocator(),了解涉及的步骤。SAS URI 可传递到您的播放器应用程序中。

    示例代码

    请注意,本主题中的代码使用Azure媒体服务 .NET SDK扩展。媒体服务 .NET SDK 扩展是一组扩展方法和帮助功能,可简化您的代码,以简化利用媒体服务进行的开发。

    示例代码的 App.Config 文件如下所示

    <?xmlversion="1.0" encoding="utf-8"?>

    <configuration>

     <startup>

       <supportedRuntimeversion="v4.0" sku=".NETFramework,Version=v4.5" />

     </startup>

     <appSettings>

       <addkey="MediaServicesAccountName"value="<MediaAccountName>" />

       <add key="MediaServicesAccountKey"value="<MediaAccountKey>" />

     </appSettings>

     <runtime>

       <assemblyBindingxmlns="urn:schemas-microsoft-com:asm.v1">

         <dependentAssembly>

           <assemblyIdentityname="Microsoft.WindowsAzure.Storage"publicKeyToken="31bf3856ad364e35" culture="neutral" />

           <bindingRedirectoldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />

         </dependentAssembly>

       </assemblyBinding>

     </runtime>

    在上述 App.Config 中,将<MediaAccountName><MediaAccountKey>替换为您的媒体服务帐户名称和密钥

    此外,我采用了 Dolby 提供的动画短片“Silent”的以下示例 5.1 环绕音响文件。
    您可以在此处下载源 MP4 文件以供您的测试使用。

    ©Dolby – Silent Dolby提供)

    采用的示例代码如下所示。

    usingSystem;

    usingSystem.Linq;

    using System.Configuration;

    usingSystem.IO;

    usingSystem.Text;

    usingSystem.Threading;

    usingSystem.Threading.Tasks;

    usingSystem.Collections.Generic;

    usingSystem.Diagnostics;

    usingSystem.Globalization;

    usingMicrosoft.WindowsAzure;

    using Microsoft.WindowsAzure.MediaServices.Client;

     

    namespaceDeliveringPremiumAudio

    {

       /// <summary>

       ///

       /// </summary>

       class Program

       {

           // App.config文件中读取值。

           private static readonly string_mediaServicesAccountName =

               ConfigurationManager.AppSettings["MediaServicesAccountName"];

           private static readonly string_mediaServicesAccountKey =

               ConfigurationManager.AppSettings["MediaServicesAccountKey"];

           private static readonly string_storageConnectionString =

               ConfigurationManager.AppSettings["StorageConnectionString"];

     

           private static CloudMediaContext_context = null;

           private static MediaServicesCredentials_cachedCredentials = null;

     

           //指向示例文件的指针,以及保存的自定义预设 XML

           private static readonly string _sampleFile =@"C: empsintel.wmv";

           private static readonly string_customPreset = @"C: empDolby Audio Preset.xml";

     

           static void Main(string[] args)

           {

               try

               {

                   //在静态类变量中创建和缓存媒体服务凭据

                   _cachedCredentials =new MediaServicesCredentials(_mediaServicesAccountName,_mediaServicesAccountKey);

     

                   // 使用缓存的凭据创建 CloudMediaContext

                   _context = newCloudMediaContext(_cachedCredentials);

     

                   // 步骤 1.上传示例内容并创建资

                   IAsset inputAsset =CreateAssetAndUploadSingleFile(AssetCreationOptions.None, _sampleFile);

     

                   // 步骤 2.将自定义预设加载到配置字符串中

                   string configuration =File.ReadAllText(_customPreset);

     

                   // 步骤 3.将输入转码

                   IAsset outputAsset =Transcode(inputAsset, configuration);

     

                   // 步骤 4.为输出资创建 SAS 定位并打印到控制台

                   if (null != outputAsset)CreateSASLocator(outputAsset);

     

                   // 上述方法创建的定位符有效期为 30

                   // 测试完成后,您应考虑删除定位

               }

               catch (Exception ex)

               {

                   Console.WriteLine(ex.Message);

               }

           }

     

           ///<summary>

           /// 此功能将创建一个空资源

           /// </summary>

           static private IAssetCreateEmptyAsset(string assetName, AssetCreationOptions assetCreationOptions)

           {

               var asset =_context.Assets.Create(assetName, assetCreationOptions);

               Console.WriteLine("Assetname:" + asset.Name);

               Console.WriteLine("Timecreated:" + asset.Created.Date.ToString());

     

               return asset;

           }

     

           /// <summary>

           /// 此功能创建了一个资,并将输入文件上传至其中

           /// </summary>

           static public IAssetCreateAssetAndUploadSingleFile(AssetCreationOptions assetCreationOptions,string singleFilePath)

           {

               var fileName =Path.GetFileName(singleFilePath);

               // 创建唯一的资名称

               var assetName = fileName +DateTime.UtcNow.ToString();

               var asset =CreateEmptyAsset(assetName, assetCreationOptions);

     

               var assetFile =asset.AssetFiles.Create(fileName);

               Console.WriteLine("CreatedassetFile {0}", assetFile.Name);

     

               //为上传文件,我们需要具有适当访问政策的定位

               var accessPolicy = _context.AccessPolicies.Create(assetName,TimeSpan.FromDays(30),

                                                                   AccessPermissions.Write | AccessPermissions.List);

               var locator =_context.Locators.CreateLocator(LocatorType.Sas, asset, accessPolicy);

     

               assetFile.Upload(singleFilePath);

               Console.WriteLine("Doneuploading {0}", assetFile.Name);

               Console.WriteLine("");

               long size =assetFile.ContentFileSize;

     

               locator.Delete();

               accessPolicy.Delete();

     

               return asset;

           }

     

           ///<summary>

           /// 此功能使用提供的预设将源资源转码,并返回输出资源

           /// </summary>

           static public IAsset Transcode(IAssetsourceAsset, string preset)

           {

               // 申报新作业。

               IJob job = _context.Jobs.Create("TranscodingJob for " + sourceAsset.Name);

               // 获取 Windows Azure MediaEncoder参考,向其传递

               // 此特定任务使用的处理器名称。

               IMediaProcessor processor =GetLatestMediaProcessorByName("Windows Azure Media Encoder");

     

               //使用字符串预设创建一个包含编码详细信息的任务。

               ITask task = job.Tasks.AddNew("Transcoding Task for "+ sourceAsset.Name,

                   processor,

                   preset,

                   Microsoft.WindowsAzure.MediaServices.Client.TaskOptions.None);

     

               // 指定要编码的源资

               task.InputAssets.Add(sourceAsset);

               // 添加输出资,以包含作业结果。

               // 输出指定为AssetCreationOptions.None,这表示

               // 输出资未加密。

               task.OutputAssets.AddNew("Output asset", AssetCreationOptions.None);

     

               //使用下面的事件处理程序查看作业进度。 

               job.StateChanged += new

                       EventHandler<JobStateChangedEventArgs>(StateChanged);

     

               //启动作业。

               job.Submit();

     

               //检查作业执行情况,等待作业完成。

               Task progressJobTask =job.GetExecutionProgressTask(CancellationToken.None);

               progressJobTask.Wait();

     

               //获取更新的作业参考。

               job = GetJob(job.Id);

     

               //如果作业状态为错误,作业进度的事件处理

               //方法应该会记录错误。这里我们查找

               //错误状态并在需要时退出。

               if (job.State == JobState.Error)

               {

                   Console.WriteLine("Transcode() failed, exiting...");

                   return null;

               }

     

               // 从作业中获取输出资产的参考。

               IAsset outAsset = job.OutputMediaAssets[0];

     

               return outAsset;

           }

     

           /// <summary>

           /// 此功能返回指定媒体处理器最新版本的参考

           /// </summary>

           private static IMediaProcessorGetLatestMediaProcessorByName(string mediaProcessorName)

           {

               var processor =_context.MediaProcessors.Where(p => p.Name == mediaProcessorName).

                   ToList().OrderBy(p => newVersion(p.Version)).LastOrDefault();

     

               if (processor == null)

                   throw new ArgumentException(string.Format("Unknownmedia processor", mediaProcessorName));

     

               return processor;

           }

     

           /// <summary>

           /// 处理事件的帮助程序方法

           /// </summary>

           private static void StateChanged(objectsender, JobStateChangedEventArgs e)

           {

               Console.WriteLine("Job statechanged event:");

               Console.WriteLine("  Previous state:" + e.PreviousState);

               Console.WriteLine("  Current state:" + e.CurrentState);

     

               switch (e.CurrentState)

               {

                   case JobState.Finished:

                       Console.WriteLine();

                       Console.WriteLine("********************");

                       Console.WriteLine("Jobis finished.");

                       Console.WriteLine("Please wait while local tasks or downloadscomplete...");

                       Console.WriteLine("********************");

                       Console.WriteLine();

                       Console.WriteLine();

                       break;

                   case JobState.Canceling:

                   case JobState.Queued:

                   case JobState.Scheduled:

                   case JobState.Processing:

                       Console.WriteLine("Please wait... ");

                       break;

                   case JobState.Canceled:

                   case JobState.Error:

                       //将发送者转换为作业。

                       IJob job = (IJob)sender;

                       //必要时显示或记录错误详细信息。

                       LogJobStop(job.Id);

                       break;

                   default:

                       break;

               }

           }

     

           /// <summary>

           /// 记录失败作业信息的帮助程序方法

           /// </summary>

           private static void LogJobStop(stringjobId)

           {

               StringBuilder builder = newStringBuilder();

               IJob job = GetJob(jobId);

     

               builder.AppendLine(" The jobstopped due to cancellation or an error.");

               builder.AppendLine("***************************");

               builder.AppendLine("JobID:" + job.Id);

               builder.AppendLine("JobName:" + job.Name);

               builder.AppendLine("JobState:" + job.State.ToString());

               builder.AppendLine("Jobstarted (server UTC time):" + job.StartTime.ToString());

               //记录任何存在的作业错误。 

               if (job.State == JobState.Error)

               {

                   builder.Append("ErrorDetails: ");

                   foreach (ITask task injob.Tasks)

                   {

                       foreach (ErrorDetail detailin task.ErrorDetails)

                       {

                           builder.AppendLine(" TaskId:" + task.Id);

                           builder.AppendLine("   ErrorCode:" + detail.Code);

                           builder.AppendLine("   ErrorMessage:" + detail.Message + " ");

                       }

                   }

               }

               builder.AppendLine("*************************** ");

               Console.Write(builder.ToString());

           }

     

           /// <summary>

           /// 此功能为给定资创建 SAS 定位

           /// </summary>

           private static voidCreateSASLocator(IAsset asset)

           {

               Console.WriteLine("Publishingasset " + asset.Name);

               // 创建原始定位,发布输出资 

               //确定只读访问策略并

               //指定资源可访问的时限为 30天。 

               _context.Locators.Create(

                   LocatorType.Sas,

                   asset,

                   AccessPermissions.Read,

                   TimeSpan.FromDays(30));

     

               // 为此 MP4文件生成 SAS定位符。

               var mp4AssetFile =asset.AssetFiles.ToList().Where(f => f.Name.EndsWith(".mp4",StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

               Uri mp4Uri =mp4AssetFile.GetSasUri();

               Console.WriteLine("Output isnow available for progressive download: ");

               Console.WriteLine(mp4Uri.OriginalString);

     

               return;

           }

       }

    }

    播放演示

    演示播放的一个简单方法是在 Windows 8.1 上启动 Windows Media Player应用程序,转至 FileOpen URL(文件打开 URL),输入编码后的资源的 SAS路径
    如果您的 PC已连接到能够进行 5.1播放的 AV接收器,您将能够听到完整 5.1环绕音响输出。

    您可以在此处下载我的编码成果示例。

    注意事项

    使用立体声进行编码

    如果您的输入资源是立体声,则 Azure Media Encoder 会在环绕声道中插入静音输出资源仍然包含 5.1音频。请注意,仅当以 Smooth Streaming格式交付输出内容时才建议插入静音。

    或者,您也可以修改 <DolbyDigitalPlusAudioProfile>元素,参考编码成 Dolby Digital Plus 立体声 XML 记录的设置,从而使编码为立体声输出。

    通过 Smooth Streaming进行 Dolby Digital Plus音频的流式传输

    可以将 Dolby Digital Plus 音频传送到 Windows 8.1上的现代化应用程序或者通过 Smooth Streaming传送到 Xbox One为实现这一点,您需要对示例代码进行如下修改

    之后,您将需要使用针对 Windows 8 Smooth Streaming客户端 SDK构建一个 Windows 8现代化应用程序。有关使用 Smooth Streaming客户端 SDK构建应用程序以播放 Dolby内容的详细信息,请阅读文章如何构建 Smooth Streaming Windows Store 应用程序

    目前并非所有平台(如 Apple iOS)都支持 Dolby 解码器,但市场上有很多其他的客户端框架支持机顶盒和智能电视等设备上的 Dolby 解码。如果您想要使用其他设备,则需要向设备制造商确认支持的解码器。

    启用 Dolby Professional Loudness Metering

    过去,很多使用多声道音频的广播公司都碰到过原声带平均音量高于或低于其他节目音量的问题。您可能也遇见过此问题,比如在节目间歇听到了声音很大的商业广告。另外,当环绕音响内容以立体或单声道音频输出形式在电视机上播放时,也会出现问题。如 Dolby 的此报告中所述,使用多声道音频的一个常见问题是在不同节目之间保持音量稳定一致的问题。要解决这个问题,建议的做法是在 Dolby Digital Plus 流中指定对话音量参数(也称为对话规范化或 DialNorm)。该值将音频音量设置为预设水平,可帮助解码器在不同节目之间进行音量水平的匹配,并摆脱烦人的音量变化。

    上文提供的预设假设源内容的默认对话规范化值为 -31 dB。请参阅使用 Dolby Professional Loudness Metering (DPLM) 支持部分,了解如何测量源内容对话的实际音量以及如何为对话规范化设置正确的值。

    要了解有关 Dolby Digital Plus技术的更多内容,请在此Dolby页面查看我们合作伙伴提供的详细信息。

    如果你有任何疑问,欢迎访问MSDN社区,由专家来为您解答Windows Azure各种技术问题,或者拨打世纪互联客户服务热线400-089-0365/010-84563652咨询各类服务信息。

    本文翻译自:http://azure.microsoft.com/blog/2014/09/03/delivering-premium-audio-experiences-with-dolby-digital-plus/


  • 相关阅读:
    paip.提升效率僵尸代码的迷思
    paip.输入法编程词库多意义条目分割 python实现.
    paip.提升效率提升绑定层次form绑定取代field绑定
    paip.提升效率调试日志系统日志参数含义python
    paip.自定义java 泛型类与泛型方法的实现总结
    paip.提升效率request自动绑定domain object
    paip.提升效率filter map reduce 的java 函数式编程实现
    paip.php 5.0 5.3 5.4 5.5 6.0的新特性总结与比较
    paip.解决中文url路径的问题图片文件不能显示
    paip.判断字符是否中文与以及判读是否是汉字uapi python java php
  • 原文地址:https://www.cnblogs.com/new0801/p/6176177.html
Copyright © 2011-2022 走看看