CustomAVPlayer


view类:

#import <UIKit/UIKit.h>


#import <AVFoundation/AVFoundation.h>


@interface PlayViwe : UIView


@property(nonatomic,strong)AVPlayer *player;
@property(nonatomic,strong)UIView *bottom;
@property(nonatomic,strong)AVPlayerItem *playerItem;
@property(nonatomic,strong)UISlider *slider;


- (id)initWithFrame:(CGRect)frame WithVideoStr:(NSString *)videoStr;


- (void)PlayOrPause;




@end



#import "PlayViwe.h"
#import <AVFoundation/AVFoundation.h>


static void *PlayViewCMTimeValue = &PlayViewCMTimeValue;
static void *AVPlayerDemoPlaybackViewControllerStatusObservationContext;


@implementation PlayViwe


/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/


- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}


- (id)initWithFrame:(CGRect)frame WithVideoStr:(NSString *)videoStr{
    self = [super init];
    if (self) {
        self.frame = frame;
        
        NSURL *sourceMovieURL = [NSURL fileURLWithPath:videoStr];
        AVAsset *movieAsset = [AVURLAsset URLAssetWithURL:sourceMovieURL options:nil];
        self.playerItem = [AVPlayerItem playerItemWithAsset:movieAsset];
        self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
        AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
        playerLayer.frame = self.layer.bounds;
        playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
        
        [self.layer addSublayer:playerLayer];
        [self.player play];
        
        self.bottom = [[UIView alloc]initWithFrame:CGRectMake(0, self.frame.size.height - 40, self.frame.size.width, 40)];
        self.bottom.backgroundColor = [UIColor grayColor];
        UIButton *playBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        [playBtn addTarget:self action:@selector(PlayOrPause) forControlEvents:UIControlEventTouchUpInside];
        playBtn.frame = CGRectMake(5, 5, 30, 30);
        playBtn.backgroundColor = [UIColor redColor];
        [self.bottom addSubview:playBtn];
        [self addSubview:self.bottom];
        
        self.slider = [[UISlider alloc]initWithFrame:CGRectMake(45, 5, 200, 30)];
        self.slider.minimumValue = 0.0;
        self.slider.maximumValue = CMTimeGetSeconds(movieAsset.duration);
        self.slider.value = 0.0;//指定初始值
        [self.slider addTarget:self action:@selector(updateValue:) forControlEvents:UIControlEventTouchUpInside];//设置响应事件
        [self.bottom addSubview:self.slider];
        
        // 单击的 Recognizer
        UITapGestureRecognizer* singleRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTapFrom)];
        singleRecognizer.numberOfTapsRequired = 1; // 单击
        [self addGestureRecognizer:singleRecognizer];
        
        [self.playerItem addObserver:self
                          forKeyPath:@"status"
                             options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                             context:AVPlayerDemoPlaybackViewControllerStatusObservationContext];
        [self initScrubberTimer];
        
    }
    return self;
}




- (double)duration
{
    AVPlayerItem *playerItem = [[self player] currentItem];
    
    if ([playerItem status] == AVPlayerItemStatusReadyToPlay)
        return CMTimeGetSeconds([[playerItem asset] duration]);
    else
        return 0.f;
}


- (double)currentTime
{
    return CMTimeGetSeconds([[self player] currentTime]);
}


- (void)setCurrentTime:(double)time
{
    [[self player] seekToTime:CMTimeMakeWithSeconds(time, 1)];
}


- (void)PlayOrPause{
    
    if ([[self player] rate] != 1.f) {
        if ([self currentTime] == [self duration])
            [self setCurrentTime:0.f];
        [[self player] play];
    } else {
        [[self player] pause];
    }
    
    CMTime time = [self.player currentTime];
    NSLog(@"%lld",self.playerItem.duration.value/self.playerItem.duration.timescale);
    NSLog(@"%lld",time.value/time.timescale);
    
}




#pragma mark - 手势方法
- (void)handleSingleTapFrom{
    
    [UIView animateWithDuration:0.5 animations:^{
        
        if (self.bottom.alpha == 0.0) {
            self.bottom.alpha = 1.0;
        }else{
            self.bottom.alpha = 0.0;
        }
        
    } completion:^(BOOL finish){
        
    }];
}


