一篇文章讲明白iosavplayer视频播放器

简介: 一篇文章讲明白iosavplayer视频播放器

#import

@interface XGGesTuresView : UIView

//左下角播放按钮

@property (nonatomic ,strong)UIButton leftplayerbutton;

//最小时间

@property (nonatomic ,strong)UILabel mintimelable;

//滑动条

@property (nonatomic ,strong)UISlider slider;

//缓冲条

@property (nonatomic ,strong)UIProgressView progressview;

//视频总时长

@property (nonatomic ,strong)UILabel maxtimelable;

//全屏按钮

@property (nonatomic ,strong)UIButton fullbutton;

//右边中间播放按钮

@property (nonatomic ,strong)UIButton rightplayerbutton;

//小菊花

@property (nonatomic ,strong)UIActivityIndicatorView activity;

//视频出错提示 网络提示 用同一个

//@property (nonatomic ,strong)UILabel errorlable;

//返回按钮

@property (nonatomic ,strong)UIButton backbutton;

//标题 可以自己调颜色

@property (nonatomic ,strong)UILabel titlelable;

@end

#import "XGGesTuresView.h"

@implementation XGGesTuresView

- (instancetype)initWithFrame:(CGRect)frame{

if (self =【super initWithFrame:frame】) {

self.backgroundColor = 【UIColor clearColor】;

【self creatUI】;

}

return self;

}

- (void)creatUI{

//左边播放按钮

self.leftplayerbutton = 【UIButton buttonWithType:UIButtonTypeCustom】;

【self.leftplayerbutton setImage:【UIImage imageNamed:@"Stop"】 forState:UIControlStateNormal】;

【self.leftplayerbutton setImage:【UIImage imageNamed:@"Play"】 forState:UIControlStateSelected】;

【self addSubview:self.leftplayerbutton】;

//进度时间lable

self.mintimelable = 【【UILabel alloc】init】;

self.mintimelable.text = @"00:00";

self.mintimelable.textColor = 【UIColor whiteColor】;

self.mintimelable.textAlignment = NSTextAlignmentCenter;

self.mintimelable.font = 【UIFont systemFontOfSize:14】;

【self addSubview:self.mintimelable】;

//缓冲条

self.progressview = 【【UIProgressView alloc】init】;

self.progressview.progressTintColor = 【UIColor magentaColor】;

【self addSubview:self.progressview】;

//滑动条

self.slider = 【【UISlider alloc】init】;

self.slider.minimumTrackTintColor = 【UIColor whiteColor】;

self.slider.maximumTrackTintColor = 【UIColor clearColor】;

【self.slider setThumbImage:【UIImage imageNamed:@"icmpv_thumb_light"】 forState:UIControlStateNormal】;

【self addSubview:self.slider】;

//总时间lable

self.maxtimelable = 【【UILabel alloc】init】;

self.maxtimelable.text = @"00:00";

self.maxtimelable.textColor = 【UIColor whiteColor】;

self.maxtimelable.textAlignment = NSTextAlignmentCenter;

self.maxtimelable.font = 【UIFont systemFontOfSize:14】;

【self addSubview:self.maxtimelable】;

//全屏按钮

self.fullbutton = 【UIButton buttonWithType:UIButtonTypeCustom】;

【self.fullbutton setImage:【UIImage imageNamed:@"Rotation"】 forState:UIControlStateNormal】;

【self addSubview:self.fullbutton】;

//右边播放按钮

self.rightplayerbutton = 【UIButton buttonWithType:UIButtonTypeCustom】;

【self.rightplayerbutton setImage:【UIImage imageNamed:@"player_pause_iphone_window"】 forState:UIControlStateNormal】;

【self.rightplayerbutton setImage:【UIImage imageNamed:@"player_start_iphone_window"】 forState:UIControlStateSelected】;

【self addSubview:self.rightplayerbutton】;

//小菊花

self.activity = 【【UIActivityIndicatorView alloc】initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge】;

self.activity.color = 【UIColor whiteColor】;

【self addSubview:self.activity】;

//视频出错提示

// self.errorlable = 【【UILabel alloc】init】;

// self.errorlable.text = @"亲,视频解析出错了!";

// self.errorlable.textColor = 【UIColor whiteColor】;

// self.errorlable.textAlignment = NSTextAlignmentCenter;

// self.errorlable.hidden = YES;

// 【self addSubview:self.errorlable】;

//返回按钮

self.backbutton = 【UIButton buttonWithType:UIButtonTypeCustom】;

【self.backbutton setImage:【UIImage imageNamed:@"public返回"】 forState:UIControlStateNormal】;

【self addSubview:self.backbutton】;

//标题

self.titlelable = 【【UILabel alloc】init】;

self.titlelable.textColor = 【UIColor whiteColor】;

【self addSubview:self.titlelable】;

}

