开源 Objective-C IOS 应用开发(十九)视频的播放

  文章的目的为了记录使用Objective-C 进行IOS app 开发学习的经历。本职为嵌入式软件开发,公司安排开发app,临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

 相关链接:

开源 Objective-C IOS 应用开发(一)macOS 的使用

开源 Objective-C IOS 应用开发(二)Xcode安装

开源 Objective-C IOS 应用开发(三)第一个iPhone的APP

开源 Objective-C IOS 应用开发(四)Xcode工程文件结构

开源 Objective-C IOS 应用开发(五)iOS操作(action)和输出口(Outlet)

开源 Objective-C IOS 应用开发(六)Objective-C 和 C语言

开源 Objective-C IOS 应用开发(七)Objective-C核心代码示例

开源 Objective-C IOS 应用开发(八)常见控件UI

开源 Objective-C IOS 应用开发(九)复杂控件-tableview

开源 Objective-C IOS 应用开发(十)数据持久化--文件

开源 Objective-C IOS 应用开发(十一)数据持久化--sqlite

开源 Objective-C IOS 应用开发(十二)通讯--ble

开源 Objective-C IOS 应用开发(十三)通讯--Http访问

开源 Objective-C IOS 应用开发(十四)传感器--陀螺仪和gps

开源 Objective-C IOS 应用开发(十五)通讯--蓝牙ble扫描

开源 Objective-C IOS 应用开发(十六)Storyboard模式下的纯代码界面

开源 Objective-C IOS 应用开发(十七)CAF音频的录制

开源 Objective-C IOS 应用开发(十八)音频的播放

开源 Objective-C IOS 应用开发(十九)视频的播放

开源 Objective-C IOS 应用开发(二十)多线程处理

开源 Objective-C IOS 应用开发(二十一)自定义控件--示波器

开源 Objective-C IOS 应用开发(二十二)自定义控件--车速仪表盘

 推荐链接:

开源 Arkts 鸿蒙应用 开发(一)工程文件分析-优快云博客

开源 Arkts 鸿蒙应用 开发(二)封装库.har制作和应用-优快云博客

开源 Arkts 鸿蒙应用 开发(三)Arkts的介绍-优快云博客

开源 Arkts 鸿蒙应用 开发(四)布局和常用控件-优快云博客

开源 Arkts 鸿蒙应用 开发(五)控件组成和复杂控件-优快云博客

开源 Arkts 鸿蒙应用 开发(六)数据持久--文件和首选项存储-优快云博客

开源 Arkts 鸿蒙应用 开发(七)数据持久--sqlite关系数据库-优快云博客

开源 Arkts 鸿蒙应用 开发(八)多媒体--相册和相机-优快云博客

开源 Arkts 鸿蒙应用 开发(九)通讯--tcp客户端-优快云博客

开源 Arkts 鸿蒙应用 开发(十)通讯--Http-优快云博客

开源 Arkts 鸿蒙应用 开发(十一)证书和包名修改-优快云博客

开源 Arkts 鸿蒙应用 开发(十二)传感器的使用-优快云博客

开源 Arkts 鸿蒙应用 开发(十三)音频--MP3播放_arkts avplayer播放音频 mp3-优快云博客

开源 Arkts 鸿蒙应用 开发(十四)线程--任务池(taskpool)-优快云博客

开源 Arkts 鸿蒙应用 开发(十五)自定义绘图控件--仪表盘-优快云博客

开源 Arkts 鸿蒙应用 开发(十六)自定义绘图控件--波形图-优快云博客

开源 Arkts 鸿蒙应用 开发(十七)通讯--http多文件下载-优快云博客

开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器-优快云博客

推荐链接:

开源 java android app 开发(一)开发环境的搭建-优快云博客

开源 java android app 开发(二)工程文件结构-优快云博客

开源 java android app 开发(三)GUI界面布局和常用组件-优快云博客

开源 java android app 开发(四)GUI界面重要组件-优快云博客

开源 java android app 开发(五)文件和数据库存储-优快云博客

开源 java android app 开发(六)多媒体使用-优快云博客

开源 java android app 开发(七)通讯之Tcp和Http-优快云博客

