1. 程式人生 > >Stream Player Control-流播放器控制元件

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> /// <paramname="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> /// <paramname="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> /// <paramname="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> /// <paramname="widthPtr">A pointer to an int that will receive the width.</param> /// <paramname="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>
    /// <paramname="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>
    /// <paramname="window">A container window that frame should be drawn on.</param>
    void Draw(HWND window);

    /// <summary>
    /// Converts the frame to a bitmap.
    /// </summary>
    /// <paramname="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>
/// <paramname="uri">The url of a stream to play.</param>
/// <exceptioncref="ArgumentException">An invalid string is passed as an argument.</exception>
/// <exceptioncref="Win32Exception">Failed to load the FFmpeg facade dll.</exception>
/// <exceptioncref="StreamPlayerException">Failed to play the stream.</exception>
public void StartPlay(Uri uri)

/// <summary>
/// Retrieves the image being played.
/// </summary>
/// <returns>The current image.</returns>
/// <exceptioncref="InvalidOperationException">The control is not playing a video stream.</exception>
/// <exceptioncref="StreamPlayerException">Failed to get the current image.</exception>
public Bitmap GetCurrentFrame();

/// <summary>
/// Stops a stream.
/// </summary>
/// <exceptioncref="InvalidOperationException">The control is not playing a stream.</exception>
/// <exceptioncref="StreamPlayerException">Failed to stop a stream.</exception>
public void Stop();

/// <summary>
/// Gets a value indicating whether the control is playing a video stream.
/// </summary>
public Boolean IsPlaying { get; }

/// <summary>
/// Gets the unstretched frame size, in pixels.
/// </summary>
public Size VideoSize  { get; }

/// <summary>
/// Occurs when the first frame is read from a stream.
/// </summary>
public event EventHandler StreamStarted;

/// <summary>
/// Occurs when there are no more frames to read from a stream.
/// </summary>
public event EventHandler StreamStopped;

/// <summary>
/// Occurs when the player fails to play a stream.
/// </summary>
public event EventHandler StreamFailed;

使用

開啟程式包管理器控制檯,並將Nuget程式包新增到您的專案中:

Install-Package WebEye.Controls.WinForms.StreamPlayerControl
首先,我們需要將控制元件新增到Visual Studio Designer工具箱,使用右鍵單擊,然後選擇“選擇專案...”選單項。 然後,我們將控制元件放置在所需的位置並具有所需的大小。 控制元件例項變數的預設名稱將是streamPlayerControl1。


以下程式碼使用提供的地址非同步播放流。
streamPlayerControl1.StartPlay(new Uri("rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov"));

還有一個選項可以指定連線超時和底層傳輸協議。

streamPlayerControl1.StartPlay(new Uri("rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov"),
    TimeSpan.FromSeconds(15), RtspTransport.UdpMulticast);
要獲得正在播放的幀,只需呼叫GetCurrentFrame()方法即可。 幀的解析度和質量取決於流的質量。
using (Bitmap image = streamPlayerControl1.GetCurrentFrame())
{
    // image processing...
}
要停止流,使用Stop()方法。
streamPlayerControl1.Stop();
您可以隨時使用以下程式碼檢查播放狀態。
if (streamPlayerControl1.IsPlaying)
{
    streamPlayerControl1.Stop();
}
此外,StreamStarted,StreamStopped和StreamFailed事件可用於監視播放狀態。


要報告錯誤,使用異常,所以不要忘記將程式碼包裝在try / catch塊中。 這就是使用它。 要檢視完整的示例,請檢視演示應用程式來源。

WPF版本

FFmpeg外觀需要一個WinAPI視窗控制代碼(HWND)才能將其用作渲染目標。 問題是,在WPF世界的窗戶不再有處理。 VideoWindow類解決了這個問題。

<UserControlx:Class="WebEye.StreamPlayerControl"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"mc:Ignorable="d"d:DesignHeight="300"d:DesignWidth="300"xmlns:local="clr-namespace:WebEye">
    <local:VideoWindowx:Name="_videoWindow"HorizontalAlignment="Stretch"VerticalAlignment="
            
           

相關推薦

Stream Player Control-播放控制元件

在本文中,您將會找到一個流播放器控制元件的實現。介紹這篇文章是我前一篇文章的一個延續,它展示了一個網路攝像頭控制元件的實現。 最近我建立了另一個控制元件,並希望與社群分享我的經驗。 這是一個基於FFmpeg的流播放器控制元件,可以執行以下操作:播放RTSP/RTMP視訊流或本

VS2010/MFC對話方塊程式呼叫Windows Media Player播放控制元件

MFC對話方塊程式呼叫Windows Media Player播放器控制元件播放開啟的avi格式的檔案,具體步驟如下: 1.根據MFC嚮導提示,建立一個預設的對話方塊專案TestMediaPlayer. 2.開啟資源,在主對話方塊上點選右鍵,在右鍵選單中選擇“插入Activ

h5+js視訊播放控制元件

   由於h5相容性問題,很多瀏覽器對於插入視訊播放的支援都大不相同。火狐支援的比較完整,谷歌則支援的不是很好,很多功能都不能實現,這就需要我們去自制一個播放介面,去相容不同的瀏覽器。    只插入一個視訊時,瀏覽器中只會出現這樣一個畫面。只有單擊右鍵才可以彈出選單欄顯示播放或者顯示控制元件;   下

一個強悍而優美的Android視訊播放控制元件JieCaoVideoPlayer

本文為轉載文章,原文作者:魑魅魍魎9527   原文連結:https://blog.csdn.net/qq_28934205/article/details/52944383?utm_source=copy  效果圖:   視訊可以全屏 顯示 旋轉螢幕也可以  xml程

