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@.

  • 相关阅读:
    Makefile学习笔记1
    Shell脚本学习笔记8——跳出循环
    Shell脚本学习笔记7——case...esac
    Shell脚本学习笔记6——循环语句
    Linux命令学习笔记3:touch命令
    Linux命令学习笔记2:ll命令
    Linux命令学习笔记1:ln命令
    Shell脚本学习笔记5——if条件语句
    Shell 脚本学习笔记4——Shell脚本中 '$' 符号的多种用法
    利用C++创建DLL并C#调用
  • 原文地址:https://www.cnblogs.com/merray/p/2622592.html
Copyright © 2011-2022 走看看