开源 java android app 开发(八)通讯之Mqtt和Ble-优快云博客

开源 java android app 开发(九)后台之线程和服务-优快云博客

开源 java android app 开发(十)广播机制-优快云博客

开源 java android app 开发(十一)调试、发布-优快云博客

开源 java android app 开发(十二)封库.aar-优快云博客
 

本章内容主要是使用iphone进行视频播放。

目录:

1.手机演示

2.所有源码

3.源码分析

一、手机演示

二、所有源码

AppDelegate.h文件

#import <UIKit/UIKit.h>

@class VideoViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) VideoViewController *videoViewController;

@end

AppDelegate.m文件

#import "AppDelegate.h"
#import "VideoViewController.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 创建主窗口
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    
    // 创建视频播放视图控制器
    self.videoViewController = [[VideoViewController alloc] init];
    
    // 设置根视图控制器
    self.window.rootViewController = self.videoViewController;
    
    // 显示窗口
    [self.window makeKeyAndVisible];
    
    return YES;
}

@end

VideoPlayer.h文件

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface VideoPlayer : UIView

@property (strong, nonatomic) AVPlayer *player;
@property (strong, nonatomic) AVPlayerLayer *playerLayer;
@property (strong, nonatomic) UIButton *playPauseButton;
@property (strong, nonatomic) UIButton *fullscreenButton;
@property (strong, nonatomic) UIView *controlsView;
@property (strong, nonatomic) UISlider *progressSlider;
@property (strong, nonatomic) UILabel *currentTimeLabel;
@property (strong, nonatomic) UILabel *totalTimeLabel;

- (void)setupPlayerWithURL:(NSURL *)videoURL;
- (void)play;
- (void)pause;
- (void)togglePlayPause;

@end

NS_ASSUME_NONNULL_END

VideoPlayer.m文件

#import "VideoPlayer.h"

@interface VideoPlayer()

@property (strong, nonatomic) id timeObserver;
@property (assign, nonatomic) BOOL controlsHidden;

@end

@implementation VideoPlayer

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setupUI];
        [self setupGestures];
    }
    return self;
}

