zoukankan      html  css  js  c++  java
  • silverlight 视频

    原文转自:http://www.systhinker.com/?viewnews-20671

    这是我第一次文章发布到IT工程技术网首页上。

    我自认为这篇文章的内容达到了在IT工程技术网首页上发布的水准,因为我搜遍了 Google、CodePlex、SourceForge、MSDN 和 Silverlight 的官方论坛也没能找到和这篇文章内容相同的文章。

     

    我想在这篇文章中和各位分享我解决问题的过程,希望我的经历能让阅读这篇文章的人有所收获。

     

    Silverlight 有没有对 FLV 视频提供支持?

    好吧,所有的开发人员都是懒惰的,ME2。先查查微软的文档吧,FLV 视频是如此的普及,没准儿微软已经在 Silverlight 中提供了对 FLV 视频的支持。

    结果,微软在 Silverlight 文档中淡定的表示不支 FLV 格式,也不支持 VP6 和 H.263 编码的视频。

    嘛,这样的结果已经提前预见到了。既然官方不支持,那就自己山寨一个吧。

     

    如何才能让 Silverlight 支持非官方的媒体格式?

    在 Silverlight 应用程序中播放媒体必须要用到的是 MediaElement 控件。为 MediaElement 控件设置媒体源的方法有两种:

    1. 为 Source 属性设置一个 Uri 实例;
    2. 使用 SetSource 方法设置一个 Stream 或 MediaStreamSource 实例。

    Uri 和 Stream 的用法我知道,行不通。剩下的就只有 MediaStreamSource 了,查查怎么用吧。

     

    没错,就是在这个时候,我做了这篇文章开头提到的事情:

    我相信我搜遍了整个互联网,但所有的搜索结果都指向了同一个链接:ManagedMediaHelpers

     

    那么 ManagedMediaHelpers 是个什么东东?简单说就是:

    1. 实现了一个 MP3 文件的分离器,用于将 MP3 文件中的音频流数据分离出来;
    2. 实现了一个继承自 MediaStreamSource 的 Mp3MediaStreamSource,把从 MP3 文件中分离出来的音频流交给 MediaElement 解码。

     

    根据之前研究 DirectShow 的经验,要让 Silverlight 支持非官方的媒体格式,我得出了以下结论:

    1. 需要一个将音视频流从文件中分离出来的分离器(Parser);
    2. 需要一个将音频流解码成 PCM 的音频解码器(Audio Decoder)除非 Silverlight 已经内置;
    3. 需要一个将视频流解码成 YV12 的视频解码器(Vidoe Decoder)除非 Silverlight 已经内置;
    4. 将音视频的帧数据写入 MediaStreamSample 通过 MediaStreamSource 交给 MediaElement 呈现(Renderer)。

     

    如何实现 FLV 视频的分离器?

    所有点击这篇文章标题进来的人应该都知道 FLV 是 Adobe 定义的一个媒体文件格式。youtube、nicovidoe等在线视频网站(好吧,还有优酷和土豆)都使用这种文件格式作为媒体内容的容器,因为它能在 Flash 应用程序中播放。

     

    要实现 FLV 视频的分离器,就要知道 FLV 的文件格式定义。既然是 Adobe 定义的,那就在 Adobe 上找找吧。在 Google 中敲入 flv site:adobe.com 可以在搜索结果中找到一个名为 Video File Format Specification Version 9 的 PDF 文件。天杀的 Adobe !这个文档中存在两位数的错误,直接导致我浪费了几个小时的时间。下载 Video File Format Specification Version 10 这个文档,错误比较少。第10页中 CompositionTime 的 Type 是 UI24,而不是 SI24。

     

    根据这个文档中的定义就可以写出一个 FLV 文件的分离器了。FLV 文件格式的定义比较简单,如果有时间,我会在另一篇文章里进行介绍。

    如何实现 FLV 视频的解码器?

    我对看到这里的朋友说声抱歉。我没能实现对 VP6 和 H.263 视频的解码,所以我这篇文章的标题才叫《在 Silverlight 应用程序中实现对 FLV 视频格式的支持》。

    要实现对 VP6 和 H.263 视频的解码,可以将 FFMpage 工程组的代码改写成 C# 的版本(泪目ing,逃~)。

    就算要改写解码器,那也要有个参考吧。哼哼,本人号称搜遍互联网可不是吹的,我推荐你参考 Saluse MediaKit for Silverlight 的代码,作者实现了 Silverlight 版的 Mp3 解码器。还有 DirectShow for Silverlight 的代码,作者实现了 Silverlight 版的 Ogg 解码器。

     

    既然没能实现自己解码器,那就继续用 Silverlight 内置的解码器吧。

    FLV 支持 AAC 音频和 H.264 视频流,而 Silverlight 则内置了 AAC 和 H.264 解码器。所以还是可以在 Silverlight 中播放 FLV 视频的,只不过必须是音频流使用 AAC 编码,视频流使用 H.264 编码的 FLV 格式的文件。

     

    顺便说一句,土豆网的高清黑豆就是这种格式,并且新浪播客也支持上传这种格式(虽然官方说法是不支持)。

     

    如何解码 FLV 文件中的 AAC 音频流?

    要想 Silverlight 对 AAC 音频流进行解码,必须先让 Silverlight 知道用哪个解码器才能对 AAC 音频流进行解码。

    下面的代码完成了这个工作,其中 MediaStreamAttributeKeys.CodecPrivateData 是重点所在,它是通过计算 WaveFormatExtensible 结构得到的。

    WaveFormatExtensible 结构的值来自于 AUDIOTAG 的 AUDIODATA 结构(见第6页)。下面的代码为了方便说明,直接进行了赋值。

     protected override void OpenMediaAsync() {     WaveFormatExtensible wfx = new WaveFormatExtensible();     wfx.FormatTag = 0x00FF; // AAC=0x00FF; MP3=0x0055; ADPCM=0x5346; PCM=0x0001     wfx.Channels = 2;     wfx.BlockAlign = 8;     wfx.BitsPerSample = 16;     wfx.SamplesPerSec = 44100;     wfx.AverageBytesPerSecond = wfx.SamplesPerSec * wfx.Channels * wfx.BitsPerSample / wfx.BlockAlign;     wfx.Size = 0;     string codecPrivateData = wfx.ToHexString();      Dictionary<MediaStreamAttributeKeys, string> audioStreamAttributes = new Dictionary<MediaStreamAttributeKeys, string>();     audioStreamAttributes[MediaStreamAttributeKeys.CodecPrivateData] = codecPrivateData; // 管线需要正确初始化和呈现的编码解码器数据。对于视频,这是标头信息。对于音频,这是 base16 编码的 WaveFormatEx 结构。     this.audioStreamDescription = new MediaStreamDescription(MediaStreamType.Audio, audioStreamAttributes); // 详尽描述媒体流,以便初始化 MediaElement 和基础媒体管线。      Dictionary<MediaSourceAttributesKeys, string> mediaSourceAttributes = new Dictionary<MediaSourceAttributesKeys, string>();     mediaSourceAttributes[MediaSourceAttributesKeys.Duration] = "0"; // 源的整数形式的播放时间长度,以 100 纳秒为增量单位(TimeSpan 结构的值的计时周期数)。      List<MediaStreamDescription> mediaStreamDescriptions = new List<MediaStreamDescription>();     mediaStreamDescriptions.Add(this.audioStreamDescription);      this.ReportOpenMediaCompleted(mediaSourceAttributes, mediaStreamDescriptions); // 向 MediaStreamSource 附加到的 MediaElement 报告媒体已打开并且 MediaStreamSource 具有其所包含的内容流的说明。 } 

    之后 Silverlight 会调用 GetSampleAsync 方法获得 AAC 音频流的 MediaStreamSample 实例。实例化 AAC 音频流的 MediaStreamSample 很简单,只要将 FLV 文件中 AUDIOTAG 的 AUDIODATA 的 AACAUDIODATA 的 Data(Raw AAC frame)交给 MediaStreamSample 就行了。


    注意!FLV 文件中第一个 AUDIOTAG 的 AUDIODATA 的 AACAUDIODATA 的 Data 总是 AudioSpecificConfig(在 ISO/IEC 14496-3 中定义),解码的时候注意跳过这个 AUDIOTAG。

     

    如何解码 FLV 文件中的 H.264 视频流?

    接下来是这篇文章最重要的部分,我将展示如何用 MediaStreamSource 解码 H.264 视频流。在此之前,你不会在互联网上获得任何关于如何使用 MediaStreamSource 解码视频的文字或代码。

    下面的代码中不包括 MediaStreamAttributeKeys.CodecPrivateData 的设置,那是因为 H.264 视频流是不需要设置 MediaStreamAttributeKeys.CodecPrivateData 的。而 MediaStreamAttributeKeys.Width 和 MediaStreamAttributeKeys.Height 也不是需要设置的。为什么不需要设置?请继续往下读。

     protected override void OpenMediaAsync() {     Dictionary<MediaStreamAttributeKeys, string> videoStreamAttributes = new Dictionary<MediaStreamAttributeKeys, string>();     videoStreamAttributes[MediaStreamAttributeKeys.VideoFourCC] = "H264"; // 实例化视频编码解码器所需的数据。这是一个由四个字符构成的值,也称作 FourCC。     this.videoStreamDescription = new MediaStreamDescription(MediaStreamType.Video, videoStreamAttributes);      Dictionary<MediaSourceAttributesKeys, string> mediaSourceAttributes = new Dictionary<MediaSourceAttributesKeys, string>();     mediaSourceAttributes[MediaSourceAttributesKeys.Duration] = "0";      List<MediaStreamDescription> mediaStreamDescriptions = new List<MediaStreamDescription>();     mediaStreamDescriptions.Add(this.videoStreamDescription);      this.ReportOpenMediaCompleted(mediaSourceAttributes, mediaStreamDescriptions); } 

    之后 Silverlight 会调用 GetSampleAsync 方法获得 H.264 视频流的 MediaStreamSample 实例。实例化 H.264 的 MediaStreamSample 是比较麻烦的,要分成两个部分来说:

    1. 计算 AVCDecoderConfigurationRecord  得到 CodecPrivateData 数据(只有第一帧需要);
    2. 计算 NALUs 得到帧数据。

     

    计算 AVCDecoderConfigurationRecord  得到 CodecPrivateData 数据

    H.264 视频流的 CodecPrivateData 实际上就是 AVCDecoderConfigurationRecord 中 SequenceParameterSets(SPS)和 PictureParameterSets(PPS)使用 byte[] {00, 00, 01} 连接的字节数组。

    注意!FLV 文件中第一个 VIDEOTAG 的 VIDEODATA 的 AVCVIDEOPACKET 的 Data 总是 AVCDecoderConfigurationRecord(在 ISO/IEC 14496-15 中定义),解码的时候注意跳过这个 VIDOETAG。

     

    AVCDecoderConfigurationRecord 结构的定义:

    aligned(8) class AVCDecoderConfigurationRecord { 
    unsigned int(8) configurationVersion = 1; 
    unsigned int(8) AVCProfileIndication; 
    unsigned int(8) profile_compatibility; 
    unsigned int(8) AVCLevelIndication; 
    bit(6) reserved = ‘111111’b; 
    unsigned int(2) lengthSizeMinusOne; 
    bit(3) reserved = ‘111’b; 
    unsigned int(5) numOfSequenceParameterSets; 
    for (i=0; i< numOfSequenceParameterSets; i++) { 
    unsigned int(16) sequenceParameterSetLength ; 
    bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit; 
    } 
    unsigned int(8) numOfPictureParameterSets; 
    for (i=0; i< numOfPictureParameterSets; i++) { 
    unsigned int(16) pictureParameterSetLength; 
    bit(8*pictureParameterSetLength) pictureParameterSetNALUnit; 
    } 
    }

     

    下面蓝色的部分就是 FLV 文件中的 AVCDecoderConfigurationRecord 部分。

    00000130h: 00 00 00 17 00 00 00 00 01 4D 40 15 FF E1 00 0A ; .........M@.

  • 相关阅读:
    LeetCode 258 Add Digits
    LeetCode 231 Power of Two
    LeetCode 28 Implement strStr()
    LeetCode 26 Remove Duplicates from Sorted Array
    LeetCode 21 Merge Two Sorted Lists
    LeetCode 20 Valid Parentheses
    图形处理函数库 ImageTTFBBox
    php一些函数
    func_get_arg(),func_get_args()和func_num_args()的用法
    人生不是故事,人生是世故,摸爬滚打才不会辜负功名尘土
  • 原文地址:https://www.cnblogs.com/merray/p/2622592.html
Copyright © 2011-2022 走看看