- (void)layoutSubviews{

【super layoutSubviews】;

self.leftplayerbutton.frame = CGRectMake(10, self.frame.size.height - 40, 30, 30);

self.mintimelable.frame = CGRectMake(CGRectGetMaxX(self.leftplayerbutton.frame), self.frame.size.height - 40, 60, 30);

self.slider.frame = CGRectMake(CGRectGetMaxX(self.mintimelable.frame), self.frame.size.height - 35, self.frame.size.width - 2 CGRectGetMaxX(self.mintimelable.frame), 20);

self.progressview.frame = CGRectMake(self.slider.frame.origin.x + 6, self.slider.frame.origin.y, self.slider.frame.size.width - 12, 2);

self.progressview.center = self.slider.center;

self.maxtimelable.frame = CGRectMake(CGRectGetMaxX(self.slider.frame), self.frame.size.height - 40, 60, 30);

self.fullbutton.frame = CGRectMake(CGRectGetMaxX(self.maxtimelable.frame), self.frame.size.height -40, 30, 30);

self.rightplayerbutton.frame = CGRectMake(self.frame.size.width - 60, self.frame.size.height - 100, 50, 50);

self.activity.frame = CGRectMake(CGRectGetMidX(self.bounds) - 370.5, CGRectGetMidY(self.bounds)-370.5, 37, 37);

//self.errorlable.frame = CGRectMake(self.frame.size.width/ 2 - 100, self.frame.size.height / 2 -10, 200, 20);

self.backbutton.frame = CGRectMake(0, 0, 30, 30);

self.titlelable.frame = CGRectMake(CGRectGetMaxX(self.backbutton.frame), CGRectGetMinY(self.backbutton.frame), self.frame.size.width - CGRectGetMaxX(self.backbutton.frame), 30);

}

- (void)dealloc{

}

上面这些是提示视图 和 手势视图 下面是主要 用avplayer 来播放 代码可以复制到你的工程里 把类改一下就行

#import

@protocol XGAVPlayerViewdelegate

//全屏

- (void)XGAVPlayerViewfullScreen:(BOOL)fullscreen;

//返回

- (void)XGAVPlayerViewback;

@end

@interface XGAVPlayerView : UIView

//代理

@property (nonatomic ,weak)iddelegate;

//本地和网络亦可

- (void)setUrl:(NSURL )url andIsStartplay:(BOOL)isstartplay;

//

- (void)closeplay;

@end

#import "XGAVPlayerView.h"

#import "Reachability.h"

#import "XGGesTuresView.h"

#import

#import

#import

@interface XGAVPlayerView ()

//播放工具

@property (nonatomic ,strong)AVPlayer player;

//播放图层

@property (nonatomic ,strong)AVPlayerLayer playerlayer;

//播放资源对象

@property (nonatomic ,strong)AVPlayerItem playeritem;

//手势视图

@property (nonatomic ,strong)XGGesTuresView gesturesview;

//错误lable

@property (nonatomic ,strong)UILabel errorlable;

//重连

@property (nonatomic ,strong)UIButton reconnectbutton;

@property (nonatomic ,strong)CTCallCenter telephone;

//播放地址

@property (nonatomic ,strong)NSURL playerurl;

//player时间观察者

@property (nonatomic ,strong)id playTimeObserver;

//是否正在播放

@property (nonatomic ,assign)BOOL isplaying;

//是否开始播放 可从外界控制

@property (nonatomic ,assign)BOOL isstartplay;

//网络

@property (nonatomic ,assign)NetworkStatus netstatus;

@end

@implementation XGAVPlayerView

//初始化

- (instancetype)initWithFrame:(CGRect)frame{

if (self = 【super initWithFrame:frame】) {

self.backgroundColor = 【UIColor blackColor】;

【self creatAvPlayer】;

【self creatUI】;

【self addtap】;

}

return self;

}

//播放管理