- (void)setupUI {
    self.backgroundColor = [UIColor blackColor];
    
    // 创建播放器图层
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:nil];
    self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
    [self.layer addSublayer:self.playerLayer];
    
    // 创建控制界面
    self.controlsView = [[UIView alloc] init];
    self.controlsView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.7];
    [self addSubview:self.controlsView];
    
    // 播放/暂停按钮
    self.playPauseButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.playPauseButton setTitle:@"播放" forState:UIControlStateNormal];
    [self.playPauseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [self.playPauseButton addTarget:self action:@selector(togglePlayPause) forControlEvents:UIControlEventTouchUpInside];
    [self.controlsView addSubview:self.playPauseButton];
    
    // 进度条
    self.progressSlider = [[UISlider alloc] init];
    self.progressSlider.minimumValue = 0.0;
    [self.progressSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
    [self.progressSlider addTarget:self action:@selector(sliderTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
    [self.controlsView addSubview:self.progressSlider];
    
    // 时间标签
    self.currentTimeLabel = [[UILabel alloc] init];
    self.currentTimeLabel.text = @"00:00";
    self.currentTimeLabel.textColor = [UIColor whiteColor];
    self.currentTimeLabel.font = [UIFont systemFontOfSize:12];
    [self.controlsView addSubview:self.currentTimeLabel];
    
    self.totalTimeLabel = [[UILabel alloc] init];
    self.totalTimeLabel.text = @"00:00";
    self.totalTimeLabel.textColor = [UIColor whiteColor];
    self.totalTimeLabel.font = [UIFont systemFontOfSize:12];
    [self.controlsView addSubview:self.totalTimeLabel];
    
    // 全屏按钮
    self.fullscreenButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.fullscreenButton setTitle:@"全屏" forState:UIControlStateNormal];
    [self.fullscreenButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [self.controlsView addSubview:self.fullscreenButton];
    
    self.controlsHidden = NO;
    [self hideControlsAfterDelay];
}

- (void)setupGestures {
    // 添加点击手势来控制界面显示/隐藏
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControls)];
    [self addGestureRecognizer:tapGesture];
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    // 设置播放器图层大小
    self.playerLayer.frame = self.bounds;
    
    // 设置控制界面布局
    CGFloat controlsHeight = 60.0;
    self.controlsView.frame = CGRectMake(0, self.bounds.size.height - controlsHeight,
                                        self.bounds.size.width, controlsHeight);
    
    CGFloat padding = 10.0;
    CGFloat buttonWidth = 60.0;
    
    self.playPauseButton.frame = CGRectMake(padding, 10, buttonWidth, 30);
    
    self.currentTimeLabel.frame = CGRectMake(CGRectGetMaxX(self.playPauseButton.frame) + padding, 15, 40, 20);
    
    CGFloat sliderX = CGRectGetMaxX(self.currentTimeLabel.frame) + padding;
    CGFloat fullscreenButtonWidth = 50.0;
    self.fullscreenButton.frame = CGRectMake(self.bounds.size.width - fullscreenButtonWidth - padding, 10,
                                           fullscreenButtonWidth, 30);
    
    self.totalTimeLabel.frame = CGRectMake(CGRectGetMinX(self.fullscreenButton.frame) - 40 - padding, 15, 40, 20);
    
    CGFloat sliderWidth = CGRectGetMinX(self.totalTimeLabel.frame) - sliderX - padding;
    self.progressSlider.frame = CGRectMake(sliderX, 15, sliderWidth, 20);
}

- (void)setupPlayerWithURL:(NSURL *)videoURL {
    // 创建AVPlayerItem
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:videoURL];
    
    // 创建AVPlayer
    self.player = [AVPlayer playerWithPlayerItem:playerItem];
    self.playerLayer.player = self.player;
    
    // 添加时间观察者
    __weak typeof(self) weakSelf = self;
    self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
                                                                  queue:dispatch_get_main_queue()
                                                             usingBlock:^(CMTime time) {
        [weakSelf updateProgress];
    }];
    
    // 监听播放结束
    [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(playerItemDidReachEnd:)
                                               name:AVPlayerItemDidPlayToEndTimeNotification
                                             object:playerItem];
}

- (void)updateProgress {
    if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
        Float64 currentTime = CMTimeGetSeconds(self.player.currentTime);
        Float64 duration = CMTimeGetSeconds(self.player.currentItem.duration);
        
        if (isfinite(duration)) {
            self.progressSlider.value = currentTime / duration;
            self.currentTimeLabel.text = [self timeStringFromSeconds:currentTime];
            self.totalTimeLabel.text = [self timeStringFromSeconds:duration];
        }
    }
}

- (NSString *)timeStringFromSeconds:(Float64)seconds {
    NSInteger minutes = (NSInteger)seconds / 60;
    NSInteger secs = (NSInteger)seconds % 60;
    return [NSString stringWithFormat:@"%02ld:%02ld", (long)minutes, (long)secs];
}

- (void)play {
    [self.player play];
    [self.playPauseButton setTitle:@"暂停" forState:UIControlStateNormal];
}

- (void)pause {
    [self.player pause];
    [self.playPauseButton setTitle:@"播放" forState:UIControlStateNormal];
}

- (void)togglePlayPause {
    if (self.player.rate == 0.0) {
        [self play];
    } else {
        [self pause];
    }
}

- (void)sliderValueChanged:(UISlider *)slider {
    [self.player removeTimeObserver:self.timeObserver];
}

- (void)sliderTouchUpInside:(UISlider *)slider {
    if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
        Float64 duration = CMTimeGetSeconds(self.player.currentItem.duration);
        Float64 seekTime = duration * slider.value;
        
        [self.player seekToTime:CMTimeMakeWithSeconds(seekTime, NSEC_PER_SEC)];
        
        // 重新添加时间观察者
        __weak typeof(self) weakSelf = self;
        self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
                                                                      queue:dispatch_get_main_queue()
                                                                 usingBlock:^(CMTime time) {
            [weakSelf updateProgress];
        }];
    }
}

- (void)playerItemDidReachEnd:(NSNotification *)notification {
    [self.player seekToTime:kCMTimeZero];
    [self pause];
}

