VLC客戶端和SDK的簡單應用

VLC_SDK編程指南html

VLC 是一款自由、開源的跨平臺多媒體播放器及框架,可播放大多數多媒體文件,以及 DVD、音頻 CD、VCD 及各種流媒體協議。它能夠支持目前市面上大多數的視頻解碼,除了Real。編程

VLC_SDK的調用

VLC的SDK使用C語言寫成,它的解碼庫部分的基礎是FFMpeg,FFMpeg也是一套能夠用來記錄、轉換數字音頻、視頻,並能將其轉化爲流的開源計算機程序。c#

VLC的SDK是在其客戶端的安裝文件根目錄下,下載完VLC的客戶端,並安裝後,在其根目錄下能夠找到,例如windows

客戶端安裝在C:\Program Files\VideoLAN\VLC。由於咱們是使用C#語言來調用其SDK,因此只需使用到其plugins文件夾和libvlc.dll、libvlccore.dll兩個文件。api

libvlc.dll和libvlccore.dll是其兩個核心的動態連接庫。libvlc對外提供了c語言的接口;libvlccore是VLC 媒體播放器管理線程,VLC 媒體播放器的運行核稱之爲libvlccore;vlc把編解碼和格式解析的支持設計成了插件的形式,因此還必需要帶上vlc的plugins目錄裏的插件。網絡

引用位置

         把libvlc.dll和libvlccore.dll和plugins文件夾放在」 bin/Debug」下。架構

封裝方式

      常規封裝

因爲兩個動態連接庫是用C語言寫的。因此還要用C#封裝一下非託管方法。例如:框架

