zoukankan      html  css  js  c++  java
  • MediaElement应用之顺畅播放实时流

      在以前的一篇文章中介绍过《MediaElement播放YUV实时流数据技巧 》,通过重载 MediaStreamSource 来解决视频监控情形下的实时流的播放问题。

      但最后有个问题一直没能解决,当时采用了“取巧”的方式:   

    element.Pause();            
    streamSource.AddStream(stream);
    element.Play();

      年前又在研究Silverlight这方面的内容,在同事的提点下才知道是自己孤陋寡闻了,算是解决了这一问题。现在对其进行简要的说明:

      首先了解一个概念:时间戳——Time Stamp,在音视频领域主要理解为用以同步音视频的。

      以前出现的问题就是没有理解到【时间戳】这一概念及其应用,简单的为了能够进入GetSampleAsync获取 MediaStreamSample而不得不频繁的调用SetSource方法,没能在实际的流处理上下功夫。

      再来看看 MediaStreamSample的构造函数:

    public MediaStreamSample(MediaStreamDescription mediaStreamDescription, Stream stream, long offset, long count,long timestamp, 
    IDictionary<MediaSampleAttributeKeys, string> attributes);


    public MediaStreamSample(MediaStreamDescription mediaStreamDescription, Stream stream, long offset, long count, long timestamp,
    long duration, IDictionary<MediaSampleAttributeKeys, string> attributes);

      其中的:

      stream——实例使用到的流数据(不一定包含的是实际播放的数据,可以与offect和count等集合指示播放的数据)

      offect——相对于stream的字节偏移量(取决于stream的数据)

      count——播放实例包含的字节数

      timestamp——实例的时间戳

      duration——实例的间隔时间(可用可不用)

      attributes——描述实例的属性集合

      同样来看看实时流解码后的数据:

    private void OnDecCBFun(int nPort, IntPtr pBuf, int nSize, ref NET_POSA_FRAME_INFO pFrameInfo, int nReserved1, int nReserved2){}

      nport——解码所用端口

      pBuf——解码后的数据指针

      nSize——解码后数据大小

      其中的【NET_POSA_FRAME_INFO】结构体如下:

    public struct NET_POSA_FRAME_INFO
    {
    public int nWidth;
    public int nHeight;
    public int nStamp;
    public int nType;
    public int nFrameRate;
    }

      nWidth——图像宽度

      nHeight——图像高度

      nStamp——时间戳

      nType——数据类型

      nFrameRate——帧率

      值得一提的是:

      MediaStreamSample的时间戳是相对时间戳,而解码数据里的时间戳是绝对时间戳,需要计算。

      MediaStreamSample的时间戳单位是100纳秒,解码数据的时间戳单位为秒(一开始没注意,会导致视频画面播放速度显得很快),需要转换。

       为了方便使用及理清条例,封装了以下几个类:  

    RealFile 管理Sample
        public class RealFile
    {
    RealMediaTypes mediaType;
    List<RealSample> videoSampleList;
    long timestamp = 0;

    public RealFile(int width, int height, RealMediaTypes type)
    {
    this.Height = height;
    this.Width = width;
    this.mediaType = type;
    videoSampleList = new List<RealSample>();
    }

    #region Property
    public long Height { get; set; }

    public long Width { get; set; }
    #endregion

    public RealSample GetVideoSample()
    {
    if (videoSampleList.Count > 0)
    {
    RealSample sample = videoSampleList[0];
    videoSampleList.RemoveAt(0);
    return sample;
    }
    else
    return null;
    }

    public void AddStream(RealStream realStream, long stamp)
    {
    if (timestamp == 0)
    timestamp = stamp;
    RealSample sample = new RealSample();
    sample.Offset = 0;
    sample.Count = realStream.Length;

    if (mediaType == RealMediaTypes.YV12)
    sample.Timestamp = (stamp - timestamp) * 10000;
    else
    sample.Timestamp = (stamp - timestamp);

    sample.MediaStream = realStream.MediaStream;

    videoSampleList.Add(sample);
    }
    }
    RealSample Sample对应信息
        public class RealSample
    {
    public long Offset { get; set; }
    public long Count { get; set; }
    public long Timestamp { get; set; }
    public long Duration { get; set; }
    public Stream MediaStream { get; set; }
    }
    RealStream
        public class RealStream
    {
    private Stream mediaStream;
    public Stream MediaStream
    {
    get { return mediaStream; }
    set { mediaStream = value; }
    }

    public RealStream(Stream stream)
    {
    this.mediaStream = stream;
    }

    public virtual void Close()
    {
    this.mediaStream.Close();
    }

    #region Property
    public virtual long Length
    {
    get { return this.mediaStream.Length; }
    }

    public virtual long Position
    {
    get { return this.mediaStream.Position; }
    }
    #endregion
    }


      则RealMediaStreamSource的代码如下:

    RealMediaStreamSource
        public enum RealMediaTypes
    {
    MBF,
    YV12,
    }

    public class RealMediaStreamSource : MediaStreamSource
    {
    RealMediaTypes _mediaType;
    Stream _mediaStream;
    long _stamp;
    int _width;
    int _height;

    RealFile realFile;

    Dictionary<MediaSampleAttributeKeys, string> _mediaSampleAttributes = new Dictionary<MediaSampleAttributeKeys, string>();
    Dictionary<MediaStreamAttributeKeys, string> _mediaStreamAttributes = new Dictionary<MediaStreamAttributeKeys, string>();
    Dictionary<MediaSourceAttributesKeys, string> _mediaSourceAttributes = new Dictionary<MediaSourceAttributesKeys, string>();
    List<MediaStreamDescription> _mediaStreamDescriptions = new List<MediaStreamDescription>();

    private int index;

    public RealMediaStreamSource(RealMediaTypes type, Stream stream, int width, int height, long stamp)
    {
    this._mediaType = type;
    this._mediaStream = stream;
    this._width = width;
    this._height = height;
    this._stamp = stamp;
    }

    #region Override
    protected override void OpenMediaAsync()
    {
    index = 0;
    realFile = new RealFile(_width, _height, _mediaType);
    RealStream stream = new RealStream(_mediaStream);
    realFile.AddStream(stream, _stamp);

    //描述媒体示例
    _mediaSampleAttributes[MediaSampleAttributeKeys.FrameHeight] = _height.ToString();
    _mediaSampleAttributes[MediaSampleAttributeKeys.FrameWidth] = _width.ToString();

    //描述媒体流
    _mediaStreamAttributes[MediaStreamAttributeKeys.Height] = _height.ToString();
    _mediaStreamAttributes[MediaStreamAttributeKeys.Width] = _width.ToString();
    _mediaStreamAttributes[MediaStreamAttributeKeys.CodecPrivateData] = "";
    if (_mediaType == RealMediaTypes.MBF)
    _mediaStreamAttributes[MediaStreamAttributeKeys.VideoFourCC] = "H264";
    else if (_mediaType == RealMediaTypes.YV12)
    _mediaStreamAttributes[MediaStreamAttributeKeys.VideoFourCC] = "YV12";

    //详尽描述媒体流
    _mediaStreamDescriptions.Add(new MediaStreamDescription(MediaStreamType.Video, _mediaStreamAttributes));

    //描述媒体源
    _mediaSourceAttributes[MediaSourceAttributesKeys.CanSeek] = "true";
    _mediaSourceAttributes[MediaSourceAttributesKeys.Duration] = "";

    index++;
    this.ReportOpenMediaCompleted(_mediaSourceAttributes, _mediaStreamDescriptions);
    }

    protected override void GetSampleAsync(MediaStreamType mediaStreamType)
    {
    MediaStreamSample mediaSample = null;
    if (mediaStreamType == MediaStreamType.Video)
    mediaSample = GetVideoSample();
    if (mediaSample != null)
    ReportGetSampleCompleted(mediaSample);
    }

    private MediaStreamSample GetVideoSample()
    {
    RealSample sample = realFile.GetVideoSample();
    MediaStreamDescription msd = new MediaStreamDescription(MediaStreamType.Video, _mediaStreamAttributes);
    MediaStreamSample mediaSample;
    if (sample == null)
    {
    mediaSample = new MediaStreamSample(
    msd,
    _mediaStream,
    0,
    _mediaStream.Length,
    0,
    _mediaSampleAttributes);
    }
    else
    {
    mediaSample = new MediaStreamSample(
    msd,
    sample.MediaStream,
    sample.Offset,
    sample.Count,
    sample.Timestamp,
    _mediaSampleAttributes);
    }
    index--;
    return mediaSample;
    }

    protected override void SeekAsync(long seekToTime)
    {
    this.ReportSeekCompleted(seekToTime);
    }

    protected override void CloseMedia()
    {
    }

    protected override void SwitchMediaStreamAsync(MediaStreamDescription mediaStreamDescription)
    {
    }

    protected override void GetDiagnosticAsync(MediaStreamSourceDiagnosticKind diagnosticKind)
    {
    }
    #endregion

    public void AddStream(Stream mediaStream, long stamp)
    {
    this._mediaStream = mediaStream;
    RealStream addstream = new RealStream(mediaStream);
    realFile.AddStream(addstream, stamp);
    index++;
    }
    }

      RealMediaStreamSource具体调用如下:

    调用
            bool isFirst = true;
    void proxy_GetVideosReceived(object sender, GetVideosReceivedEventArgs e)
    {
    StreamData sources = e.data;
    if (sources.Stamp == 0)
    return;

    if (isFirst)
    {
    object obj = new object();
    lock (obj)
    {
    ShowStream(sources);
    isFirst = false;
    }
    }
    else
    {
    try
    {
    ShowStreamToo(sources);
    }
    catch (System.Exception ex)
    {
    }
    }
    }

    private void ShowStream(StreamData sources)
    {
    Stream stream = new MemoryStream(sources.Streams);
    streamSource1 = new RealMediaStreamSource(RealMediaTypes.YV12, stream, sources.Width, sources.Height, sources.Stamp);
    element1.SetSource(streamSource1);
    }

    private void ShowStreamToo(StreamData sources)
    {
    Stream stream = new MemoryStream(sources.Streams);
    ((RealMediaStreamSource)streamSource1).AddStream(stream, sources.Stamp);
    }


     

      至此便能够顺利的播放视频流啦,归根结底问题在于实例化的MediaStreamSample所包含的信息是否准确

       GetVideoSample()里的处理还需要大家根据自己的实际情况灵活应用哦!

      目前个人也还处于探索学习阶段,欢迎大家多多指教!



  • 相关阅读:
    对象和接口简单比较
    DevExpress报表开发基本流程
    有关ExecuteNonQuery返回值的分析
    2012年度计划
    小测试:有关++i&&i++,你是不是看晕了
    “PE文件格式”1.9版 完整译文
    .NET中的入口及幕后英雄:MSCorEE.dll(转)
    软件构建过程中的隐喻
    转:地图导出格式,教你如何选择
    推荐几个网站
  • 原文地址:https://www.cnblogs.com/wdysunflower/p/2331372.html
Copyright © 2011-2022 走看看