- (void)toggleControls {
    self.controlsHidden = !self.controlsHidden;
    
    [UIView animateWithDuration:0.3 animations:^{
        self.controlsView.alpha = self.controlsHidden ? 0.0 : 1.0;
    }];
    
    if (!self.controlsHidden) {
        [self hideControlsAfterDelay];
    }
}

- (void)hideControlsAfterDelay {
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideControls) object:nil];
    [self performSelector:@selector(hideControls) withObject:nil afterDelay:3.0];
}

- (void)hideControls {
    [UIView animateWithDuration:0.3 animations:^{
        self.controlsView.alpha = 0.0;
    }];
    self.controlsHidden = YES;
}

- (void)dealloc {
    if (self.timeObserver) {
        [self.player removeTimeObserver:self.timeObserver];
    }
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end

VideoViewController.h文件

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface VideoViewController : UIViewController

@end

NS_ASSUME_NONNULL_END

VideoViewController.m文件

#import "VideoViewController.h"
#import "VideoPlayer.h"

@interface VideoViewController ()

@property (strong, nonatomic) VideoPlayer *videoPlayer;

@end

@implementation VideoViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setupUI];
    [self setupVideoPlayer];
}

- (void)setupUI {
    self.view.backgroundColor = [UIColor whiteColor];
    
    // 创建标题
    UILabel *titleLabel = [[UILabel alloc] init];
    titleLabel.text = @"视频播放器";
    titleLabel.font = [UIFont boldSystemFontOfSize:24];
    titleLabel.textAlignment = NSTextAlignmentCenter;
    titleLabel.frame = CGRectMake(0, 60, self.view.bounds.size.width, 40);
    [self.view addSubview:titleLabel];
}

- (void)setupVideoPlayer {
    // 创建视频播放器
    CGFloat playerWidth = self.view.bounds.size.width - 40;
    CGFloat playerHeight = playerWidth * 9.0 / 16.0; // 16:9 比例
    
    self.videoPlayer = [[VideoPlayer alloc] initWithFrame:CGRectMake(20, 120, playerWidth, playerHeight)];
    [self.view addSubview:self.videoPlayer];
    
    // 使用本地视频文件或网络视频URL
    NSURL *videoURL = nil;
    
    // 方式1: 使用本地视频文件 (需要将视频文件添加到项目中)
    NSString *localVideoPath = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"mp4"];
    if (localVideoPath) {
        videoURL = [NSURL fileURLWithPath:localVideoPath];
    } else {
        // 方式2: 使用网络视频URL
        videoURL = [NSURL URLWithString:@"https://example.com/sample.mp4"];
        
        // 方式3: 使用测试视频URL (这是一个公开的测试视频)
        //videoURL = [NSURL URLWithString:@"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"];
        
    }
    
    if (videoURL) {
        [self.videoPlayer setupPlayerWithURL:videoURL];
    } else {
        NSLog(@"无法找到视频文件");
    }
    
    // 添加说明标签
    UILabel *instructionLabel = [[UILabel alloc] init];
    instructionLabel.text = @"点击视频区域显示/隐藏控制界面";
    instructionLabel.font = [UIFont systemFontOfSize:14];
    instructionLabel.textAlignment = NSTextAlignmentCenter;
    instructionLabel.textColor = [UIColor grayColor];
    instructionLabel.frame = CGRectMake(20, CGRectGetMaxY(self.videoPlayer.frame) + 20,
                                      self.view.bounds.size.width - 40, 30);
    [self.view addSubview:instructionLabel];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    // 开始播放视频
    [self.videoPlayer play];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    // 暂停播放视频
    [self.videoPlayer pause];
}

- (BOOL)shouldAutorotate {
    return YES;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}

@end

info.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
</plist>

三、源码分析

VideoPlayer.h 头文件分析

属性声明

objectivec

