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

  1.  
    let player = AVPlayer(url: URL(string: "http://www.xxxx.mp3"))
  2.  
    复制代码

使用 AVPlayerItem 创建 AVPlayer

  1.  
    if let url = URL(string: "http://www.***.mp3") {
  2.  
    let asset = AVAsset(url: url)
  3.  
    guard asset.isPlayable else{
  4.  
    // 检测文件是否可播放
  5.  
    return
  6.  
    }
  7.  
    let playItem = AVPlayerItem(asset: asset)
  8.  
    let player = AVPlayer(playerItem: playItem)
  9.  
    player.play()
  10.  
    }
  11.  
    复制代码

AVPlayer 控制播放

  1.  
    player.play() // 播放
  2.  
    player.pause() //暂停
  3.  
    player.rate = 1.0 // 播放速度
  4.  
    复制代码

通过通知监听播放状态变化

//播放完成
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)
}
  1. 在播放控制界面接受远程控制(Remote Control)

  2. 开启远程控制

 

// 声明接收Remote Control事件

UIApplication.shared.beginReceivingRemoteControlEvents()
  1. 设置 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()
  1. 通过重写父类方法响应外部事件

  2. 开启接受远程控制

  3. 使当前页面成为第一响应者

  4. 重写 remoteControlReceivedWithEvent 方法. UIEvent Type 取值:

    1. UIEventSubtypeRemoteControlTogglePlayPause // 暂停
    2. UIEventSubtypeRemoteControlPreviousTrack // 上一首
    3. UIEventSubtypeRemoteControlNextTrack // 下一首
    4. UIEventSubtypeRemoteControlPlay // 播放
    5. UIEventSubtypeRemoteControlPause // 暂停
  5. 关闭接受远程控制

  6. 锁屏页面显示播放信息(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.

  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

}

}

  1. 其他

  2. AVPlayer那些坑
  3. 项目参考地址
  4. 针对缓存实现 Demo

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 音视频播放的更多相关文章

  1. iOS AVKit音视频播放全面详解

    公司项目中经常要用到音视频处理,也需要去定制一些东西,然后整理这些音视频处理就显得尤为重要!方便自己和广大朋友学习收藏! 以下参考连接特别重要: 苹果官方:AVKit API 苹果官方:AVFound ...

  2. iOS - AVPlayer 音视频播放

    前言 NS_CLASS_AVAILABLE(10_7, 4_0) @interface AVPlayer : NSObject @available(iOS 4.0, *) public class ...

  3. ios 音视频实现边播边缓存的思路和解决方案 (转)

    本片为转载内容,主要是以后自己看起来方便一些 原文地址:iOS音视频实现边下载边播放 其实音视频本地缓存的思想都差不多,都需要一个中间对象来连接播放器和服务器. 近段时间制作视频播放社区的功能,期间查 ...

  4. ios开发视频播放后台下载功能实现 :1,ios播放视频 ,包含基于AVPlayer播放器,2,实现下载,iOS后台下载(多任务同时下载,单任务下载,下载进度,下载百分比,文件大小,下载状态)(真机调试功能正常)

    ABBPlayerKit ios开发视频播放后台下载功能实现 : 代码下载地址:https://github.com/niexiaobo/ABBPlayerKit github资料学习和下载地址:ht ...

  5. Android音视频之MediaPlayer音视频播放

    前言: 昨天总结了视频录制,今天来学习一下视频的播放,Android的视频播放主要采用MediaPlayer类. MediaPlayer介绍 MediaPlayer类可用于控制音频/视频文件或流的播放 ...

  6. Pyqt 音视频播放器

    在寻找如何使用Pyqt做一个播放器时首先找到的是openCV2 openCV2 貌似太强大了,各种关于图像处理的事情它都能完成,如 读取摄像头.图像识别.人脸识别.  图像灰度处理 . 播放视频等,强 ...

  7. iOS开发——视频播放 待更新……

    本文主要实现调用系统自带的播放器,自带的播放器已经做好了屏幕的适配,集成播放,暂停,快进等功能.能够满足基本的视频播放功能及iOS的视频播放的开发. 最后将其简单的封装成一个iOS视频播放工具类. 一 ...

  8. 分享几个不错的Android开源音视频播放器

    整理了一下Github上几个开源的音视频播放器项目,有兴趣的同学可以clone代码去研究学习.   UniversalMusicPlayer https://github.com/googlesamp ...

  9. AVAudioFoundation(2):音视频播放

    本文转自:AVAudioFoundation(2):音视频播放 | www.samirchen.com 本文主要内容来自 AVFoundation Programming Guide. 要播放 AVA ...

随机推荐

  1. vue项目出现Module not found: Error: Can't resolve 'stylus-loader'错误解决方案

    因为没有安装stylus和stylus-loader npm install stylus stylus-loader --save-dev 安装成功后,使用npm install重新建立依赖 打开项 ...

  2. 巨杉TechDay回顾 | WARNING!您参加的数据库沙龙热度已爆表……

    自从2008年“大数据”这一概念被首次提出以来,在过去这10年中,几乎各行各业都或多或少受到了这一概念的影响.与此同时,在AI.云计算.物联网.区块链等新兴技术快速发展的今天,数据库己经成为了决定所有 ...

  3. Eclipse导入工程Some projects cannot be imported because they already exist in the workspace

    记录一下本次出错原因,以及解决方法 错误原因: 第一次导入后,删除工程,没有没有勾选Delete project contents on disk(cannot be undone) 解决方法: 1 ...

  4. Java selenium下拉滚动页面

    Selenium强大之处在于,可以操作模拟键盘和点击页面的任何结构,本文对于Selenium的视图滚动操作方法进行解释. 1.可以滑动页面到最底端: //设置本地ChromDrive驱动路径,改成你自 ...

  5. java-判断年份是不是闰年

    if ((year%4==0)&&(year%100!=0)||(year%400==0)) {//是闰年 leapYear = true; }

  6. css div布局示例2(head-main-footer

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. HTML连载61-焦点图、固定定位

    一.焦点图 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...

  8. Having用法以及其和Where区别

    例如,进行分组语句: select year,count(id) num from tblPlantProduce group by year 在这条语句中若要筛选出年份>2005年的有两种方式 ...

  9. MVC HTML辅助方法

    HTML辅助方法(HTML Helper)用来辅助产生HTML,在开发View的时候会面对许多HTML标签,处理这些HTML标签非常繁琐,为了降低View的复杂度,可以使用HTML辅助方法帮助你产生一 ...

  10. [linux] Ubuntu18.04 安装mysql密码不对

    安装 安装过程可以说是非常简单了 sudo apt-get install mysql-server 然后看看有没有启动成功 systemctl status mysql 看到状态是 running就 ...