#pragma mark - slider
- (void)updateValue:(UISlider *)slider{
    
    [self.player seekToTime:CMTimeMakeWithSeconds(slider.value, 1)];
    
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    /* AVPlayerItem "status" property value observer. */
    if (context == AVPlayerDemoPlaybackViewControllerStatusObservationContext)
    {
        
        AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
        switch (status)
        {
                /* Indicates that the status of the player is not yet known because
                 it has not tried to load new media resources for playback */
            case AVPlayerStatusUnknown:
            {
                
            }
                break;
                
            case AVPlayerStatusReadyToPlay:
            {
                /* Once the AVPlayerItem becomes ready to play, i.e.
                 [playerItem status] == AVPlayerItemStatusReadyToPlay,
                 its duration can be fetched from the item. */
                
                [self initScrubberTimer];
                
            }
                break;
                
            case AVPlayerStatusFailed:
            {
                
            }
                break;
        }
    }
    
}


#pragma  maik - 监听
-(void)initScrubberTimer
{
    double interval = .1f;
    
    CMTime playerDuration = [self playerItemDuration];
    if (CMTIME_IS_INVALID(playerDuration))
    {
        return;
    }
    double duration = CMTimeGetSeconds(playerDuration);
    if (isfinite(duration))
    {
        CGFloat width = CGRectGetWidth([self.slider bounds]);
        interval = 0.5f * duration / width;
    }
    NSLog(@"interva === %f",interval);
    
    __weak typeof(self) weakSelf = self;
    
    /* Update the scrubber during normal playback. */
    [weakSelf.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(interval, NSEC_PER_SEC)
                                                  queue:NULL /* If you pass NULL, the main queue is used. */
                                             usingBlock:^(CMTime time)
     {
         [self syncScrubber];
     }];
    
}


/* Set the scrubber based on the player current time. */
- (void)syncScrubber
{
    NSLog(@"syncScrubber");
    CMTime playerDuration = [self playerItemDuration];
    if (CMTIME_IS_INVALID(playerDuration))
    {
        self.slider.minimumValue = 0.0;
        return;
    }
    
    double duration = CMTimeGetSeconds(playerDuration);
    if (isfinite(duration))
    {
        float minValue = [self.slider minimumValue];
        float maxValue = [self.slider maximumValue];
        double time = CMTimeGetSeconds([self.player currentTime]);
        NSLog(@"时间 :: %f",(maxValue - minValue) * time / duration + minValue);
        [self.slider setValue:(maxValue - minValue) * time / duration + minValue];
    }
}


- (CMTime)playerItemDuration
{
    AVPlayerItem *playerItem = [self.player currentItem];
    NSLog(@"%ld",playerItem.status);
    if (playerItem.status == AVPlayerItemStatusReadyToPlay)
    {
        return([playerItem duration]);
    }
    
    return(kCMTimeInvalid);
}


@end


#import <UIKit/UIKit.h>


@interface ViewController : UIViewController




@end


//视图控制器类

#import "ViewController.h"
#import "PlayViwe.h"


@interface ViewController (){
    
    PlayViwe *_playView;
}


@end


@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    NSString *videoStr = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp4"];
    _playView = [[PlayViwe alloc] initWithFrame:self.view.bounds WithVideoStr: videoStr];
    
    [self.view addSubview:_playView];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}




//横竖屏
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return YES;
}




@end