@property (strong, nonatomic) AVPlayer *player;           // AVFoundation播放器核心
@property (strong, nonatomic) AVPlayerLayer *playerLayer; // 视频显示图层
@property (strong, nonatomic) UIButton *playPauseButton;  // 播放/暂停按钮
@property (strong, nonatomic) UIButton *fullscreenButton; // 全屏按钮(目前无功能)
@property (strong, nonatomic) UIView *controlsView;       // 控制界面容器
@property (strong, nonatomic) UISlider *progressSlider;   // 进度条
@property (strong, nonatomic) UILabel *currentTimeLabel;  // 当前时间显示
@property (strong, nonatomic) UILabel *totalTimeLabel;    // 总时间显示

公共方法

objectivec

- (void)setupPlayerWithURL:(NSURL *)videoURL; // 初始化播放器并设置视频URL
- (void)play;  // 开始播放
- (void)pause; // 暂停播放
- (void)togglePlayPause; // 切换播放/暂停状态

VideoPlayer.m 实现文件分析

1. 初始化函数

objectivec

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setupUI];        // 初始化用户界面
        [self setupGestures];  // 设置手势识别
    }
    return self;
}

2. 界面设置函数

objectivec

- (void)setupUI {
    // 设置黑色背景
    self.backgroundColor = [UIColor blackColor];

    // 创建播放器图层
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:nil];
    self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; // 保持视频比例
    [self.layer addSublayer:self.playerLayer];

    // 创建半透明控制界面
    self.controlsView = [[UIView alloc] init];
    self.controlsView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.7];

    // 创建播放/暂停按钮
    self.playPauseButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.playPauseButton setTitle:@"播放" forState:UIControlStateNormal];
    [self.playPauseButton addTarget:self action:@selector(togglePlayPause) forControlEvents:UIControlEventTouchUpInside];

    // 创建进度条并绑定事件
    self.progressSlider = [[UISlider alloc] init];
    [self.progressSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
    [self.progressSlider addTarget:self action:@selector(sliderTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];

    // 创建时间显示标签
    self.currentTimeLabel = [[UILabel alloc] init];
    self.currentTimeLabel.text = @"00:00";

    self.totalTimeLabel = [[UILabel alloc] init];
    self.totalTimeLabel.text = @"00:00";

    // 创建全屏按钮(目前只有UI,无功能)
    self.fullscreenButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.fullscreenButton setTitle:@"全屏" forState:UIControlStateNormal];

    // 初始化控制界面状态并设置自动隐藏
    self.controlsHidden = NO;
    [self hideControlsAfterDelay];
}

3. 手势设置函数

objectivec

- (void)setupGestures {
    // 添加点击手势用于显示/隐藏控制界面
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
                                         initWithTarget:self
                                         action:@selector(toggleControls)];
    [self addGestureRecognizer:tapGesture];
}

4. 布局函数

objectivec

- (void)layoutSubviews {
    [super layoutSubviews];

    // 设置播放器图层充满整个视图
    self.playerLayer.frame = self.bounds;

    // 设置控制界面在底部
    CGFloat controlsHeight = 60.0;
    self.controlsView.frame = CGRectMake(0, self.bounds.size.height - controlsHeight,
                                        self.bounds.size.width, controlsHeight);

    // 详细布局各个控件的位置和大小
    // 从左到右:播放按钮 -> 当前时间 -> 进度条 -> 总时间 -> 全屏按钮
}

5. 播放器初始化函数

objectivec

- (void)setupPlayerWithURL:(NSURL *)videoURL {
    // 创建播放项目
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:videoURL];

    // 创建播放器实例
    self.player = [AVPlayer playerWithPlayerItem:playerItem];
    self.playerLayer.player = self.player;

    // 添加时间观察者,每秒更新一次进度
    __weak typeof(self) weakSelf = self;
    self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
                                                                  queue:dispatch_get_main_queue()
                                                             usingBlock:^(CMTime time) {
        [weakSelf updateProgress]; // 更新进度显示
    }];

    // 监听播放结束通知
    [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(playerItemDidReachEnd:)
                                               name:AVPlayerItemDidPlayToEndTimeNotification
                                             object:playerItem];
}

6. 进度更新函数

objectivec