- (void)creatAvPlayer{

self.player = 【【AVPlayer alloc】init】;

self.playerlayer = 【AVPlayerLayer playerLayerWithPlayer:self.player】;

self.playerlayer.videoGravity = AVLayerVideoGravityResizeAspect;

【self.layer insertSublayer:self.playerlayer atIndex:0】;//放到最底层

}

//视图

- (void)creatUI{

//错误

self.errorlable = 【【UILabel alloc】init】;

self.errorlable.textColor = 【UIColor whiteColor】;

self.errorlable.textAlignment = NSTextAlignmentCenter;

self.errorlable.hidden = YES;

【self addSubview:self.errorlable】;

//重连

self.reconnectbutton = 【UIButton buttonWithType:UIButtonTypeCustom】;

【self.reconnectbutton setTitle:@"重 连" forState:UIControlStateNormal】;

self.reconnectbutton.backgroundColor = 【UIColor greenColor】;

self.reconnectbutton.hidden = YES;

【self.reconnectbutton addTarget:self action:@selector(notreachableclick) forControlEvents:UIControlEventTouchUpInside】;

【self addSubview:self.reconnectbutton】;

//手势视图

self.gesturesview = 【【XGGesTuresView alloc】init】;

【self addSubview:self.gesturesview】;

self.gesturesview.slider.value = 0.0;//滑动条初始值

self.gesturesview.progressview.progress = 0.0;//缓冲条初始值

【self.gesturesview.leftplayerbutton addTarget:self action:@selector(playclick) forControlEvents:UIControlEventTouchUpInside】;

【self.gesturesview.fullbutton addTarget:self action:@selector(fullclick:) forControlEvents:UIControlEventTouchUpInside】;

【self.gesturesview.rightplayerbutton addTarget:self action:@selector(playclick) forControlEvents:UIControlEventTouchUpInside】;

【self.gesturesview.backbutton addTarget:self action:@selector(backclick) forControlEvents:UIControlEventTouchUpInside】;

【self.gesturesview.slider addTarget:self action:@selector(palyslidertouchdown:) forControlEvents:UIControlEventTouchDown】;//按下

【self.gesturesview.slider addTarget:self action:@selector(palysliderdrop:) forControlEvents:UIControlEventValueChanged】;//拖动

【self.gesturesview.slider addTarget:self action:@selector(palysliderdropfinish:) forControlEvents:UIControlEventTouchUpInside】;//点击

}

//播放 暂停

- (void)playclick{

self.isplaying ? 【self stopplay】 :【self startplay】;

}

//全屏

- (void)fullclick:(UIButton )sender{

sender.selected = !sender.selected;

if (self.delegate 【self.delegate respondsToSelector:@selector(XGAVPlayerViewfullScreen:)】){

【self.delegate XGAVPlayerViewfullScreen:sender.selected】;

}

}

//返回 如果反回到原始图(竖屏) 记得改变全屏按钮状态 如果返回到上一个控制器 则不需要改变全屏按钮状态 这里自定义

- (void)backclick{

【self stopplay】;

if (self.delegate 【self.delegate respondsToSelector:@selector(XGAVPlayerViewback)】){

【self.delegate XGAVPlayerViewback】;

}

}

//按下

- (void)palyslidertouchdown:(UISlider )sender{

【self.player pause】;

if (self.isplaying == YES) {

self.gesturesview.leftplayerbutton.selected = YES;

self.gesturesview.rightplayerbutton.selected = YES;

}

【【self class】 cancelPreviousPerformRequestsWithTarget:self selector:@selector(showgesturesview) object:nil】;

}

//拖动

- (void)palysliderdrop:(UISlider )sender{

CMTime changetime = CMTimeMakeWithSeconds(sender.value, 1.0);

self.gesturesview.mintimelable.text = 【self convertTime:CMTimeGetSeconds(changetime)】;

}

//拖动结束

- (void)palysliderdropfinish:(UISlider )sender{

CMTime changetime = CMTimeMakeWithSeconds(sender.value, 1.0);

【self.playeritem seekToTime:changetime completionHandler:^(BOOL finished) {

self.isplaying ? 【self startplay】 : nil;

}】 ;

}

//手势

- (void)addtap{

UITapGestureRecognizer tap = 【【UITapGestureRecognizer alloc】initWithTarget:self action:@selector(showgesturesview)】;

tap.delegate = self;

【self addGestureRecognizer:tap】;

}

//手势方法