// src/main/ets/pages/VideoPage.ets import router from '@ohos.router'; import media from '@ohos.multimedia.media'; // 定义视频项类型 interface VideoItem { id: number; title: string; cover: Resource; url: string; isPlaying: boolean; } @Entry @Component struct VideoPage { // 明确定义视频数据及类型 @State videoList: VideoItem[] = [ { id: 1, title: '今日热点新闻简报', cover: $r('app.media.1'), url: 'https://video19.ifeng.com/video09/2025/03/31/p7312398788159607176-102-171116.mp4', isPlaying: false }, ]; private avPlayers: media.AVPlayer[] = []; aboutToAppear() { // 初始化AVPlayer实例 this.avPlayers = this.videoList.map(()=>new media.createAVPlayer()); } // 当前选中的tab @State currentTab: string = 'video'; build() { Column() { // 视频列表 List({ space: 10 }) { ForEach(this.videoList, (item: VideoItem, index: number) => { ListItem() { Column() { // 视频播放器 Video({ src: item.url, previewUri: item.cover, controller: this.avPlayers[index] }) .width('100%') .height(200) .autoPlay(false) .controls(true) .onStart(() => { this.videoList[index].isPlaying = true; }) .onPause(() => { this.videoList[index].isPlaying = false; }) // 视频标题 Text(item.title) .fontSize(16) .fontWeight(FontWeight.Bold) .margin({ top: 8, left: 10, right: 10 }) // 播放/暂停按钮 Button(this.videoList[index].isPlaying ? '暂停' : '播放') .width(120) .margin(10) .onClick(() => { if (this.videoList[index].isPlaying) { this.avPlayers[index].pause(); } else { // 暂停其他正在播放的视频 this.videoList.forEach((video, i) => { if (i !== index && video.isPlaying) { this.avPlayers[i].pause(); this.videoList[i].isPlaying = false; } }); this.avPlayers[index].play(); } }) } .width('100%') .backgroundColor('#FFFFFF') .borderRadius(8) .padding(10) } }) } .width('100%') .layoutWeight(1) // 底部导航栏 this.buildTextBottomTabs() } .width('100%') .height('100%') .backgroundColor('#F5F5F5') } // 纯文字底部导航栏 @Builder buildTextBottomTabs() { Row() { // 首页Tab Text('首页') .fontSize(16) .fontColor(this.currentTab === 'home' ? $r('app.color.primary_color') : '#999999') .fontWeight(this.currentTab === 'home' ? FontWeight.Bold : FontWeight.Normal) .onClick(() => { if (this.currentTab !== 'home') { router.replace({ url: 'pages/Index' }); } }) // 分隔线 Divider() .vertical(true) .height(20) .margin({ left: 30, right: 30 }) .color('#EEEEEE') // 视频Tab(当前页) Text('视频') .fontSize(16) .fontColor($r('app.color.primary_color')) .fontWeight(FontWeight.Bold) // 分隔线 Divider() .vertical(true) .height(20) .margin({ left: 30, right: 30 }) .color('#EEEEEE') // 我的Tab Text('我的') .fontSize(16) .fontColor(this.currentTab === 'me' ? $r('app.color.primary_color') : '#999999') .fontWeight(this.currentTab === 'me' ? FontWeight.Bold : FontWeight.Normal) .onClick(() => { if (this.currentTab !== 'me') { router.replace({ url: 'pages/ProfilePage' }); } }) } .width('100%') .height(50) .padding(10) .backgroundColor('#FFFFFF') .margin({ top: 10 }) .justifyContent(FlexAlign.Center) } } 报错Function return type inference is limited (arkts-no-implicit-return-types) <ArkTSCheck> Only a void function can be called with the 'new' keyword. <ArkTSCheck>和Type 'AVPlayer' is missing the following properties from type 'VideoController': start, setCurrentTime, requestFullscreen, exitFullscreen <ArkTSCheck>,纠正并给出完整代码
07-13
通过短时倒谱(Cepstrogram)计算进行时-倒频分析研究(Matlab代码实现)内容概要:本文主要介绍了一项关于短时倒谱(Cepstrogram)计算在时-倒频分析中的研究,并提供了相应的Matlab代码实现。通过短时倒谱分析方法,能够有效提取信号在时间与倒频率域的特征,适用于语音、机械振动、生物医学等领域的信号处理与故障诊断。文中阐述了倒谱分析的基本原理、短时倒谱的计算流程及其在实际工程中的应用价值,展示了如何利用Matlab进行时-倒频图的可视化与分析,帮助研究人员深入理解非平稳信号的周期性成分与谐波结构。; 适合人群:具备一定信号处理基础,熟悉Matlab编程,从事电子信息、机械工程、生物医学或通信等相关领域科研工作的研究生、工程师及科研人员。; 使用场景及目标:①掌握倒谱分析与短时倒谱的基本理论及其与傅里叶变换的关系;②学习如何用Matlab实现Cepstrogram并应用于实际信号的周期性特征提取与故障诊断;③为语音识别、机械设备状态监测、振动信号分析等研究提供技术支持与方法参考; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,先理解倒谱的基本概念再逐步实现短时倒谱分析,注意参数设置如窗长、重叠率等对结果的影响,同时可将该方法与其他时频分析方法(如STFT、小波变换)进行对比,以提升对信号特征的理解能力。
先看效果: https://pan.quark.cn/s/aceef06006d4 OJBetter OJBetter 是一个 Tampermonkey 脚本项目,旨在提升你在各个在线评测系统(Online Judge, OJ)网站的使用体验。 通过添加多项实用功能,改善网站界面和用户交互,使你的编程竞赛之旅更加高效、便捷。 ----- 简体中文 ----- 安装 主要功能 安装脚本,你可以获得: 黑暗模式支持:为网站添加黑暗模式,夜晚刷题不伤眼。 网站本地化:将网站的主要文本替换成你选择的语言。 题目翻译:一键翻译题目为目标语言,同时确保不破坏 LaTeX 公式。 Clist Rating 分数:显示题目的 Clist Rating 分数数据。 快捷跳转:一键跳转到该题在洛谷、VJudge 的对应页面。 代码编辑器:在题目页下方集成 Monaco 代码编辑器,支持自动保存、快捷提交、在线测试运行等功能。 一些其他小功能…… [!NOTE] 点击 网页右上角 的 按钮,即可打开设置面板, 绝大部分功能均提供了帮助文本,鼠标悬浮在 ”? 图标“ 上即可查看。 使用文档 了解更多详细信息和使用指南,请访问 Wiki 页面。 如何贡献 如果你有任何想法或功能请求,欢迎通过 Pull Requests 或 Issues 与我们分享。 改善翻译质量 项目的非中文版本主要通过机器翻译(Deepl & Google)完成,托管在 Crowdin 上。 如果你愿意帮助改进翻译,使其更准确、自然,请访问 Crowdin 项目页面 贡献你的力量。 支持其他OJ? 由于作者精力有限,并不会维护太多的类似脚本, 如果你有兴趣将此脚本适配到其他在线评测系统,非常欢迎,你只需要遵守 GP...
企业微信私域流量管理系统是一套基于Go语言和React框架开发的高质量企业级客户关系管理解决方案,专门用于企业微信生态下的私域流量运营与管理。该系统遵守Apache2.0开源协议,支持免费商用,为企业提供了一套完整、高效且成本可控的私域流量管理工具。 系统采用现代化的技术架构,后端使用Go语言开发,确保了高性能和高并发处理能力;前端采用React框架,提供了流畅的用户交互体验。数据库方面,系统使用MySQL进行结构化数据存储,Redis用于缓存和会话管理,有效提升了系统的响应速度和数据处理效率。此外,系统支持Docker容器化部署,简化了部署和维护流程,提高了系统的可移植性和扩展性。 在架构设计上,系统融合了MVC(模型-视图-控制器)模式、分层架构和事件驱动架构,确保了代码的清晰结构和模块化设计。同时,系统应用了多种设计模式,如单例模式、工厂模式和观察者模式,增强了系统的可维护性和可扩展性。这些技术特点使得系统不仅适用于毕业设计项目,还能满足实际企业应用的需求。 系统的主要功能包括企业微信集成、客户管理、营销活动管理、数据分析与报表生成等。通过企业微信接口,系统能够无缝对接企业微信平台,实现客户信息的自动同步、消息推送、群发管理等功能。客户管理模块支持客户标签、分组、跟进记录等操作,帮助企业精细化运营私域流量。营销活动模块提供了活动创建、执行、监控和效果分析的全流程管理,助力企业提升营销效率。数据分析模块则通过可视化报表展示客户行为、活动效果等关键指标,为企业决策提供数据支持。 应用场景广泛,适用于各类需要进行私域流量运营的企业,如电商、教育、金融、零售等行业。无论是初创公司还是大型企业,都可以通过该系统低成本地搭建自己的私域流量管理体系,提升客户粘性和转化率。系统的高质量和免费商用特性,使其成为毕业设计项目的理想选择,学生可以通过学习和实践该系统,掌握Go、Re
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值