- (void)updateProgress {
    if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
        // 获取当前播放时间和总时长
        Float64 currentTime = CMTimeGetSeconds(self.player.currentTime);
        Float64 duration = CMTimeGetSeconds(self.player.currentItem.duration);

        if (isfinite(duration)) {
            // 更新进度条值
            self.progressSlider.value = currentTime / duration;
            // 更新时间显示
            self.currentTimeLabel.text = [self timeStringFromSeconds:currentTime];
            self.totalTimeLabel.text = [self timeStringFromSeconds:duration];
        }
    }
}

7. 时间格式化函数

objectivec

- (NSString *)timeStringFromSeconds:(Float64)seconds {
    // 将秒数转换为 "分:秒" 格式
    NSInteger minutes = (NSInteger)seconds / 60;
    NSInteger secs = (NSInteger)seconds % 60;
    return [NSString stringWithFormat:@"%02ld:%02ld", (long)minutes, (long)secs];
}

8. 播放控制函数

objectivec

- (void)play {
    [self.player play];
    [self.playPauseButton setTitle:@"暂停" forState:UIControlStateNormal]; // 更新按钮文字
}

- (void)pause {
    [self.player pause];
    [self.playPauseButton setTitle:@"播放" forState:UIControlStateNormal]; // 更新按钮文字
}

- (void)togglePlayPause {
    // 根据当前播放状态切换
    if (self.player.rate == 0.0) {
        [self play];
    } else {
        [self pause];
    }
}

9. 进度条控制函数

objectivec

- (void)sliderValueChanged:(UISlider *)slider {
    // 拖动进度条时移除时间观察者,避免冲突
    [self.player removeTimeObserver:self.timeObserver];
}

- (void)sliderTouchUpInside:(UISlider *)slider {
    if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
        // 计算要跳转的时间点
        Float64 duration = CMTimeGetSeconds(self.player.currentItem.duration);
        Float64 seekTime = duration * slider.value;

        // 执行跳转
        [self.player seekToTime:CMTimeMakeWithSeconds(seekTime, NSEC_PER_SEC)];

        // 重新添加时间观察者
        __weak typeof(self) weakSelf = self;
        self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
                                                                      queue:dispatch_get_main_queue()
                                                                 usingBlock:^(CMTime time) {
            [weakSelf updateProgress];
        }];
    }
}

10. 播放结束处理函数

objectivec

- (void)playerItemDidReachEnd:(NSNotification *)notification {
    // 播放结束时回到开头并暂停
    [self.player seekToTime:kCMTimeZero];
    [self pause];
}

11. 控制界面显示/隐藏函数

objectivec

- (void)toggleControls {
    // 切换控制界面显示状态
    self.controlsHidden = !self.controlsHidden;

    [UIView animateWithDuration:0.3 animations:^{
        self.controlsView.alpha = self.controlsHidden ? 0.0 : 1.0;
    }];

    // 如果显示控制界面,设置3秒后自动隐藏
    if (!self.controlsHidden) {
        [self hideControlsAfterDelay];
    }
}

- (void)hideControlsAfterDelay {
    // 取消之前的隐藏请求,避免重复
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideControls) object:nil];
    // 3秒后执行隐藏
    [self performSelector:@selector(hideControls) withObject:nil afterDelay:3.0];
}

- (void)hideControls {
    // 隐藏控制界面
    [UIView animateWithDuration:0.3 animations:^{
        self.controlsView.alpha = 0.0;
    }];
    self.controlsHidden = YES;
}

12. 清理函数

objectivec

- (void)dealloc {
    // 移除时间观察者
    if (self.timeObserver) {
        [self.player removeTimeObserver:self.timeObserver];
    }
    // 移除通知观察者
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

VideoViewController 分析

主要函数

objectivec

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUI];         // 设置界面标题
    [self setupVideoPlayer]; // 初始化视频播放器
}

- (void)setupVideoPlayer {
    // 创建16:9比例的视频播放器
    CGFloat playerWidth = self.view.bounds.size.width - 40;
    CGFloat playerHeight = playerWidth * 9.0 / 16.0;

    self.videoPlayer = [[VideoPlayer alloc] initWithFrame:CGRectMake(20, 120, playerWidth, playerHeight)];
    [self.view addSubview:self.videoPlayer];

    // 尝试加载视频文件(本地→网络→测试URL)
    // 设置播放器并开始播放
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值