Skip to content

nazar-41/Player41

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 

Repository files navigation

Video Player Example

This is a simple iOS project that demonstrates how to handle video player states, download HLS live streams, and play them locally. The app includes:

  • Player State Handling: Demonstrates managing player states like playing, paused, loading, and error.
  • Custom Slider: Allows seeking through the video and displays the current playback time.
  • Buffering: Shows real-time buffer progress with a customizable buffer duration.
  • HLS Stream Download: Allows downloading HLS .m3u8 streams for offline use, including both video and audio streams.

Screenshots

img-1 img-2 img-3
Playing and Buffering Process Downloading Stream Offline Playback

Installation

  1. Clone this repository:
    git clone https://github.com/nazar-41/Player41.git
  2. Open Player41.xcodeproj in Xcode.
  3. Build and run on an iOS device or simulator.

How It Works

State Handling

The app uses an Enum_PlayerState to manage the four playback states:

enum Enum_PlayerState {
    case playing
    case paused
    case loading
    case error(String)
}

The state is updated dynamically based on the AVPlayer's status:

player?.publisher(for: \.timeControlStatus)
    .sink { [weak self] status in
        switch status {
        case .playing:
            self?.playerStatus = .playing
        case .paused:
            self?.playerStatus = .paused
        case .waitingToPlayAtSpecifiedRate:
            self?.playerStatus = .loading
        @unknown default:
            break
        }
    }
    .store(in: &cancellables)

Custom Slider

The app includes a slider to scrub through the video timeline. The slider reflects the currentTime and totalDuration dynamically:

Slider(
    value: $viewModel.currentTime,
    in: 0...(viewModel.totalDuration > 0 ? viewModel.totalDuration : 1),
    onEditingChanged: { isDragging in
        if isDragging {
            viewModel.pause()
        } else {
            viewModel.seek(to: viewModel.currentTime)
            viewModel.play()
        }
    }
)

Buffer Handling

The buffer progress is calculated using the loadedTimeRanges of the player's currentItem:

player?.publisher(for: \.currentItem?.loadedTimeRanges)
    .sink { [weak self] timeRanges in
        guard let self = self else { return }
        self.updateBufferProgress(timeRanges)
    }
    .store(in: &cancellables)

private func updateBufferProgress(_ timeRanges: [NSValue]?) {
    guard let timeRanges = timeRanges,
          let firstRange = timeRanges.first?.timeRangeValue,
          totalDuration > 0 else {
        bufferProgress = 0
        return
    }

    let bufferedTime = CMTimeGetSeconds(firstRange.start) + CMTimeGetSeconds(firstRange.duration)
    bufferProgress = bufferedTime / totalDuration
}

Download Feature

The app supports downloading HLS streams using the following logic:

  • Start downloading a stream:

    func downloadAsset(_ asset: Asset) {
        AssetPersistenceManager.sharedManager.downloadStream(asset.stream)
    }
  • Cancel a download:

    func cancelDownload(_ asset: Asset) {
        AssetPersistenceManager.sharedManager.cancelDownload(for: asset.stream)
    }
  • Delete a downloaded stream:

    func deleteAsset(_ asset: Asset) {
        AssetPersistenceManager.sharedManager.deleteAsset(asset)
    }

The UI provides a seamless way to download, cancel, and delete streams.


Note

The download logic for HLS streams was taken from Apple's official example on how to handle media downloading in iOS apps. It demonstrates how to download, cache, and play HLS streams locally, and is implemented using AVAssetDownloadURLSession for efficient downloading.

Author

Developed by Nazar Velkakayev

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages