iOS 音视频播放
- 播放控制切换为: ijkplayer
- 播放控制切换为: AliPlayer
- 播放控制切换为: AliyunVodPlayer
- https://gitee.com/changsanjiang/SJVideoPlayer
- 自定义http://www.west999.com/www/info/80265-1.htm
2018.9.18
项目介绍:
SJVideoPlayer
一个可自定义控制层的播放器(如果缺少API, 可以邮箱联系我 changsanjiang@gmail.com )
安装
# 有默认控制层的播放器
pod 'SJVideoPlayer'
# 基础播放器, 不含控制层, 如果需要自定义控制层, 可以使用它.
pod 'SJBaseVideoPlayer'
有默认控制层的播放器
基础播放器, 不含控制层
_
播放
Player.asset = [[SJVideoPlayerAssetCarrier alloc] initWithAssetURL:[NSURL URLWithString:@"http://....."] beginTime:10];
_
在cell
中播放
Player.asset =
[[SJVideoPlayerAssetCarrier alloc] initWithAssetURL:[NSURL URLWithString:cell.model.playURLStr]
scrollView:self.tableView
indexPath:[self.tableView indexPathForCell:cell]
superviewTag:playerParentView.tag];
_
在嵌套(tableView嵌套collectionView或者反之
)的视图中播放
Player.asset =
[[SJVideoPlayerAssetCarrier alloc] initWithAssetURL:playURL
indexPath:indexPath
superviewTag:playerParentView.tag
scrollViewIndexPath:embeddedScrollViewIndexPath
scrollViewTag:embeddedScrollView.tag
rootScrollView:self.tableView];
_
播放相关
#pragma mark - 播放
@interface SJBaseVideoPlayer (Play)
@property (nonatomic, strong, readwrite, nullable) NSURL *assetURL;
@property (nonatomic, strong, readwrite, nullable) SJVideoPlayerURLAsset *URLAsset;
- (void)playWithURL:(NSURL *)playURL;
- (void)playWithURL:(NSURL *)playURL jumpedToTime:(NSTimeInterval)time;
- (void)refresh;
@end
_
时间相关
#pragma mark - 时间
@interface SJBaseVideoPlayer (Time)
- (NSString *)timeStringWithSeconds:(NSInteger)secs; // format: 00:00:00
@property (nonatomic, readonly) float progress;
@property (nonatomic, readonly) NSTimeInterval currentTime;
@property (nonatomic, readonly) NSTimeInterval totalTime;
@property (nonatomic, strong, readonly) NSString *currentTimeStr;
@property (nonatomic, strong, readonly) NSString *totalTimeStr;
- (void)jumpedToTime:(NSTimeInterval)secs completionHandler:(void (^ __nullable)(BOOL finished))completionHandler; // unit is sec. 单位是秒.
- (void)seekToTime:(CMTime)time completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
@end
_
控制相关
#pragma mark - 控制
@interface SJBaseVideoPlayer (Control)
@property (nonatomic, readwrite) BOOL mute; // default is no. 静音.
@property (nonatomic, readwrite, getter=isLockedScreen) BOOL lockedScreen; // 锁定播放器. 所有交互事件将不会触发.
@property (nonatomic, readwrite, getter=isAutoPlay) BOOL autoPlay; // 自动播放. default is YES.
- (BOOL)play;
- (BOOL)pause; // 调用此方法, 表示开发者暂停.
- (void)pauseForUser; // 调用此方法, 表示用户暂停.
@property (nonatomic, assign, readonly) BOOL userPaused; // 区分是用户暂停的, 还是开发者暂停的
- (void)stop;
- (void)stopAndFadeOut; // 停止播放并淡出
- (void)replay;
@property (nonatomic, readwrite) float volume;
@property (nonatomic, readwrite) float brightness;
@property (nonatomic, readwrite) float rate; // 0.5...2
@property (nonatomic, copy, readwrite, nullable) void(^rateChanged)(__kindof SJBaseVideoPlayer *player);
- (void)resetRate;
@property (nonatomic, copy, readwrite, nullable) void(^playDidToEnd)(__kindof SJBaseVideoPlayer *player); // 播放完毕
@end
_
控制层管理器相关
#pragma mark - 控制层
@interface SJBaseVideoPlayer (ControlLayer)
@property (nonatomic, readwrite) BOOL enableControlLayerDisplayController; // default is YES. 是否开启控制层[显示/隐藏]的管理器
@property (nonatomic, readonly) BOOL controlLayerAppeared; // 控制层是否显示
@property (nonatomic, copy, readwrite, nullable) void(^controlLayerAppearStateChanged)(__kindof SJBaseVideoPlayer *player, BOOL state);
- (void)controlLayerNeedAppear;
- (void)controlLayerNeedDisappear;
// 控制层是否显示
@property (nonatomic, readonly) BOOL controlViewDisplayed NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, "use `controlLayerAppeared`");
/*!
* Call when the control view is appear or disappear.
*
* 控制视图隐藏或显示的时候调用.
**/
@property (nonatomic, copy, readwrite, nullable) void(^controlViewDisplayStatus)(__kindof SJBaseVideoPlayer *player, BOOL displayed) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, "use `controlLayerAppearStateChanged`");
@end
_
屏幕旋转相关
#pragma mark - 屏幕旋转
@interface SJBaseVideoPlayer (Rotation)
- (void)rotation; // 旋转
@property (nonatomic, assign, readwrite) BOOL disableRotation; // 禁止播放器旋转
@property (nonatomic, copy, readwrite, nullable) void(^willRotateScreen)(__kindof SJBaseVideoPlayer *player, BOOL isFullScreen); // 将要旋转的时候调用
@property (nonatomic, copy, readwrite, nullable) void(^rotatedScreen)(__kindof SJBaseVideoPlayer *player, BOOL isFullScreen); // 已旋转
@property (nonatomic, assign, readonly) BOOL isFullScreen; // 是否全屏
@end
截图相关
#pragma mark - 截图
@interface SJBaseVideoPlayer (Screenshot)
@property (nonatomic, copy, readwrite, nullable) void(^presentationSize)(__kindof SJBaseVideoPlayer *videoPlayer, CGSize size);
- (UIImage * __nullable)screenshot;
- (void)screenshotWithTime:(NSTimeInterval)time
completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, UIImage * __nullable image, NSError *__nullable error))block;
- (void)screenshotWithTime:(NSTimeInterval)time
size:(CGSize)size
completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, UIImage * __nullable image, NSError *__nullable error))block;
- (void)generatedPreviewImagesWithMaxItemSize:(CGSize)itemSize
completion:(void(^)(__kindof SJBaseVideoPlayer *player, NSArray<id<SJVideoPlayerPreviewInfo>> *__nullable images, NSError *__nullable error))block;
@end
_
提示相关
#pragma mark - 提示
@interface SJBaseVideoPlayer (Prompt)
/*!
* prompt.update(^(SJPromptConfig * _Nonnull config) {
config.cornerRadius = 4; // default cornerRadius.
config.font = [UIFont systemFontOfSize:12]; // default font.
});
*
**/
@property (nonatomic, strong, readonly) SJPrompt *prompt;
- (void)showTitle:(NSString *)title; // duration default is 1.0
- (void)showTitle:(NSString *)title duration:(NSTimeInterval)duration; // duration if value set -1, promptView will always show.
- (void)hiddenTitle;
@end
_
控制层协议
#pragma mark - Protocol
@protocol SJVideoPlayerControlLayerDataSource <NSObject>
@required
- (UIView *)controlView;
/// 控制层需要隐藏之前会调用这个方法, 如果返回NO, 将不调用`controlLayerNeedDisappear:`.
- (BOOL)controlLayerDisappearCondition;
/// 触发手势之前会调用这个方法, 如果返回NO, 将不调用水平手势相关的代理方法.
- (BOOL)triggerGesturesCondition:(CGPoint)location;
@optional
/// 安装完控制层的回调.
- (void)installedControlViewToVideoPlayer:(SJBaseVideoPlayer *)videoPlayer;
@end
@protocol SJVideoPlayerControlLayerDelegate <NSObject>
@optional
#pragma mark - 播放之前/状态
/// 当设置播放资源时调用.
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer prepareToPlay:(SJVideoPlayerURLAsset *)asset;
/// 播放状态改变.
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer stateChanged:(SJVideoPlayerPlayState)state;
/// 播放报错
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer playFailed:(NSError *)error;
#pragma mark - 进度
/// 播放进度回调.
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer
currentTime:(NSTimeInterval)currentTime currentTimeStr:(NSString *)currentTimeStr
totalTime:(NSTimeInterval)totalTime totalTimeStr:(NSString *)totalTimeStr;
/// 缓冲的进度.
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer loadedTimeProgress:(float)progress;
/// 开始缓冲.
- (void)startLoading:(SJBaseVideoPlayer *)videoPlayer;
/// 缓冲完成.
- (void)loadCompletion:(SJBaseVideoPlayer *)videoPlayer;
#pragma mark - 显示/消失
/// 控制层需要显示.
- (void)controlLayerNeedAppear:(SJBaseVideoPlayer *)videoPlayer;
/// 控制层需要隐藏.
- (void)controlLayerNeedDisappear:(SJBaseVideoPlayer *)videoPlayer;
/// 在`tableView`或`collectionView`上将要显示的时候调用.
- (void)videoPlayerWillAppearInScrollView:(SJBaseVideoPlayer *)videoPlayer;
/// 在`tableView`或`collectionView`上将要消失的时候调用.
- (void)videoPlayerWillDisappearInScrollView:(SJBaseVideoPlayer *)videoPlayer;
#pragma mark - 锁屏
/// 播放器被锁屏, 此时将不旋转, 不触发手势相关事件.
- (void)lockedVideoPlayer:(SJBaseVideoPlayer *)videoPlayer;
/// 播放器解除锁屏.
- (void)unlockedVideoPlayer:(SJBaseVideoPlayer *)videoPlayer;
#pragma mark - 屏幕旋转
/// 播放器将要旋转屏幕, `isFull`如果为`YES`, 则全屏.
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer willRotateView:(BOOL)isFull;
/// 旋转完毕.
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer didEndRotation:(BOOL)isFull;
#pragma mark - 音量 / 亮度 / 播放速度
/// 静音开关变更
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer muteChanged:(BOOL)mute;
/// 声音被改变.
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer volumeChanged:(float)volume;
/// 亮度被改变.
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer brightnessChanged:(float)brightness;
/// 播放速度被改变.
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer rateChanged:(float)rate;
#pragma mark - 水平手势
/// 水平方向开始拖动.
- (void)horizontalDirectionWillBeginDragging:(SJBaseVideoPlayer *)videoPlayer;
/// 水平方向拖动中. `translation`为此次增加的值.
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer horizontalDirectionDidDrag:(CGFloat)translation;
/// 水平方向拖动结束.
- (void)horizontalDirectionDidEndDragging:(SJBaseVideoPlayer *)videoPlayer;
#pragma mark - size
- (void)videoPlayer:(SJBaseVideoPlayer *)videoPlayer presentationSize:(CGSize)size;
@end
_
抽离出的控件, 可独立使用
加载视图
全屏返回手势
手势在UIScrollView和UIPageViewController中完美处理。
可指定盲区。指定的区域不会触发手势。它不会影响其他ViewControllers。
可在指定页面禁用手势。指定ViewController禁用手势。它不会影响其他ViewControllers。
WKWebView返回上一个网页。
亮度和音量调整
播放资源载体
屏幕旋转观察者
滑动条
提示
便捷创建UI的工厂
播放类型
https://blog.csdn.net/weixin_33825683/article/details/91374810
按公司需求需要对音频文件进行后台播放,借此机会对音频播放做了个总结.主要针对 AVPlayer 进行详细说明.
iOS 各播放器比较
名称 | 使用环境 | 优点 | 缺点 |
---|---|---|---|
System Sound Services | AVFoundation | C语言的底层写法,节省内存 | 支持的格式有限,音量无法通过音量键控制,而且播放方式单一。 |
AVAudioPlayer | AVFoundation | 抒写效率更高,基本上支持所有的音频格式,对播放的控制,如循环播放,声音大小,暂停等比较方便。 | 对内存的消耗会多些。不支持流式,即无法播放在线音乐。 |
AVPlayer | AVFoundation | 可以播放音视频,可播放在线音乐,使用灵活 | |
MPMoviePlayerController | MediaPlayer | 简单易用 | 不可定制 |
AVPlayerViewController | AVKit | 简单易用 | 不可定制 |
IJKPlayer | IJKMediaFramework | 定制度高,支持流媒体播放 | 使用稍复杂 |
AVPlayer 使用
简介
AVPlayer 是iOS上常用的视频播放器组件,支持常见的音视频格式,支持流播放,可以播放在线音乐. 支持视频格式: WMV,AVI,MKV,RMVB,RM,XVID,MP4,3GP,MPG等。 支持音频格式:MP3,WMA,RM,ACC,OGG,APE,FLAC,FLV等。
相关类
- AVPlayer:播放器,控制播放器的播放,暂停,播放速度.
- AVURLAsset : AVAsset 的一个子类,使用 URL 进行实例化,实例化对象包换 URL 对应视频资源的所有信息.
- AVPlayerItem:管理资源对象,提供播放数据源.
- AVPlayerLayer:负责显示视频,如果没有添加该类,只有声音没有画面.
简单使用
使用 url 创建 AVPlayer
- let player = AVPlayer(url: URL(string: "http://www.xxxx.mp3"))
- 复制代码
使用 AVPlayerItem 创建 AVPlayer
- if let url = URL(string: "http://www.***.mp3") {
- let asset = AVAsset(url: url)
- guard asset.isPlayable else{
- // 检测文件是否可播放
- return
- }
- let playItem = AVPlayerItem(asset: asset)
- let player = AVPlayer(playerItem: playItem)
- player.play()
- }
- 复制代码
AVPlayer 控制播放
- player.play() // 播放
- player.pause() //暂停
- player.rate = 1.0 // 播放速度
- 复制代码
通过通知监听播放状态变化
//播放完成
AVPlayerItemDidPlayToEndTimeNotification
//播放失败
AVPlayerItemFailedToPlayToEndTimeNotification
//异常中断
AVPlayerItemPlaybackStalledNotification
// eg: 播放结束通知
NotificationCenter.default.addObserver(self, selector: #selector(finish(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
复制代码
监听播放进度
// 添加周期时间观察者 一秒执行一次 block
let timeObserver = player.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: 1), queue: DispatchQueue.main, using: { [weak self] (cmTime) in
if let totalTime = self?.currentPlayItem?.duration {
self?.delegate?.player(self!, currentTime: cmTime.seconds, totalTime: totalTime.seconds)
}
})
// 不要忘记移除
player.removeTimeObserver(observer)
AVPlayerItem 创建
// 监听 playerItem 状态变化
playItem.addObserver(self, forKeyPath: "status", options: .new, context: nil)
// 监听缓存时间
playItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil)
// 移除监听
currentPlayItem?.removeObserver(self, forKeyPath: "status")
currentPlayItem?.removeObserver(self, forKeyPath: "loadedTimeRanges")
复制代码
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if object is AVPlayerItem {
if keyPath == "status" {
if let playerItem = object as? AVPlayerItem {
switch playerItem.status {
case .readyToPlay:
// 准备播放
case .failed:
// 加载失败
default:
// 未知状态
}
}
}
if keyPath == "loadedTimeRanges" {
if let playerItem = object as? AVPlayerItem {
if let timeRange = playerItem.loadedTimeRanges.first as? CMTimeRange {
let cache = timeRange.start.seconds + timeRange.duration.seconds // 缓存总时长
}
}
}
}
音频后台播放
开启所需后台模式
利用 AVAudioSession 申请后台播放权限
let session = AVAudioSession.sharedInstance()
do {
try session.setActive(true)
try session.setCategory(AVAudioSessionCategoryPlayback)
} catch {
print(error)
}
在播放控制界面接受远程控制(Remote Control)
开启远程控制
// 声明接收Remote Control事件
设置 Remote Control 响应
// 响应 Remote Control事件
MPRemoteCommandCenter.shared().playCommand.addTarget(self, action: #selector(play))
MPRemoteCommandCenter.shared().nextTrackCommand.addTarget(self, action: #selector(next))
MPRemoteCommandCenter.shared().pauseCommand.addTarget(self, action: #selector(pause))
MPRemoteCommandCenter.shared().previousTrackCommand.addTarget(self, action: #selector(previous))
移除 Remote Control 响应
// 在关闭播放页面时记得移除
MPRemoteCommandCenter.shared().playCommand.removeTarget(self, action: #selector(play))
MPRemoteCommandCenter.shared().nextTrackCommand.removeTarget(self, action: #selector(next))
MPRemoteCommandCenter.shared().pauseCommand.removeTarget(self, action: #selector(pause))
MPRemoteCommandCenter.shared().previousTrackCommand.removeTarget(self, action: #selector(previous))
// 停止响应 Remote Control
UIApplication.shared.endReceivingRemoteControlEvents()
通过重写父类方法响应外部事件
开启接受远程控制
使当前页面成为第一响应者
重写 remoteControlReceivedWithEvent 方法. UIEvent Type 取值:
- UIEventSubtypeRemoteControlTogglePlayPause // 暂停
- UIEventSubtypeRemoteControlPreviousTrack // 上一首
- UIEventSubtypeRemoteControlNextTrack // 下一首
- UIEventSubtypeRemoteControlPlay // 播放
- UIEventSubtypeRemoteControlPause // 暂停
关闭接受远程控制
锁屏页面显示播放信息(Now Playing Center)
使用 MPNowPlayingInfoCenter 设置锁屏页面音乐信息.
func setLockScreenPlayingInfo(_ info: YTTMediaInfo) {
// Now Playing Center可以在锁屏界面展示音乐的信息,也达到增强用户体验的作用。
// https://www.jianshu.com/p/458b67f84f27
var infoDic: [String : Any] = [:]
infoDic[MPMediaItemPropertyTitle] = info.title // 歌曲名
infoDic[MPMediaItemPropertyArtist] = info.singer // 歌手
if let img = info.image {
infoDic[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(image: img) // 专辑图片
}
infoDic[MPMediaItemPropertyPlaybackDuration] = info.totalTime // 歌曲总时长
infoDic[MPNowPlayingInfoPropertyElapsedPlaybackTime] = info.currentTime // 当前播放时间
infoDic[MPNowPlayingInfoPropertyPlaybackRate] = 1.0 // 播放速度
MPNowPlayingInfoCenter.default().nowPlayingInfo = infoDic
}
注意: MPNowPlayingInfoPropertyElapsedPlaybackTime 设置的并不是时时的,他是根据你设置的值进行计时的,如果想要在锁屏页面得到准确的时间,请及时刷新 MPNowPlayingInfoPropertyElapsedPlaybackTime 的值.当暂停时要暂停播放时间,只需将 MPNowPlayingInfoPropertyPlaybackRate 设置为 0.播放时设置回 1.
补充说明
iOS 对后台管理十分严格,任何 app 都有大约3分钟或者10分钟的后台执行时间.3分钟或者10分钟后, app 就会被强制挂起.使用 AVAudioSession 申请后台权限时,可以保证播放本地音乐能在后台长久播放,当播放网络音乐时就会出现不能播放情况,针对这情况使用了 beginBackgroundTask 设置后台任务 ID,通过这种方式我们大约可以获得额外的 10 分钟来执行后台任务.为了能无限后台播放网络音乐添加计时器,当即将挂起时再次申请后台任务 ID.
func applicationDidEnterBackground(_ application: UIApplication) {
// 这样做,可以在按home键进入后台后 ,播放一段时间,几分钟吧。但是不能持续播放网络歌曲,若需要持续播放网络歌曲,还需要申请后台任务id
bgTask = application.beginBackgroundTask(expirationHandler: nil)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
}
@objc func timerAction() {
timerCount = timerCount + 1
if timerCount < 500 {
return
}
timerCount = 0
let newTask = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
if bgTask != UIBackgroundTaskInvalid && newTask != UIBackgroundTaskInvalid {
UIApplication.shared.endBackgroundTask(bgTask)
bgTask = newTask
}
}
iOS 音视频开发-MediaPlayer播放本地、远程音频
https://www.jianshu.com/p/ec3533549aa6
iOS音视频播放---改善用户体验
https://blog.csdn.net/szk972092933/article/details/82770725
iOS——给视频添加音频和字幕
https://www.jianshu.com/p/9d16907b2c52
iOS 一个功能很全的视频播放器
https://www.jianshu.com/p/4c2a493fb4bf
SJVideoPlayer
基于AVPlayer.
https://github.com/changsanjiang/SJVideoPlayer
极速初始化, 不阻塞主线程. 这个应该是目前基于AVPlayer的播放器中, 功能最全的一个吧.
图解使用请移步: https://www.jianshu.com/p/a60389f9acaf 模块化控制层请移步: https://www.jianshu.com/p/6a968ec24d3f
过滤视频中的黑帧(生成视频thumbnail时,跳过黑帧)
https://www.jianshu.com/p/eaaee36195cb
iOS 音视频播放的更多相关文章
- iOS AVKit音视频播放全面详解
公司项目中经常要用到音视频处理,也需要去定制一些东西,然后整理这些音视频处理就显得尤为重要!方便自己和广大朋友学习收藏! 以下参考连接特别重要: 苹果官方:AVKit API 苹果官方:AVFound ...
- iOS - AVPlayer 音视频播放
前言 NS_CLASS_AVAILABLE(10_7, 4_0) @interface AVPlayer : NSObject @available(iOS 4.0, *) public class ...
- ios 音视频实现边播边缓存的思路和解决方案 (转)
本片为转载内容,主要是以后自己看起来方便一些 原文地址:iOS音视频实现边下载边播放 其实音视频本地缓存的思想都差不多,都需要一个中间对象来连接播放器和服务器. 近段时间制作视频播放社区的功能,期间查 ...
- ios开发视频播放后台下载功能实现 :1,ios播放视频 ,包含基于AVPlayer播放器,2,实现下载,iOS后台下载(多任务同时下载,单任务下载,下载进度,下载百分比,文件大小,下载状态)(真机调试功能正常)
ABBPlayerKit ios开发视频播放后台下载功能实现 : 代码下载地址:https://github.com/niexiaobo/ABBPlayerKit github资料学习和下载地址:ht ...
- Android音视频之MediaPlayer音视频播放
前言: 昨天总结了视频录制,今天来学习一下视频的播放,Android的视频播放主要采用MediaPlayer类. MediaPlayer介绍 MediaPlayer类可用于控制音频/视频文件或流的播放 ...
- Pyqt 音视频播放器
在寻找如何使用Pyqt做一个播放器时首先找到的是openCV2 openCV2 貌似太强大了,各种关于图像处理的事情它都能完成,如 读取摄像头.图像识别.人脸识别. 图像灰度处理 . 播放视频等,强 ...
- iOS开发——视频播放 待更新……
本文主要实现调用系统自带的播放器,自带的播放器已经做好了屏幕的适配,集成播放,暂停,快进等功能.能够满足基本的视频播放功能及iOS的视频播放的开发. 最后将其简单的封装成一个iOS视频播放工具类. 一 ...
- 分享几个不错的Android开源音视频播放器
整理了一下Github上几个开源的音视频播放器项目,有兴趣的同学可以clone代码去研究学习. UniversalMusicPlayer https://github.com/googlesamp ...
- AVAudioFoundation(2):音视频播放
本文转自:AVAudioFoundation(2):音视频播放 | www.samirchen.com 本文主要内容来自 AVFoundation Programming Guide. 要播放 AVA ...
随机推荐
- vue项目出现Module not found: Error: Can't resolve 'stylus-loader'错误解决方案
因为没有安装stylus和stylus-loader npm install stylus stylus-loader --save-dev 安装成功后,使用npm install重新建立依赖 打开项 ...
- 巨杉TechDay回顾 | WARNING!您参加的数据库沙龙热度已爆表……
自从2008年“大数据”这一概念被首次提出以来,在过去这10年中,几乎各行各业都或多或少受到了这一概念的影响.与此同时,在AI.云计算.物联网.区块链等新兴技术快速发展的今天,数据库己经成为了决定所有 ...
- Eclipse导入工程Some projects cannot be imported because they already exist in the workspace
记录一下本次出错原因,以及解决方法 错误原因: 第一次导入后,删除工程,没有没有勾选Delete project contents on disk(cannot be undone) 解决方法: 1 ...
- Java selenium下拉滚动页面
Selenium强大之处在于,可以操作模拟键盘和点击页面的任何结构,本文对于Selenium的视图滚动操作方法进行解释. 1.可以滑动页面到最底端: //设置本地ChromDrive驱动路径,改成你自 ...
- java-判断年份是不是闰年
if ((year%4==0)&&(year%100!=0)||(year%400==0)) {//是闰年 leapYear = true; }
- css div布局示例2(head-main-footer
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- HTML连载61-焦点图、固定定位
一.焦点图 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...
- Having用法以及其和Where区别
例如,进行分组语句: select year,count(id) num from tblPlantProduce group by year 在这条语句中若要筛选出年份>2005年的有两种方式 ...
- MVC HTML辅助方法
HTML辅助方法(HTML Helper)用来辅助产生HTML,在开发View的时候会面对许多HTML标签,处理这些HTML标签非常繁琐,为了降低View的复杂度,可以使用HTML辅助方法帮助你产生一 ...
- [linux] Ubuntu18.04 安装mysql密码不对
安装 安装过程可以说是非常简单了 sudo apt-get install mysql-server 然后看看有没有启动成功 systemctl status mysql 看到状态是 running就 ...