Android上定義播放控制元件UniversalVideoView

在Android上播放視訊最簡單的方法是使用SDK中內建的VideoView,然後加上MediaController來控制視訊播放暫停等,但是這樣有一個缺點是無法定製自己的控制UI,所以這裡提供一個自定義播放控制元件,它可以設定多種自定義屬性(如loading樣式,錯誤視訊

零基礎讀懂視頻播放控制原理: ffplay 播放源代碼分析

5.4 編碼方式 是否播放 都對 enum 其中 mat 源碼 開始 https://www.qcloud.com/community/article/535574001486630869 視頻播放器原理其實大抵相同,都是對音視頻幀序列的控制。只是一些播放器在音視頻同步上可

關於Timer計時控制元件的java.lang.IllegalStateException: TimerTask is scheduled already問題分析

博主最近在改被人的專案,準備寫一個類似今日頭條的載入完成提示,就寫了個計時器讓它顯示幾秒,於是就用到了timer這個計時器控制元件,簡單的正常程式碼如下,因為博主的載入需要多次,當timer二次呼叫的時候就出現了java.lang.IllegalStateException: TimerTask i

iOS學習十二之選擇控制元件UIPickerView

UIPickerView是一個簡易的列表控制元件,用於提供有限個數的選項供使用者選擇。 它是通過代理和資料來源的方法對其進行設定和資料來源填充的,這種控制元件的設計模式也是代理模式的應用之一。 新增下面的程式碼即可實現基本功能。 class ViewController: UIViewControlle

零基礎讀懂視訊播放控制原理——ffplay播放原始碼分析(二)

圖7音視訊解碼分析 圖7為輸出的音訊幀和視訊幀序列,每一幀都有PTS和DTS標籤,這兩個標籤究竟是什麼意思呢? DTS(Decode Time Stamp)和PTS(Presentation Time Stamp)都是時間戳,前者是解碼時間,後者是顯示時間,都是為視訊幀、音訊幀打上的時間標籤,以更

ABAP 使用嚮導建立TABLE CONTROL (TC 表控制元件

使用嚮導建立TC,主要有以下步驟, 1.定義你要在TC上顯示的列為一個數據結構,並用該結構定義內表和工作區。在建立TC前一定要啟用程式,這樣TC才能獲得定義的內表和工作區。 2.建立螢幕,轉到格式佈局

MFC 主對話方塊獲得tab control子對話方塊控制元件的值

1.建立兩個對話方塊程式,用來當做Tab Control控制元件的兩個頁。別忘了把Style改為Child,Border改為None。然後就可以在上面加其他控制元件了。 接著分別為這兩個對話方塊建立兩個類,比如CPage1和CPage2。 然後在對話方塊類標頭檔案中,加入這

WPF Control Development Unleashed(wpf控制元件開發揭祕) 章節一 WPF的設計理念

章節一 WPF的設計理念 想像你是一個技術相當成熟的設計師,你被要求在空地上蓋一座房子.你看看附近的建築物確保你使用的外部的顏色不與鄰居的衝突.當你完成你的新房子大作之後,你退後一步,被你完成的東西驚呆了.房子擁有輝煌的四層結構和巨大的開放式玻璃幕牆,周圍的美

axure rp 中繼 控制元件 預設資料

我們使用中繼器來操作表格的初始化(頁面載入時展示預設的一部分資料)、增(增加一行)、刪(刪除一行)。首先來看效果:表格裡面放的是控制元件,不是純文字。不管做什麼,最重要的是思路。我的實現思路是:首先要設定一個預設的資料集,然後一行一行的讀取這個預設的資料集,在讀取資料時再根據

iOS:選擇控制元件UIPickerView的詳解和演示

#import "ViewController.h" 2 3 @interface ViewController () 4 @property (weak, nonatomic) IBOutlet UIPickerView *pickerView; 5 6 @end

java Swing 時間選擇控制元件

效果圖: 中的 flowlayout_v.jar 檔案 示例程式碼: HongYeLingGuDate類 import java.awt.Color; import java.awt.Dimension; import java.awt.Flow

WPF播放GIF控制元件完整程式碼

WPF擁有很強的介面設計能力,可以很方便的做出非常漂亮的介面。但有個問題,WPF沒有自己的播放GIF的控制元件。這讓很多想在介面播放動態動畫的人不得不使用視訊來代替。WPF就真的不能播放GIF動圖了嗎?當然是可以播放的,只是我們需要寫一些程式碼。下面是我總結後寫的一個WPF

介紹一個基於QT的原始碼編輯控制元件QScintilla

轉 : pc的部落格 什麼是QScintilla? QScintilla is a port to Qt of Neil Hodgson’s Scintilla C++ editor control. QScintilla是Scintilla在QT上的移植,Scintil

那些年我們熬夜打造一可收縮式標籤控制元件

一、前言 時間匆匆,一眨眼來廈門已經一個多月了。似乎已經適應了這邊的生活,喜歡這邊的風,溫和而舒適,還有淡淡海的味道 。。。 還在再次跟大家致個歉意,部落格的更新又延期了。新的環境,新的工作加上專案大改版,基本每天都有大量的事情,週末也不得空閒。 非常感謝

RN開源播放Video元件(react-native-video)

一. 專案介紹 該元件進行封裝成React Native平臺播放器Video元件,大家可以使用該元件進行播放視訊啦~不過支援React Native的版本最低0.19版本。 二. 1.1. 執行命令進行安裝 npm instal

Android基礎控制元件Chronometer計時控制元件

Chronometer→計時器控制元件 java.lang.Object   android.view.View     android.widget.TextView       android.widget.Chronometer main.xml佈局 <?xm