- (void)showgesturesview{

【【self class】 cancelPreviousPerformRequestsWithTarget:self selector:@selector(showgesturesview) object:nil】;

【UIView animateWithDuration:0.25 animations:^{

self.gesturesview.hidden = !self.gesturesview.hidden;

【【UIApplication sharedApplication】setStatusBarHidden:self.gesturesview.hidden withAnimation:UIStatusBarAnimationFade】;

} completion:nil】;

}

//手势代理 用来解决uislider和tap 的手势冲突

- (BOOL)gestureRecognizer:(UIGestureRecognizer )gestureRecognizer shouldReceiveTouch:(nonnull UITouch )touch{

if (【touch.view isKindOfClass:【UISlider class】】) {

return NO;

}

return YES;

}

//本地和网络亦可

- (void)setUrl:(NSURL )url andIsStartplay:(BOOL)isstartplay{//进来优先检查网络

self.isstartplay = isstartplay;

self.playerurl = url;

NetworkStatus firststatus = 【self CheckNowNetWorkStatus】;

switch (firststatus) {

case NotReachable:{//进来时没网络

【self notreachable】;

}break;

case ReachableViaWiFi:{//进来wifi

【self reachableviawifi:url】;

}break;

case ReachableViaWWAN:{//进来时没网络

UIAlertView firstalert = 【【UIAlertView alloc】initWithTitle:@"温馨提示" message:@"当前处于2g/3g/4g模式下,继续播放会产生流量!" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil】;

firstalert.tag = 1;

【firstalert show】;

}break;

default:

break;

}

}

//无网络提示

- (void)notreachable{

【self stopplay】;

self.errorlable.hidden = NO;

self.errorlable.text = @"亲,没有网络!";

self.reconnectbutton.hidden = NO;

self.gesturesview.userInteractionEnabled = NO;

}

//刚进来时无网络状态 没有创建self.playeritem 和监听 所以不影响后面的

- (void)notreachableclick{

NetworkStatus reconnect = 【self CheckNowNetWorkStatus】;

switch (reconnect) {

case NotReachable:{//重连 无网络点击 不做任何反应

NSLog(@"无网络");

}break;

case ReachableViaWiFi:{//wifi

NSLog(@"wifi");

}

case ReachableViaWWAN:{//在流量状态重连 表示用户默认接受了流量状态下播放 wifi就不用说了

【self reachableviawifi:self.playerurl】;

}break;

default:

break;

}

}

//wifi

- (void)reachableviawifi:(NSURL )url{

//代码效果参考:http://www.zidongmutanji.com/zsjx/473665.html

self.errorlable.hidden = YES;

self.reconnectbutton.hidden = YES;

self.gesturesview.userInteractionEnabled = YES;

if (【url.absoluteString hasPrefix:@"http"】 ||【url.absoluteString hasPrefix:@"https"】){//网络视频 添加网络监听

self.playeritem = 【AVPlayerItem playerItemWithURL:【NSURL URLWithString:【url.absoluteString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding】】】;

【self addnetworkNotification】;

}else{//本地

self.playeritem = 【AVPlayerItem playerItemWithURL:url】;

}

【self.player replaceCurrentItemWithPlayerItem:self.playeritem】;

【self addNotification】;

}

//网络监控

- (void)addnetworkNotification{

【【NSNotificationCenter defaultCenter】addObserver:self selector:@selector(netchange:) name:kReachabilityChangedNotification object:nil】;

}

//网络状态

- (void)netchange://代码效果参考:http://www.zidongmutanji.com/zsjx/246679.html

(NSNotification )noti{

Reachability reach = 【noti object】;

if (【reach isKindOfClass:【Reachability class】】) {

NetworkStatus status = 【reach currentReachabilityStatus】;

【self updatenetworkstatus:status】;

}

}

//根据网络 改变播放状态

- (void)updatenetworkstatus:(NetworkStatus)status{

switch (status) {

case NotReachable:{//无网络

【self notreachable】;

}break;

case ReachableViaWiFi:{

self.errorlable.hidden = YES;

self.reconnectbutton.hidden = YES;

self.gesturesview.userInteractionEnabled = YES;

}break;

case ReachableViaWWAN:{//2g3g4g

【self stopplay】;

UIAlertView wwanalert = 【【UIAlertView alloc】initWithTitle:@"温馨提示" message:@"当前处于2g/3g/4g模式下,继续播放会产生流量!" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil】;

wwanalert.tag = 2;

【wwanalert show】;

}break;

default:

break;

}

}

