ffplay自定义系列
第一章 自定义播放器接口
第二章 倍速播放
第三章 dxva2硬解渲染
第四章 提供C#接口(本章)
第五章 制作wpf播放器
前言
因为曾经的wpf项目涉及到多种播放功能,拉流、点播、摄像头预览、音频测试、视频剪辑等,将ffplay的接口封装给C#调用实现了那些功能。最近自己新版本的播放模块也做了C#的封装,虽然现在不做C#项目了,但是留着以备不时之需。下面将对dll的接口和功能以及使用方法做一些说明。
一、接口
由本身的c接口设计理念就是面向对象多实例化的,所以直接将播放器封装成一个对象就可以了。dllimport的部分就省略了,下面只展示对象接口。
播放对象
namespace AC
{
/************************************************************************
* @Project: AC.Play
* @Decription: 视频播放器
* 这是一个功能完整的支持主流常见媒体格式且支持多路播放的音视频播放器。
* 其播放主体为ffmpeg+sdl,本项目是通过对ffplay的源码修改拓展而成。
* 目前是支持跨平台,在Windows和Linux均可以正常使用。
* 主要更新特性
* v1.0:提供基本的播放、控制、多实例化
* v1.1:支持精准定位,播放周期事件
* v1.2:支持windows摄像头预览、倍速播放
* v1.3:支持dxva2硬解渲染,以及设置解码器
* @Verision: v1.3.6
* @Author: Xin Nie
* @Create: 2018/11/27 13:34:00
* @LastUpdate: 2022/5/21 23:40:00
************************************************************************
* Copyright @ 2022. All rights reserved.
************************************************************************/
/// <summary>
/// 播放器对象
/// </summary>
public class Play
{
/// <summary>
/// 播放开始事件
/// 调用Start后触发此事件,出现错误也可能不触发
/// </summary>
public event EventHandler<PlayStartedEventArgs> Started;
/// <summary>
/// 播放停止中事件
/// 播放开始失败、播放到结尾、调用Stop,会触发此事件
/// </summary>
public event EventHandler<PlayStoppingEventArgs> Stopping;
/// <summary>
/// 播放停止事件
/// 调用Stop、连续调用Start,会触发此事件
/// </summary>
public event EventHandler Stopped;
/// <summary>
/// 游标时间改变事件
/// 可用于获取当前播放时间
/// </summary>
public event EventHandler<PlayCursorTimeChangedEventArgs> CursorTimeChanged;
/// <summary>
/// 视频渲染事件
/// 自定义视频渲染时可使用此事件
/// </summary>
public event EventHandler<PlayDisplayEventArgs> Display;
/// <summary>
/// 播放窗口句柄
/// 设置后将渲染到对应窗口中
/// </summary>
public IntPtr Window {get;set;}
// <summary>
/// 硬件加速类型
/// 与VCodecName方法冲突,指定VideoCodecName后,此选项失效。
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="value">硬件加速类型</param>
public HardwareAccelerateType HardwareAccelerateType{get;set;}
/// <summary>
/// 指定视频解码器,可以设置硬解码器,但不可与HardwareAccelerateType同时设置。
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="codec">解码器名称,为ffmpeg的codec名称,通过ffmpeg -decoders命令查看</param>
public string VideoCodecName{get;set;}
/// <summary>
/// 是否循环播放
/// </summary>
public bool IsLoop{get;set;}
/// <summary>
/// 是否暂停
/// 调用Start后自动设为false
/// </summary>
public bool IsPause{get;set;}
/// <summary>
/// 静音
/// </summary>
public bool IsMute{get;set;}
/// <summary>
/// 禁用视频
/// </summary>
public bool IsVideoDisabled{get;set;}
/// <summary>
/// 禁用音频
/// </summary>
public bool IsAudioDisabled{get;set;}
/// <summary>
/// 禁用精准定位
/// </summary>
public bool IsPreciseSeekDisabled{get;set;}
/// <summary>
/// 时钟同步类型
/// </summary>
public ClockSyncType ClockSyncType{get;set;}
/// <summary>
/// 音量,0-100
/// </summary>
public int Volume{get;set;}
/// <summary>
/// 播放倍速,0.5-2
/// </summary>
public double Speed{get;set;}
/// <summary>
/// 开始播放
/// 可以重复调用,调用后会先停止上一个播放,再开始。
/// </summary>
/// <param name="url">播放url:文件路径、rtmp、rtsp、http、摄像头名称
/// 当前版本只支持摄像头的简单预览,无法设置格式和分辨率以及同名摄像头区分</param>
/// <param name="startTime">播放时定位的时间,0则是从头开始播放,对于可定位的视频源有效</param>
public void Start(string url, double startTime);
/// <summary>
/// 停止播放
/// </summary>
public void Stop();
/// <summary>
/// 定位
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="time">时间,单位秒</param>
public void Seek(double time);
}
}
为了确保后面示例相对完整,给出其他相关的对象的定义。
/// <summary>
/// 像素格式
/// </summary>
public enum PixelFormat
{
None = -1,
/// <summary>
/// 也叫i420或ffmpeg的yuv420p
/// </summary>
Yu12,
/// <summary>
/// uv交叉排列的420
/// </summary>
Nv12,
/// <summary>
/// rgb 8:8:8
/// </summary>
Rgb24,
/// <summary>
/// argb 8:8:8:8
/// </summary>
Argb32,
/// <summary>
/// bgra 8:8:8:8
/// </summary>
Bgra32,
/// <summary>
/// yuyv422
/// </summary>
Yuy2,
/// <summary>
/// dxva2解码格式,通常为data[3]是surface对象。
/// </summary>
Dxva2Vld
}
/// <summary>
/// 硬件加速选项
/// </summary>
public enum HardwareAccelerateType
{
Disabled,
Auto,
//使用dxva解码,仅在Windows有效,成功启动:started、display事件的pixformat为DXVA2_VLD,render事件的data[3]为d3d9的surface对象。
Dxva
};
/// <summary>
/// 停止原因
/// </summary>
public enum StopReason
{
None,
//播放结束
Reachend,
//调用了stop方法
Usercall,
//出现错误
Error,
}
/// <summary>
/// 时钟同步
/// </summary>
public enum ClockSyncType
{
//同步到音频
Audio,
//同步到视频频
Video,
//同步到外部时钟
External,
}
/// <summary>
/// 播放开始事件参数
/// </summary>
public class PlayStartedEventArgs : EventArgs
{
/// <summary>
/// 当前视频像素格式
/// 可以设置,对Display事件生效
/// 格式为NONE时,可能不存在视频流、禁用视频流、视频流打开失败、格式不支持。
/// 当格式为DXVA2_VLD时不可设置。
/// </summary>
public PixelFormat Format { get; set; }
/// <summary>
/// 视频宽
/// </summary>
public int Width { get; internal set; }
/// <summary>
/// 视频高
/// </summary>
public int Height { get; internal set; }
/// <summary>
/// 视频时长
/// </summary>
public double Duration { get; internal set; }
}
/// <summary>
/// 播放停止中事件参数
/// </summary>
public class PlayStoppingEventArgs : EventArgs
{
/// <summary>
/// 停止原因
/// </summary>
public StopReason StopReason { get; internal set; }
}
/// <summary>
/// 游标时间改变事件参数
/// </summary>
public class PlayCursorTimeChangedEventArgs : EventArgs
{
/// <summary>
/// 当前播放时间
/// </summary>
public double Time { get; internal set; }
}
/// <summary>
/// 视频渲染事件参数
/// </summary>
public class PlayDisplayEventArgs : EventArgs
{
/// <summary>
/// 视频数据像素格式
/// 当格式为DXVA2_VLD时Data[3]为d3d9的Surface对象。
/// </summary>
public PixelFormat Format { get; set; }
/// <summary>
/// 视频宽
/// </summary>
public int Width { get; internal set; }
/// <summary>
/// 视频高
/// </summary>
public int Height { get; internal set; }
/// <summary>
/// 视频数据
/// 参考ffmpeg avframe.data
/// </summary>
public IntPtr[] Data { get; internal set; }
/// <summary>
/// 一行数据大小
/// 参考ffmpeg avframe.linesize
/// </summary>
public int[] Linesize { get; internal set; }
/// <summary>
/// 是否截断操作
/// 如果设置了窗口句柄,为true时将不会渲染到窗口上
/// </summary>
public bool IsHandled { get; set; }
}
二、使用示例
1、简单播放
using AC;
Play play = new Play();
//窗口句柄
IntPtr hwnd=IntPtr.Zero;
double duration;
double cursorTime;
play.Started += (s, e) =>
{
//获取视频时长
duration = e.Duration;
};
play.CursorTimeChanged += (s, e) =>
{
//获取当前播放时间
cursorTime = e.Time;
};
//设置播放窗口
play.Window=hwnd;
//开始播放
play.Start("test.mp4");
//消息循环,等待结束...
//停止播放
play.Stop();
//销毁播放器
play.Dispose();
2、自定义渲染
本示例通过d3d渲染,具体渲染方法可以参考《C# wpf 使用d3d渲染yuv视频数据》。
using AC;
//播放器对象
Play play = new Play();
//用于渲染的对象
D3DImageSource d3d = null;
double duration;
double cursorTime;
//注册事件
play.Started += (s, e) =>
{
Dispatcher.Invoke(() =>
{
try
{
//设置渲染时强制输出此格式,此属性只对自定义渲染有效。
e.Format = AC.PixelFormat.Yu12;
//初始化d3d,像素格式需要和播发器输出格式相同,此处FrameFormat.YV12对应AC.PixelFormat.Yu12
d3d = new D3DImageSource(e.width, e.height, FrameFormat.YV12);
//给Image的Source赋值。
Img_Diplay.Source = d3d;
//获取视频时长
duration = e.Duration;
}
catch (Exception E)
{
MessageBox.Show(E.Message);
}
});
};
//播放器的渲染回调
play.Display+=(s,e) =>
{
Dispatcher.Invoke(new Action(() =>
{
try
{ //渲染
d3d.Present(e.Data, e.Linesize);
}
catch (Exception E)
{
MessageBox.Show(E.Message);
}
}));
};
play.CursorTimeChanged += (s, e) =>
{
//获取当前播放时间
cursorTime = e.Time;
};
play.Start("输入url");
//消息循环,等待结束....
//停止播放
play.Stop();
//销毁播放器
play.Dispose();
3、播放流数据
文件流
using AC;
Play play = new Play();
var fs=new FileStream(FileNameFullPath, FileMode.Open, FileAccess.Read);
play.Start(fs);
//结束销毁资源
play.Dispose();
fs.Dispose();
网络http流
using AC;
Play play = new Play();
HttpClient http = new HttpClient();
var httpResponseMessage = await http.GetAsync("https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv");/
var stream = await httpResponseMessage.Content.ReadAsStreamAsync();
play.Start(stream);
//结束销毁资源
play.Dispose();
stream.Dispose();
http.Dispose();
4、音频播放器
由于ffmpeg支持的音视频格式比较全面,所以完全可以当成音频播放器使用。不过当前版本未开放音频数据回调,以及输出频谱等功能,只能当成简单播放器使用。
using AC;
Play play = new Play();
//禁用视频
play.IsVideoDisabled = true;
play.start("test.mp3");
//消息循环,等待结束....
//停止播放
play.Stop();
//销毁播放器
play.Dispose();
5、控制播放
在播放过程中可以通过一些属性进行播放控制。
(1)循环播放
play.IsLoop=true;
(2)暂停、继续
play.IsPaused=true;
(3)静音
play.IsMuted=true;
(4)音量
play.Volume=70;
(5)倍速
play.Speed=1.5;
(6)定位
play.Seek(120);
(7)硬件加速
play.HardwareAccelerateType=HardwareAccelerateType.Dxva;
(8)编码器设置
play.VideoCodecName="h264_cuvid";
(9)时钟同步类型设置
play.ClockSyncType=ClockSyncType.Audio;
三、下载
包含完整的dll库(x86、x64)ffmpeg版本为4.3,vs2022 wpf demo项目。包含include、lib、bin,C++也可以使用,资源如下:
注:不包含播放器c语言源码,请根据需要下载。
https://download.csdn.net/download/u013113678/85368148
总结
以上就是今天要讲的内容,提供完善的封装是可以方便使用的,底层的接口虽然更加灵活但是直接使用的开发体验其实不太好。总的来说这是一个完整的播放器模块,所有接口都经过了测试,适用于音视频客户端的开发,无论是实时流还是点播或者本地本件的播放都有很好的支持,而且由于支持硬解渲染,可以适用于多路高清实时流的渲染。
附录
1、dll的import代码
namespace AC
{
//基于fflay实现的自定义播放器,实现原理参考:https://blog.csdn.net/u013113678/article/details/114266843
public class CLib
{
const string dll = @"AC.dll";
/// <summary>
/// 像素格式
/// </summary>
public enum ACPixelFormat
{
AC_PIXELFORMAT_NONE = -1,
/// <summary>
/// 也叫i420或ffmpeg的yuv420p
/// </summary>
AC_PIXELFORMAT_YU12,
/// <summary>
/// uv交叉排列的420
/// </summary>
AC_PIXELFORMAT_NV12,
/// <summary>
/// RGB 8:8:8
/// </summary>
AC_PIXELFORMAT_RGB24,
/// <summary>
/// ARGB 8:8:8:8
/// </summary>
AC_PIXELFORMAT_ARGB32,
/// <summary>
/// BGRA 8:8:8:8
/// </summary>
AC_PIXELFORMAT_BGRA32,
/// <summary>
/// yuyv422
/// </summary>
AC_PIXELFORMAT_YUY2,
/// <summary>
/// dxva2解码格式,通常为data[3]是surface对象。
/// </summary>
AC_PIXELFORMAT_DXVA2_VLD
}
/// <summary>
/// 硬件加速选项
/// </summary>
public enum ACHardwareAccelerateType
{
AC_HARDWAREACCELERATETYPE_DISABLED,
AC_HARDWAREACCELERATETYPE_AUTO,
//使用dxva解码,仅在Windows有效,成功启动:started、display事件的pixformat为AC_PIXELFORMAT_DXVA2_VLD,render事件的data[3]为d3d9的surface对象。
AC_HARDWAREACCELERATETYPE_DXVA
}
;
/// <summary>
/// 停止原因
/// </summary>
public enum ACStopReason
{
AC_STOPREASON_NONE,
//播放结束
AC_STOPREASON_REACHEND,
//调用了stop方法
AC_STOPREASON_USERCALL,
//出现错误
AC_STOPREASON_ERROR,
};
/// <summary>
/// 时钟同步
/// </summary>
public enum ACClockSyncType
{
//同步到音频
AC_CLOCKSYNCTYPE_AUDIO,
//同步到视频频
AC_CLOCKSYNCTYPE_VIDEO,
//同步到外部时钟
AC_CLOCKSYNCTYPE_EXTERNAL,
};
/// <summary>
/// 回调方法
/// </summary>
/// <param name="play">播放器对象</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ACPlayCallback(IntPtr play);
/// <summary>
/// 开始播放时回调方法
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="format">像素格式,当前视频的像素格式,可以设置想要的像素格式,在渲染回调方法中使用,比如:*format=AC_PIXELFORMAT_RGB24
/// 格式为AC_PIXELFORMAT_NONE时,可能是不存在视频流、视频流打开失败、格式不支持。
/// 当格式为AC_PIXELFORMAT_DXVA2_VLD时不可设置。
/// </param>
/// <param name="width">视频的宽</param>
/// <param name="height">视频的高</param>
/// <param name="duration">视频时长</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ACPlayStartedCallback(IntPtr play, ref ACPixelFormat format, int width, int height, double duration);
/// <summary>
/// 播放将要停止的回调方法
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="stopReason">停止原因</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ACPlayStoppingCallback(IntPtr play, ACStopReason stopReason);
/// <summary>
/// 播放时间改变的回调方法
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="time">当前时间,单位秒</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ACPlayCursorTimeChangedCallback(IntPtr play, double time);
/// <summary>
/// 渲染回调方法
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="data">视频数据</param>
/// <param name="linesize">一行大小</param>
/// <param name="width">宽</param>
/// <param name="height">高</param>
/// <param name="format">像素格式</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ACPlayDisplayCallback (IntPtr play, [MarshalAs(UnmanagedType.LPArray, SizeConst = 8)] IntPtr[] data, [MarshalAs(UnmanagedType.LPArray, SizeConst = 8)] int[] linesize, int width, int height, ACPixelFormat format,ref int isHandled);
/// <summary>
/// 创建播放器
/// </summary>
/// <returns>播放器对象</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static IntPtr ac_play_create();
/// <summary>
/// 销毁播放器
/// </summary>
/// <param name="p">播放器对象</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_destroy(IntPtr p);
/// <summary>
/// 设置播放窗口,仅在Windows有效
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="hwnd">窗口句柄</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setWindow(IntPtr play, IntPtr hwnd);
/// <summary>
/// 获取播放窗口,仅在Windows有效
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>窗口句柄</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static IntPtr ac_play_getWindow(IntPtr play);
/// <summary>
/// 设置硬件加速类型
/// 与ac_play_setVideoCodecName方法冲突,指定编码器后,此选项失效。
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="value">硬件加速类型</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setHardwareAccelerateType(IntPtr play, ACHardwareAccelerateType value);
/// <summary>
/// 获取硬件加速类型
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>硬件加速类型</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static ACHardwareAccelerateType ac_play_getHardwareAccelerateType(IntPtr play);
/// <summary>
/// 设置指定视频解码器,可以设置硬解码器,但不可与ac_play_setHardwareAccelerateType方法同时设置。
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="codec">解码器名称,为ffmpeg的codec名称,通过ffmpeg -decoders命令查看</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setVideoCodecName(IntPtr play,string codec );
/// <summary>
/// 获取视频解码器
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>解码器名称</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static string ac_play_getVideoCodecName(IntPtr play);
/// <summary>
/// 设置是否打印格式信息
/// 格式信息将打印在控制台
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="isDump">是否打印,1 or 0</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setIsDumpFormat(IntPtr play, int isDump);
/// <summary>
/// 获取是否打印格式信息
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>否打印,1 or 0</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static int ac_play_getIsDumpFormat(IntPtr play);
/// <summary>
/// 开始播放
/// 可以重复调用,调用后会先停止上一个播放,再开始。
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="url">播放url:文件路径、rtmp、rtsp、http、摄像头名称。
/// 当前版本只支持摄像头的简单预览,无法设置格式和分辨率以及同名摄像头区分</param>
/// <param name="startTime">播放时定位的时间,0则是从头开始播放,对于可定位的视频源有效</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_start(IntPtr play, string url, double startTime);
/// <summary>
/// 停止播放
/// </summary>
/// <param name="play">播放器对象</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_stop(IntPtr play);
/// <summary>
/// 是否循环播放
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="value">是否循环播放,1 or 0</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setIsLoop(IntPtr play, int value);
/// <summary>
/// 获取是否循环播放
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>是否循环播放,1 or 0</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static int ac_play_getIsLoop(IntPtr play);
/// <summary>
/// 设置暂停
/// 调用start后自动设为false
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="isPaused">是否暂停,1 or 0</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setIsPause(IntPtr play, int isPaused);
/// <summary>
/// 获取是否暂停
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>是否暂停,1 or 0</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static int ac_play_getIsPause(IntPtr play);
/// <summary>
/// 静音
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="isMuted">是否静音,1 or 0</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setIsMute(IntPtr play, int isMuted);
/// <summary>
/// 获取是否静音
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>是否静音,1 or 0</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static int ac_play_getIsMute(IntPtr play);
/// <summary>
/// 禁用视频
/// 只有start前设置生效
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="isMuted">是否禁用,1 or 0</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setIsVideoDisabled(IntPtr play, int isDisable);
/// <summary>
/// 获取是否禁用视频
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>是否禁用,1 or 0</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static int ac_play_getIsVideoDisabled(IntPtr play);
/// <summary>
/// 禁用音频
/// 只有start前设置生效
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="isMuted">是否禁用,1 or 0</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setIsAudioDisabled(IntPtr play, int isDisable);
/// <summary>
/// 获取是否禁用音频
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>是否禁用,1 or 0</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static int ac_play_getIsAudioDisabled(IntPtr play);
/// <summary>
/// 是否禁用精准定位
/// 默认非禁用
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="time">是否禁用,1 or 0</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setIsPreciseSeekDisabled(IntPtr play, int isDisable);
/// <summary>
/// 获取是否禁用精准定位
/// 默认非禁用
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>是否禁用,1 or 0</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static int ac_play_getIsPreciseSeekDisabled(IntPtr play);
/// <summary>
/// 设置时钟同步
/// 默认同步到音频
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="type">时钟同步类型</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setClockSyncType(IntPtr play, ACClockSyncType type);
/// <summary>
/// 获取时钟同步类型
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>时钟同步类型</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static ACClockSyncType ac_play_getClockSyncType(IntPtr play);
/// <summary>
/// 定位
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="time">时间,单位秒</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_seek(IntPtr play, double time);
/// <summary>
/// 设置音量
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="value">音量大小,0-100</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setVolume(IntPtr play, int value);
/// <summary>
/// 获取音量
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>音量大小,0-100</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static int ac_play_getVolume(IntPtr play);
/// <summary>
/// 设置播放倍速
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="value">倍速,范围:0.5-2</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setSpeed(IntPtr play, double value);
/// <summary>
/// 获取播放倍速
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>倍速</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static double ac_play_getSpeed(IntPtr play);
/// <summary>
/// 设置用户数据
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="userdata">用户数据</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setUserData(IntPtr play, IntPtr userdata);
/// <summary>
/// 获取用户数据
/// </summary>
/// <param name="play">播放器对象</param>
/// <returns>用户数据</returns>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static IntPtr ac_play_getUserData(IntPtr play);
/// <summary>
/// 设置开始播放的回调
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="value">回调方法</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setStartedCallback(IntPtr play, ACPlayStartedCallback value);
/// <summary>
/// 设置即将停止播放的回调,循环播放不会触发此事件。
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="value">回调方法</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setStoppingCallback(IntPtr play, ACPlayStoppingCallback value);
/// <summary>
/// 设置停止的回调
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="value">回调方法</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setStoppedCallback(IntPtr play, ACPlayCallback value);
/// <summary>
/// 设置播放位置改变的回调
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="value">回调方法</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setCursorTimeChangedCallback(IntPtr play, ACPlayCursorTimeChangedCallback value);
/// <summary>
/// 设置渲染的回调
/// </summary>
/// <param name="play">播放器对象</param>
/// <param name="callbask">回调方法</param>
[DllImport(dll, CallingConvention = CallingConvention.Cdecl)] public extern static void ac_play_setDisplayCallback(IntPtr play, ACPlayDisplayCallback value);
}
}