Design a Music Streaming App
Concepts Utilized: Audio Streaming (AVPlayer/ExoPlayer), Background Playback, Offline Downloads, DRM Protection, Lock Screen Controls, Now Playing Info Center, Gapless Playback, Audio Session Management, Download Manager
This document covers the mobile architecture for a music streaming application with audio playback, offline downloads, and background audio support.
Requirements
Functional Requirements
| Feature | Description |
|---|---|
| Browse and search | Navigate and search the music library |
| Streaming | On-demand audio streaming |
| Downloads | Offline playback of downloaded content |
| Playlists | Create and manage playlists |
| Background playback | Audio continues with lock screen controls |
| Queue management | View and modify the playback queue |
| Audio quality | Configurable streaming and download quality |
Non-Functional Requirements
| Requirement | Target |
|---|---|
| Playback start | Under 1 second |
| Gapless playback | Seamless transitions between tracks |
| Battery usage | Minimal consumption during background playback |
| Storage efficiency | Smart caching with configurable limits |
Architecture Overview
Core Components
1. Audio Player Engine
Platform Audio APIs
| Platform | API |
|---|---|
| iOS | AVFoundation (AVPlayer, AVAudioEngine) |
| Android | ExoPlayer, MediaPlayer |
| Cross-platform | Platform-specific implementations behind shared interface |
Player State Machine
2. Streaming Architecture
Adaptive Bitrate Options
| Quality | Bitrate | File Size (3 min song) |
|---|---|---|
| Low | 24 kbps | ~0.5 MB |
| Normal | 96 kbps | ~2 MB |
| High | 160 kbps | ~3.5 MB |
| Very High | 320 kbps | ~7 MB |
| Lossless | ~1000 kbps | ~20 MB |
Streaming Quality Selection
Auto quality selection: When quality is set to auto, select based on network conditions: WiFi uses very high quality, 4G uses high, 3G uses normal, and slow connections use low. Manual quality overrides this selection.
Buffer management: Monitor buffer status by comparing current playback position with buffer end. Trigger additional buffering when remaining buffer drops below a target threshold (such as 30 seconds) and more content is available.
3. Queue Management
Data structure: Maintain both original and shuffled queue arrays. Track current index and shuffle state. Select the active queue based on shuffle state.
Current track and up next: Return track at current index from the active queue. Up next returns remaining tracks after current index.
Set queue: Store tracks in original queue. If shuffled, remove current track from list, shuffle remaining, prepend current track, and reset index to 0.
Toggle shuffle: When enabling shuffle, keep current track at position 0 and shuffle remaining tracks. When disabling, find current track's position in the original queue and restore that index.
Skip: Adjust current index by the delta (positive for forward, negative for backward), clamping to valid queue bounds.
4. Offline Downloads
Download Manager
Download function:
- Check if already downloaded; return early if so
- Verify sufficient storage; notify delegate of error if insufficient
- Create download task with appropriate URL for quality
- Queue task; start immediately if under concurrent download limit
Local path lookup: Construct path in documents directory using track ID. Check file existence and return path if found, nil otherwise.
Storage management: Calculate total downloaded size by summing file sizes in the downloads folder. Remove downloads by deleting the file and updating database status.
Storage Priority
| Priority | Eviction Policy |
|---|---|
| User downloads | Never auto-delete |
| Recently played | Cache for 7 days |
| Playlists marked offline | Keep synced |
| Auto-cached songs | LRU eviction when space needed |
5. Background Playback
iOS Audio Session Configuration
Audio session setup: Configure AVAudioSession with playback category to enable background audio. Enable options for AirPlay and Bluetooth output. Activate the session to claim audio resources.
Remote command center: Register handlers for lock screen and headphone controls. Handle play, pause, next track, previous track, and seek commands. Return success or failure status from each handler.
Now playing info: Update the MPNowPlayingInfoCenter with current track metadata (title, artist, duration, elapsed time, and album artwork). This information displays on the lock screen and in Control Center.
6. Gapless Playback
Gapless playback eliminates audible gaps between consecutive tracks by pre-loading the next track before the current track ends.
Implementation approach:
- Maintain references to current player and pre-loaded next player
- When preparing next track, create an asset and player item, then preroll the player
- Observe the current player's end notification
- When current track ends, immediately swap to the pre-loaded player and start playback
- Begin preparing the subsequent track to maintain the preload chain
Data Layer
Local Database Schema
Tracks table: Stores cached track metadata including ID, title, artist and album references, duration, download status, local file path, last played timestamp, and play count.
Playlists table: Stores playlist metadata including ID, name, offline sync flag, track count, total duration, owner reference, and timestamps.
Playlist tracks table: Join table linking playlists to tracks with position ordering and added timestamp. Composite primary key prevents duplicates.
Listening history table: Records play history with track reference, timestamp, duration listened, and context (what triggered playback, such as album, playlist, or search).
Caching Strategy
Get cached audio: Check if file exists at cache path. If found, update access time for LRU tracking and return the path. Return nil if not cached.
Cache audio: Before writing, check if cache would exceed size limit. If so, evict least recently used entries until space is available. Write data to the cache path.
LRU eviction: Query database for the cached track with oldest access time. Delete that file from the cache to free space.
Performance Optimizations
1. Pre-buffering
Implementation approach: Get the next few tracks from the queue (such as 3). For each track, prefetch album art and pre-buffer the first portion of audio (such as 10 seconds) if not already cached. This ensures smooth playback transitions.
2. Album Art Loading
Two-tier caching with resizing:
- Create cache key from URL and size
- Check memory cache; return if found
- Check disk cache; promote to memory and return if found
- Download image, resize to requested dimensions
- Store in both memory and disk caches
- Return the resized image
3. List Rendering
Cell configuration: Set text labels immediately. Cancel any existing image load task. Show placeholder image. Start a new async task to load album art. When loaded, update the image view on the main thread.
Cell reuse: Cancel the image load task and reset to placeholder image. This prevents images from previous rows appearing in recycled cells.
Summary
| Decision | Options | Recommendation |
|---|---|---|
| Audio framework | AVPlayer, AVAudioEngine, ExoPlayer | Platform-native (AVPlayer/ExoPlayer) |
| Streaming format | Progressive, HLS/DASH | Progressive for music |
| Offline storage | App sandbox, shared storage | App documents directory |
| Queue state | In-memory, Persisted | Persisted to survive app termination |
| Caching | Fixed size, LRU, User-controlled | LRU with user downloads protected |