// 建立一個libvlc實例,它是引用計數的
[DllImport("libvlc", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
private static extern IntPtr libvlc_new(int argc, IntPtr argv);

須要引用下面兩個命名空間:ide

using System.Runtime.InteropServices;
using System.Security;

      聲明式封裝

還有一種是聲明式封裝引用,值得借鑑。函數

  1. 首先實現一個自定義特性類,定義其屬性用法爲委託方式。
  2. 定義一個抽象類,例如:VlcInteropsManager,使用windows API調用VLC的DLL
[AttributeUsage(AttributeTargets.Delegate, AllowMultiple = false)]
    internal sealed class LibVlcFunctionAttribute : Attribute
    {
        public string FunctionName { get; private set; }
 
        public LibVlcFunctionAttribute(string functionName)
        {
            FunctionName = functionName;
        }
    }
[DllImport("kernel32", SetLastError = true)]
 public static extern IntPtr LoadLibrary(string lpFileName);

例如:

var libVlcDllPath = Path.Combine(dynamicLinkLibrariesPath.FullName, "libvlc.dll");//獲取libvlc的路徑
            if (!File.Exists(libVlcDllPath))
                throw new FileNotFoundException();
            myLibVlcDllHandle = Win32Interops.LoadLibrary(libVlcDllPath);//獲取libvlc的指針
            if (myLibVlcDllHandle == IntPtr.Zero)
                throw new Win32Exception(Marshal.GetLastWin32Error());

根據自定義特性名稱(實際爲libvlc方法名稱)獲取libvlc非託管方法

[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);// hModule爲LoadLibrary 方法獲取的libvlc的指針,lpProcName爲方法名稱

例如:

internal T GetInteropDelegate<T>()
        {
            string vlcFunctionName = null;
            try
            {
                var attrs = typeof(T).GetCustomAttributes(typeof(LibVlcFunctionAttribute), false);
                if (attrs.Length == 0)
                    throw new Exception("Could not find the LibVlcFunctionAttribute.");
                var attr = (LibVlcFunctionAttribute)attrs[0];
                vlcFunctionName = attr.FunctionName;
                procAddress = Win32Interops.GetProcAddress(myLibVlcCoreDllHandle, attr.FunctionName);//獲取非託管函數的指針
                  if (procAddress == IntPtr.Zero)
                        throw new Win32Exception();
       }
                var delegateForFunctionPointer = Marshal.GetDelegateForFunctionPointer(procAddress, typeof(T));// 由於定義了自定義特性的屬性爲委託,因此需將非託管函數指針轉換爲委託
        return (T)Convert.ChangeType(delegateForFunctionPointer, typeof(T), null);
            }
            catch (Win32Exception e)
            {
                throw new MissingMethodException(String.Format("The address of the function '{0}' does not exist in libvlc library.", vlcFunctionName), e);
            }
        }

釋放指針

[DllImport("kernel32", SetLastError = true)]
public static extern bool FreeLibrary(IntPtr hModule);

例如:

if (myLibVlcDllHandle != IntPtr.Zero)
            {
                Win32Interops.FreeLibrary(myLibVlcDllHandle);
                myLibVlcDllHandle = IntPtr.Zero;
            }// myLibVlcDllHandle爲LoadLibrary獲取的指針
  1. 定義委託方法

例如:定義libvlc中獲取版本信息方法」 libvlc_get_version」。

[LibVlcFunction("libvlc_get_version")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr GetVersion();
  1. 繼承抽象類VlcInteropsManager調用委託方法
public void GetVersion ()
 {
    return GetInteropDelegate<GetVersion>().Invoke().ToStringAnsi();
  }

播放視頻的經常使用方法

// 建立一個libvlc實例,它是引用計數的
private static extern IntPtr libvlc_new(int argc, IntPtr argv);
 // 釋放libvlc實例
public static extern void libvlc_release(IntPtr libvlc_instance);
//獲取版本號
public static extern String libvlc_get_version();
// 從視頻來源(例如Url)構建一個libvlc_meida
private static extern IntPtr libvlc_media_new_location(IntPtr libvlc_instance, IntPtr path);
// 從本地文件路徑構建一個libvlc_media
private static extern IntPtr libvlc_media_new_path(IntPtr libvlc_instance, IntPtr path);
//釋放media
public static extern void libvlc_media_release(IntPtr libvlc_media_inst);
 // 建立libvlc_media_player(播放核心)
public static extern IntPtr libvlc_media_player_new(IntPtr libvlc_instance);
// 將視頻(libvlc_media)綁定到播放器上
public static extern void libvlc_media_player_set_media(IntPtr libvlc_media_player, IntPtr libvlc_media);
// 設置圖像輸出的窗口
public static extern void libvlc_media_player_set_hwnd(IntPtr libvlc_mediaplayer, Int32 drawable);
//播放
public static extern void libvlc_media_player_play(IntPtr libvlc_mediaplayer);
//暫停
public static extern void libvlc_media_player_pause(IntPtr libvlc_mediaplayer);
//中止
public static extern void libvlc_media_player_stop(IntPtr libvlc_mediaplayer);
// 解析視頻資源的媒體信息(如時長等)
public static extern void libvlc_media_parse(IntPtr libvlc_media);
// 返回視頻的時長(必須先調用libvlc_media_parse以後,該函數纔會生效)
public static extern Int64 libvlc_media_get_duration(IntPtr libvlc_media);
// 當前播放的時間
public static extern Int64 libvlc_media_player_get_time(IntPtr libvlc_mediaplayer);
// 設置播放位置(拖動)
public static extern void libvlc_media_player_set_time(IntPtr libvlc_mediaplayer, Int64 time);
//釋放meidiaPlayer
public static extern void libvlc_media_player_release(IntPtr libvlc_mediaplayer);

方法具體含義請參看其c語言的封裝庫具體內容。

播放調用流程

播放本地文件

libvlc_new=>libvlc_media_player_new=>libvlc_media_player_set_hwnd=>libvlc_media_new_path=>libvlc_media_parse=>libvlc_media_player_set_media=>libvlc_media_release=>libvlc_media_player_play=>libvlc_media_player_stop=>libvlc_media_player_release=>libvlc_media_release=>libvlc_release

播放URL

libvlc_new=>libvlc_media_player_new=>libvlc_media_player_set_hwnd=>libvlc_media_new_location=>libvlc_media_parse=>libvlc_media_player_set_media=>libvlc_media_release=>libvlc_media_player_play=>libvlc_media_player_stop=>libvlc_media_player_release=>libvlc_media_release=>libvlc_release

開啓硬件加速

開啓硬件加速命令爲:avcodec-hw=any

方法爲:libvlc_media_add_option (IntPtr mediaInstance, IntPtr mrl);

var handle = GCHandle.Alloc(Encoding.UTF8.GetBytes(option), GCHandleType.Pinned);//option爲命令
//兩個參數的獲取
mrl= handle.AddrOfPinnedObject();
mediaInstance= libvlc_new(null);

Vlc.DotNet

Vlc.DotNet是一個對VLC封裝的C#開源架構。使用Vlc.DotNet框架能夠播放本地文件和URL網絡視頻。在研究過程當中,目前還爲發現關於解碼方面的封裝應用。

引用位置

         Vlc.DotNet框架包含5個DLL,以下

名稱

版本號

Vlc.DotNet.Core.dll

2014.10.15.0

Vlc.DotNet.Core.Interops.dll

2014.10.15.0

Vlc.DotNet.Forms.dll

2014.10.15.0

Vlc.DotNet.Silverlight.dll

2014.10.15.0

Vlc.DotNet.Wpf.dll

2014.10.15.0

根據你作的程序是用什麼寫的來添加不一樣的引用,例如:你使用WPF來作的程序就選擇Vlc.DotNet.Wpf.dll、Vlc.DotNet.Forms.dll、Vlc.DotNet.Core.dll、Vlc.DotNet.Core.Interops.dll添加到項目中並引用。

把libvlc.dll和libvlccore.dll和plugins文件夾放在」 bin/Debug」下。

調用方法(WPF)

具體示例能夠參見Vlc.DotNet自帶的demo,簡要說明以下:

  1. XAML中添加命名空間的引用:

xmlns:wpf="clr-namespace:Vlc.DotNet.Wpf;assembly=Vlc.DotNet.Wpf"

  1. 在<Grid></Grid>標籤中添加

<wpf:VlcControl  Grid.Row="0" Grid.Column="0" x:Name="myControl1"/>

  1. 在後臺cs代碼中註冊加載DLL事件
myControl1.VlcLibDirectoryNeeded += OnVlcControlNeedsLibDirectory;

 

private void OnVlcControlNeedsLibDirectory(object sender, Forms.VlcLibDirectoryNeededEventArgs e)
        {
            var currentAssembly = Assembly.GetEntryAssembly();
            var currentDirectory = new FileInfo(currentAssembly.Location).DirectoryName;
            if (currentDirectory == null)
                return;
            e.VlcLibDirectory = new DirectoryInfo(Path.Combine(currentDirectory, @"VLC\"));
        }
  1. 播放

播放本地文件

myControl1.MediaPlayer.Play(new FileInfo(Environment.CurrentDirectory + @"\video\wow" + i + @".mp4"), null);

播放URI文件

myControl1..MediaPlayer.Play

(new Uri("rtsp://192.168.1.181"),new string[] { "avcodec-hw=any" });//開啓硬件加速

  1. 中止播放

myControl1.MediaPlayer.Stop();

其餘功能可參見開源框架源碼。

VLC客戶端做爲流媒體轉發服務端

VLC客戶端能夠做爲流媒體轉發服務端。以下圖因此操做:

  1. 打開VLC客戶端

 

  1. 選則【流】選項

 

  1. 選擇【網絡】選項卡,設置網絡協議,點擊【串流】。

 

  1. 確認流輸出媒體源,點擊【Next】。

 

  1. 設置流輸出目標,選擇輸出方式。本例選擇HTTP方式。而後點擊【添加】

 

  1. 選擇流輸出的端口號和路徑訪問名稱,而後點擊【Next】。

 

  1. 選擇輸出流的轉碼格式,並點擊工具圖標按鈕設置具體參數。

 

  1. 輸出流

 

  1. 轉發流,標題上顯示【流】字樣,即代表流媒體開始轉發

 

  1. 流顯示

顯示的方式使用HTML5Video標籤,Html5的Video標籤只支持三種視頻格式,OGG,WebM,MP4,通過測試,video標籤只能播放VLC轉發的OGG格式視頻。

創建一個html文件:

<html>
<head>
    <title>HTTP Live Streaming Player</title>
</head>
<body>
    <video id="vplayer"
        height="300"
        width="400"
        controls="controls"
        src="http://192.168.1.5:8880/ttl" />
</body>
</html>

 

也可轉發本地視頻文件流。

VLC_SDK實現流媒體轉碼廣播功能

上圖用客戶端實現的功能,能夠用SDK實現。

其C語言庫爲libvlc_vlm.h。在這個文件中包含了「推流器」廣播和點播的控制功能。

我把在c#中封裝的實現推流器功能核心SDK方法概括以下:

//添加廣播
[LibVlcFunction("libvlc_vlm_add_broadcast")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate int VlmAddBroadcast(IntPtr libvlc_instance, IntPtr name, IntPtr input, IntPtr output, int optionNumber, string[] options, bool isEnabled, bool isLoop);
//播放廣播
[LibVlcFunction("libvlc_vlm_play_media")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate int PlayVlmMedia(IntPtr libvlcInstance, IntPtr broadcastName); 
//關閉廣播
[LibVlcFunction("libvlc_vlm_stop_media")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate int StopVlmMedia(IntPtr libvlcInstance, IntPtr broadcastName);
//刪除廣播
[LibVlcFunction("libvlc_vlm_del_media")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void VlmDelMedia(IntPtr libvlcInstance,IntPtr mediaName);
//釋放廣播示例
[LibVlcFunction("libvlc_vlm_release")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void ReleaseVlm(IntPtr libvlcInstance);

在賦值參數時須要注意自定義廣播名稱、視頻源地址和目標地址的字符串轉換爲指針時須要作以下操做:

var handleName = GCHandle.Alloc(Encoding.UTF8.GetBytes(name), GCHandleType.Pinned);//廣播名稱
var handleInput = GCHandle.Alloc(Encoding.UTF8.GetBytes(input), GCHandleType.Pinned);//視頻源
 var handleOutput = GCHandle.Alloc(Encoding.UTF8.GetBytes(output), GCHandleType.Pinned);//目標

 

具體實現方式以下:

//播放廣播
public bool PlayVlmMedia(string broadcastName, string input, string output, int optionNumber, string[] options, bool isEnabled, bool isLoop)
   {
            bool result = false;
            if (_manager.AddVlmBroadcast(broadcastName, input, output, optionNumber, options, isEnabled, isLoop))//添加廣播地址
                result = _manager.PlayVlmMedia(broadcastName);//播放
            return result;
        }
//中止播放廣播
public bool StopVlmMedia(string broadcastName)
{
        return _manager.StopVlmMedia(broadcastName);
}

 

傳入參數:

String mediaName = "IPCTest";//廣播名稱
String url = "rtsp://192.168.1.181";//源地址//string sout = "#transcode{vcodec=h264,vb=800,scale=1,acodec=mpga,ab=128,channels=2,samplerate=44100}:http{mux=ts,dst=:7777/}";//轉碼目標格式H264格式
string sout = "#transcode{vcodec=theo,vb=1600,scale=1,acodec=vorb,ab=128,channels=2,samplerate=44100}:http{mux=ogg,dst=:8880/ttl}";//轉碼格式爲ogg,端口號爲8880,標識爲ttl
string[] options = {"screen-top=0", 
                            "screen-left=0",
                            "screen-width=640", 
                            "screen-height=480", 
                            "screen-fps=10"};//碼流參數
 
            _vlcControl.PlayVlmMedia(mediaName, url, sout, 5, options, true, false);//轉碼後廣播

 

VLC_SDK實現視頻流回調顯示功能

實現實時流回調的流程與普通播放方式相同,惟一區別在於,普通播放方式須要指定播放窗體,實時流回調不需指定播放窗體,只需在回調方法中獲取視頻流數據而後用自定義方式顯示出來。具體操做以下:

首先封裝三個委託方法:

//鎖定一個圖片緩衝區
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]//聲明對c語言的約束
public delegate IntPtr VideoLockCB(IntPtr opaque, IntPtr planes);
//解鎖一個圖片緩衝區
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void VideoUnlockCB(IntPtr opaque, IntPtr picture, IntPtr planes);
//顯示圖片
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void VideoDisplayCB(IntPtr opaque, IntPtr picture);

其次封裝兩個方法:

//設置解碼後視頻格式
[LibVlcFunction("libvlc_video_set_format")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void VideoSetFormat(IntPtr mediaPlayerInstance, IntPtr chroma, UInt32 width, UInt32 height, UInt32 pitch);
//設置實時流視頻解析回調
[LibVlcFunction("libvlc_video_set_callbacks")]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void VideoSetCallBacks(IntPtr mediaPlayInstance, VideoLockCB lockCB, VideoUnlockCB unlockCB, VideoDisplayCB displayCB, IntPtr opaque);

具體調用過程以下:

  1. 建立一個libvlc實例
  2. 建立一個mediaPlayer實例
  3. 建立一個media實例
IntPtr vlcInstance=libvlc_new();//
IntPtr mediaPlayerInstance=libvlc_media_player_new(vlcInstance);

建立實例經常使用兩種方式,一種是根據本地文件路徑。

IntPtr mediaInstance= libvlc_media_new_path(vlcInstance,"本地文件地址");

一種是根據網絡地址

IntPtr mediaInstance= libvlc_media_new_location(vlcInstance,"網絡地址");
  1. 解析視頻源信息

經實例運行驗證,不是用此方法普通方式和回調方式的視頻流也能夠播放。

libvlc_media_parse(mediaInstance);
  1. 將視頻源綁定到播放器上
  2. 釋放視頻源
  3. 設置解碼後視頻格式
  4. 設置實時流視頻解析
libvlc_media_player_set_media(mediaPlayerInstance, mediaInstance);
libvlc_media_release(IntPtr libvlc_media_inst);
VideoSetFormat(_mediaPlayer, "RV32", width, height, pitch);//RGBA 顏色不對,YUYV顯示錯誤,I420程序跳出

預先設置的變量

        private const int _width = 1920;
        private const int _height = 1080;
        private const int _pixelBytes = 4;
        private const int _pitch = _width * _pixelBytes;
        private IntPtr _buff = IntPtr.Zero;

初始化三個委託

        private VideoLockCB _videoLockCB;
        private VideoUnlockCB _videoUnlockCB;
        private VideoDisplayCB _videoDisplayCB;
 
        if (_videoLockCB == null)
            _videoLockCB = new VideoLockCB(VideoLockCallBack);
        if (_videoUnlockCB == null)
              _videoUnlockCB = new VideoUnlockCB(VideoUnlockCallBack);
        if (_videoDisplayCB == null)
            _videoDisplayCB = new VideoDisplayCB(VideoDiplayCallBack);

設置回調

VideoSetCallBacks(_mediaPlayer, lockCB, unlockCB, displayCB, IntPtr.Zero);

使用回調前前先定義一個線程鎖。

        bool obj = false;
        private void Lock()
        {
            obj = true;
        }
        private void Unlock()
        {
            obj = false;
        }
        private bool Islock()
        {
            return obj;
        }

鎖定一個圖片緩衝區時先鎖定,而後初始化這個緩衝區。

private IntPtr VideoLockCallBack(IntPtr opaque, IntPtr planes)
        {
            Lock();
            _buff = Marshal.AllocHGlobal(_pitch * _height);
            Marshal.WriteIntPtr(planes, _buff);//初始化
            return IntPtr.Zero;
        }

顯示圖片的委託中進行的處理,我選擇瞭解析爲Bitmap,而後顯示在pictureBox上。

if (Islock())
            {
                if (DateTime.Now.Subtract(_start).TotalSeconds > 2)
                {
                    using (Bitmap bmp = new Bitmap(_width, _height, PixelFormat.Format32bppPArgb))
                    {
                        Rectangle rect = new Rectangle(0, 0, _width, _height);
                        BitmapData bp = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
                        CopyMemory(bp.Scan0, _buff, (uint)(_height * _pitch));
                        bmp.UnlockBits(bp);
                        //bmp.Save("c:\\vlc.bmp");
                        Bitmap bitmap = (Bitmap)bmp.Clone();
                        if (this.pictureBox1.Image != null)
                            this.pictureBox1.Image.Dispose();
                        this.pictureBox1.Image = bitmap;
                    }
                }
            }

此委託中用到了一個winapi函數

[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
private static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);

解鎖圖片緩衝區

private void VideoUnlockCallBack(IntPtr opaque, IntPtr picture, IntPtr planes)
        {
            Marshal.FreeHGlobal(_buff);//釋放緩衝區
            Unlock();
        }
相關文章
相關標籤/搜索