//alert代理

- (void)alertView:(UIAlertView )alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{

switch (alertView.tag) {

case 1:{

if (buttonIndex != alertView.cancelButtonIndex) {

【self reachableviawifi:self.playerurl】;

}

}break;

case 2:{

if (buttonIndex != alertView.cancelButtonIndex) {

【self startplay】;

}

}break;

default:

break;

}

}

//通知

- (void)addNotification {

//kvo监听

【self.playeritem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil】;

// 监听loadedTimeRanges属性

【self.playeritem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil】;

// 播放完成通知

【【NSNotificationCenter defaultCenter】 addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil】;

// 前台通知

【【NSNotificationCenter defaultCenter】 addObserver:self selector:@selector(enterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil】;

// 后台通知

【【NSNotificationCenter defaultCenter】 addObserver:self selector:@selector(enterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil】;

【self monitoringPlayback:self.playeritem】;

}

//键值观察

- (void)observeValueForKeyPath:(NSString )keyPath ofObject:(id)object change:(NSDictionary )change context:(void )context {

if (【keyPath isEqualToString:@"status"】){//状态

AVPlayerStatus status = 【【change objectForKey:@"new"】 intValue】;

if (status == AVPlayerStatusReadyToPlay) {

AVPlayerItem item = (AVPlayerItem )object;

self.gesturesview.slider.maximumValue = CMTimeGetSeconds(item.duration);// 获取视频长度 设置视频时间

self.gesturesview.maxtimelable.text = 【self convertTime:CMTimeGetSeconds(item.duration)】;// 设置视频时间

self.isstartplay ? 【self startplay】:【self stopplay】;// 进来开始播放 进来暂停暂停

}else if (status == AVPlayerStatusFailed){//当地址不对 无网络时 会走此方法

NSLog(@"AVPlayerStatusFailed");

【self stopplay】;

self.errorlable.hidden = NO;

self.errorlable.text = @"亲,视频解析出错了!";

self.gesturesview.userInteractionEnabled = NO;

}else {

NSLog(@"AVPlayerStatusUnknown");

}

} else if (【keyPath isEqualToString:@"loadedTimeRanges"】){

NSTimeInterval timeInterval = 【self availableDurationRanges】; // 缓冲时间

CGFloat totalDuration = CMTimeGetSeconds(self.playeritem.duration); // 总时间

【self.gesturesview.progressview setProgress:timeInterval / totalDuration animated:YES】;//更新缓冲条

}

}

// 相对格林时间

- (NSString )convertTime:(CGFloat)second {

NSDate date = 【NSDate dateWithTimeIntervalSince1970:second】;

NSDateFormatter formatter = 【【NSDateFormatter alloc】 init】;

if (second / 3600 >= 1) {

【formatter setDateFormat:@"HH:mm:ss"】;

} else {

【formatter setDateFormat:@"mm:ss"】;

}

NSString showTimeNew = 【formatter stringFromDate:date】;

return showTimeNew;

}

// 缓冲时间 计算

- (NSTimeInterval)availableDurationRanges {

NSArray loadedTimeRanges = 【self.playeritem loadedTimeRanges】; // 获取item的缓冲数组

CMTimeRange timeRange = 【loadedTimeRanges.firstObject CMTimeRangeValue】; // 获取缓冲区域

float startSeconds = CMTimeGetSeconds(timeRange.start);// CMTimeRange 结构体 start duration 表示起始位置 和 持续时间

float durationSeconds = CMTimeGetSeconds(timeRange.duration);

NSTimeInterval result = startSeconds + durationSeconds; // 计算总缓冲时间 = start + duration

return result;

}

//播放完成通知

- (void)playbackFinished:(NSNotification )notification {

self.playeritem = 【notification object】;

【self.playeritem seekToTime:kCMTimeZero】; // 跳转到初始

【self stopplay】;//暂停

self.gesturesview.hidden = NO;

//【self startplay】;//开始 无限循环

}

//进入前台

- (void)enterForeground:(NSNotification )notification {

NSSet currentsset = self.telephone.currentCalls;

if (currentsset == nil) {

if(!self.isplaying){//切刀后台最后一刻的状态

【self startplay】;

}

}

}

//进入后台

- (void)enterBackground:(NSNotification )notification {

【self stopplay】;

}

// 观察播放进度 每秒执行1次, CMTime 为1秒

- (void)monitoringPlayback:(AVPlayerItem )item {

__weak typeof(self)WeakSelf = self;

self.playTimeObserver = 【self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {

//当前播放时间 秒

float currentPlayTime = (double)item.currentTime.value/ item.currentTime.timescale;

WeakSelf.gesturesview.slider.value = currentPlayTime;

WeakSelf.gesturesview.mintimelable.text = 【WeakSelf convertTime:currentPlayTime】;

}】;

}

//开始播放

- (void)startplay{

self.isplaying = YES;

【self.player play】;

self.gesturesview.leftplayerbutton.selected = NO;

self.gesturesview.rightplayerbutton.selected = NO;

【self performSelector:@selector(showgesturesview) withObject:nil afterDelay:5.0】;

}

//暂停播放

- (void)stopplay{

self.isplaying = NO;

【self.player pause】;

self.gesturesview.leftplayerbutton.selected = YES;

self.gesturesview.rightplayerbutton.selected = YES;

【【self class】 cancelPreviousPerformRequestsWithTarget:self selector:@selector(showgesturesview) object:nil】;

}

//约束

- (void)layoutSubviews{

【super layoutSubviews】;

self.playerlayer.frame = self.bounds;

self.gesturesview.frame = self.bounds;

self.errorlable.frame = CGRectMake(self.frame.size.width/ 2 - 150, self.frame.size.height / 2 -20, 300, 20);

self.reconnectbutton.frame = CGRectMake(self.frame.size.width/ 2 - 30, CGRectGetMaxY(self.errorlable.frame) + 10, 60, 30);

}

//第一次进入页面判断网络状态使用

- (NetworkStatus)CheckNowNetWorkStatus{

NSArray subviews = 【【【【UIApplication sharedApplication】 valueForKey:@"statusBar"】 valueForKey:@"foregroundView"】 subviews】;

NSNumber dataNetworkItemView = nil;

for (id subview in subviews) {

if(【subview isKindOfClass:【NSClassFromString(@"UIStatusBarDataNetworkItemView") class】】) {

dataNetworkItemView = subview;

break;

}

}

switch (【【dataNetworkItemView valueForKey:@"dataNetworkType"】integerValue】) {

case 0:NSLog(@"No wifi or cellular");

return NotReachable;

break;

case 1:NSLog(@"2G");

case 2:NSLog(@"3G");

case 3:NSLog(@"4G");

nbsp

相关文章
|
1月前
|
前端开发
【threejs教程】终于搞明白了!原来threejs中的透视相机这么简单!
【8月更文挑战第5天】深入学习threejs中的透视相机!
34 2
【threejs教程】终于搞明白了!原来threejs中的透视相机这么简单!
|
3月前
|
监控
一篇文章讲明白iosavplayer视频播放器
一篇文章讲明白iosavplayer视频播放器
16 0
|
3月前
|
人工智能 缓存 Java
技术经验解读:【转】详细解析用C#写的小游戏《彩色连珠》(附源代码)
技术经验解读:【转】详细解析用C#写的小游戏《彩色连珠》(附源代码)
19 0
|
12月前
|
人工智能 JSON API
|
4月前
|
存储 开发者
彻底搞懂微信小游戏制作工具中的函数
彻底搞懂微信小游戏制作工具中的函数
122 0
|
4月前
|
XML Java Android开发
Android App开发手机阅读中贝塞尔曲线的原理讲解及实现波浪起伏动画实战(附源码和演示视频 可直接使用)
Android App开发手机阅读中贝塞尔曲线的原理讲解及实现波浪起伏动画实战(附源码和演示视频 可直接使用)
95 0
|
图形学
Unity3D简陋版跑酷游戏
Unity3D简陋版跑酷游戏
Unity3D简陋版跑酷游戏
|
编解码 芯片
实战贴:开源GUI STemWin在小熊派上的移植(显示很漂亮、很细腻,棒!)
实战贴:开源GUI STemWin在小熊派上的移植(显示很漂亮、很细腻,棒!)
377 2
|
编解码 人工智能 计算机视觉
一段蛋疼的代码:超不清视频播放器
一幅图像全部转成字符序列后,就可以直接在控制台输出了。对于一个视频来说,只需要将每一帧都转换后输出,并按照一定的时间间隔清屏、输出下一帧,即可达到我们的需要的效果。
|
编解码 API C#
c#万能视频播放器(附代码)
c#万能视频播放器(附代码)
335 0