Stream Player Control-流播放器控件

Posted boonya

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Stream Player Control-流播放器控件相关的知识,希望对你有一定的参考价值。

原文地址: https://www.codeproject.com/articles/885869/stream-player-control
在本文中,您将会找到一个流播放器控件的实现。


介绍


这篇文章是我前一篇文章的一个延续,它展示了一个网络摄像头控件的实现。 最近我创建了另一个控件,并希望与社区分享我的经验。 这是一个基于FFmpeg的流播放器控件,可以执行以下操作:
  1. 播放 RTSP/RTMP视频流或本地视频文件
  2. 检索控件显示的当前帧
该控件没有额外的依赖和一个简约的界面。

必要条件

  1. 该控件的WinForms版本是使用.NET Framework 2.0实现的
  2. 该控件的WPF版本是使用.NET Framework 4 Client Profile实现的
该控件支持x86和x64平台目标。


背景

现在通过互联网流式传输音频,视频和数据是非常平常的事情。 但是,当我试图找到一个.NET控件来播放通过网络发送的视频流时,我几乎找不到任何东西。 这个项目试图填补这个空白。

实现细节

如果你对实现细节不感兴趣,那么你可以跳过这一节。


实施分为三层。

  1. 底层被实现为本地DLL模块,它将我们的调用转发给FFmpeg框架。
  2. 为了便于分发,原生DLL模块作为资源嵌入到控件的程序集中。 在运行时阶段,DLL模块将被提取到磁盘上的临时文件,并通过后期绑定技术使用。 一旦控制权被处置,临时文件将被删除。 换句话说,该控件是作为一个单独的文件分发的。 所有这些操作都是由中间层来实现的。
  3. 顶层实现控制类本身。
下图显示了实现的逻辑结构。


只有顶层应该被客户使用。

底层

底层使用facade 模式来为FFmpeg框架提供一个简化的接口。 门面由三个类组成:StreamPlayer类,它实现了流播放功能

/// <summary>
/// The StreamPlayer class implements a stream playback functionality.
/// </summary>
class StreamPlayer : private boost::noncopyable

public:

    /// <summary>
    /// Initializes a new instance of the StreamPlayer class.
    /// </summary>
    StreamPlayer();

    /// <summary>
    /// Initializes the player.
    /// </summary>
    /// <param name="playerParams">The StreamPlayerParams object that contains the information that is used to initialize the player.</param>
    void Initialize(StreamPlayerParams playerParams);

    /// <summary>
    /// Asynchronously plays a stream.
    /// </summary>
    /// <param name="streamUrl">The url of a stream to play.</param>
    void StartPlay(std::string const& streamUrl);
    
    /// <summary>
    /// Retrieves the current frame being displayed by the player.
    /// </summary>
    /// <param name="bmpPtr">Address of a pointer to a byte that will receive the DIB.</param>
    void GetCurrentFrame(uint8_t **bmpPtr);

    /// <summary>
    /// Retrieves the unstretched frame size, in pixels.
    /// </summary>
    /// <param name="widthPtr">A pointer to an int that will receive the width.</param>
    /// <param name="heightPtr">A pointer to an int that will receive the height.</param>
    void GetFrameSize(uint32_t *widthPtr, uint32_t *heightPtr);

    /// <summary>
    /// Uninitializes the player.
    /// </summary>
    void Uninitialize();
;
Stream类将视频流转换为一系列帧

/// <summary>
/// A Stream class converts a stream into series of frames. 
/// </summary>
class Stream : private boost::noncopyable

public:
    /// <summary>
    /// Initializes a new instance of the Stream class.
    /// </summary>
    /// <param name="streamUrl">The url of a stream to decode.</param>
    Stream(std::string const& streamUrl);

    /// <summary>
    /// Gets the next frame in the stream.
    /// </summary>
    /// <returns>The next frame in the stream.</returns>
    std::unique_ptr<Frame> GetNextFrame();

    /// <summary>
    /// Gets an interframe delay, in milliseconds.
    /// </summary>
    int32_t InterframeDelayInMilliseconds() const;

    /// <summary>
    /// Releases all resources used by the stream.
    /// </summary>
    ~Stream();
;
Frame类,它是一组框架相关的工具。

/// <summary>
/// The Frame class implements a set of frame-related utilities. 
/// </summary>
class Frame : private boost::noncopyable

public:
    /// <summary>
    /// Initializes a new instance of the Frame class.
    /// </summary>
    Frame(uint32_t width, uint32_t height, AVPicture &avPicture);

    /// <summary>
    /// Gets the width, in pixels, of the frame.
    /// </summary>
    uint32_t Width() const  return width_; 

    /// <summary>
    /// Gets the height, in pixels, of the frame.
    /// </summary>
    uint32_t Height() const  return height_; 

    /// <summary>
    /// Draws the frame.
    /// </summary>
    /// <param name="window">A container window that frame should be drawn on.</param>
    void Draw(HWND window);

    /// <summary>
    /// Converts the frame to a bitmap.
    /// </summary>
    /// <param name="bmpPtr">Address of a pointer to a byte that will receive the DIB.</param>
    void ToBmp(uint8_t **bmpPtr);

    /// <summary>
    /// Releases all resources used by the frame.
    /// </summary>
    ~Frame();
;
这些树类是FFmpeg Facade DLL模块的核心。

中间层

中间层由StreamPlayerProxy类实现,该类用作FFmpeg Facade DLL模块的代理。


首先,我们应该从资源中提取FFmpeg Facade DLL模块并将其保存到一个临时文件中。

_dllFile = Path.GetTempFileName();
using (FileStream stream = new FileStream(_dllFile, FileMode.Create, FileAccess.Write))

    using (BinaryWriter writer = new BinaryWriter(stream))
    
        writer.Write(Resources.StreamPlayer);
    
然后我们将DLL模块加载到调用进程的地址空间中。

_hDll = LoadLibrary(_dllFile);
if (_hDll == IntPtr.Zero)

    throw new Win32Exception(Marshal.GetLastWin32Error());
并将DLL模块函数绑定到类实例方法。

private delegate Int32 StopDelegate();
private StopDelegate _stop;

// ...

IntPtr procPtr = GetProcAddress(_hDll, "Stop");
_stop =
    (StopDelegate)Marshal.GetDelegateForFunctionPointer(procPtr, 
     typeof(StopDelegate));
在处理控件时,我们卸载DLL模块并将其删除。

private void Dispose()
    
    if (_hDll != IntPtr.Zero)
    
        FreeLibrary(_hDll);
        _hDll = IntPtr.Zero;
    

    if (File.Exists(_dllFile))
    
        File.Delete(_dllFile);
        


顶层

顶层由具有以下接口的StreamPlayerControl类实现。

/// <summary>
/// Asynchronously plays a stream.
/// </summary>
/// <param name="uri">The url of a stream to play.</param>
/// <exception cref="ArgumentException">An invalid string is passed as an argument.</exception>
/// <exception cref="Win32Exception">Failed to load the FFmpeg facade dll.</exception>
/// <exception cref="StreamPlayerException">Failed to play the stream.</exception>
public void StartPlay(Uri uri)

/// <summary>
/// Retrieves the image being played.
/// </summary>
///以上是关于Stream Player Control-流播放器控件的主要内容,如果未能解决你的问题,请参考以下文章

Stream Player control

Java音乐播放:java-stream-player

如何禁用音频(流)html5元素的自动播放

如何使用 JW Player 播放 rtmp 流?

启动stream2时如何停止stream1

如何在黑莓中播放音频作为背景