We are seeing logs were on iOS devices we see some keyframes request.
but on safari browser don’t see any request like this. could you please explain what is it.
/d8ceb9244ff889b42b82eb807327531-c27dbcb10e0bbf3cde6c-1/d8ceb9244ff88e9b42b82eb807327531-c27dbcb10e0bbf3cde6c-1/keyframes/hls/.
Streaming
RSS for tagDeep dive into the technical specifications that influence seamless playback for streaming services, including bitrates, codecs, and caching mechanisms.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I’m building a music app using Apple Music streaming via ApplicationMusicPlayer.
My goal is to decrease the volume of the current song during the last 10 seconds, and when the next track begins, restore the volume to its normal level.
I know that ApplicationMusicPlayer doesn’t expose a volume API, and I want to avoid triggering the system volume HUD.
✅ Using Apple Music streaming (not local files)
❓ Is it possible to implement per-track fade-out/fade-in logic with ApplicationMusicPlayer?
Appreciate any clarification or official guidance!
I’m building a music app using Apple Music streaming via ApplicationMusicPlayer.
My goal is to decrease the volume of the current song during the last 10 seconds, and when the next track begins, restore the volume to its normal level.
I know that ApplicationMusicPlayer doesn’t expose a volume API, and I want to avoid triggering the system volume HUD.
✅ Using Apple Music streaming (not local files)
❓ Is it possible to implement per-track fade-out/fade-in logic with ApplicationMusicPlayer?
Appreciate any clarification or official guidance!
While validating a Dolby Vision Profile 5 playlist in CMAF format (with segments in MP4), the Media Stream Validator reported the following error in the MUXT-FIX-ISSUES list:
However, the playlist correctly specifies Dolby Vision Profile 5 in both the EXT-X-STREAM-INF and EXT-X-I-STREAM-INF tags.
Playlist:
#EXTM3U
#EXT-X-VERSION:8
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-ec3",LANGUAGE="und",NAME="Undetermined",AUTOSELECT=YES,CHANNELS="6",URI="var14711339/aud1257/playlist.m3u8?device_profile=hls&seg_size=6&cmaf=1"
#EXT-X-STREAM-INF:BANDWIDTH=14680000,AVERAGE-BANDWIDTH=14676380,VIDEO-RANGE=PQ,CODECS="ec-3,dvh1.05.06",RESOLUTION=3840x2160,AUDIO="audio-ec3"
var14711339/vid/playlist.m3u8?device_profile=hls&seg_size=6&cmaf=1
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=1419881,URI="trk14711339/playlist.m3u8?device_profile=hls&cmaf=1",VIDEO-RANGE=PQ,CODECS="dvh1.05.06",RESOLUTION=3840x2160
Could you please review this and clarify:
Why is the Media Stream Validator reporting this error, even though the playlist correctly includes Dolby Vision Profile 5 parameters in CMAF format?
Why is this error not reported when using a playlist with TS segments instead of CMAF (MP4)?
I'm capturing video stream from GoPro camera (via UDP MPEG-TS packets) but unable to play in iOS app. can someone provide a source code to decode MPEG-TS data to CMSampleBuffer.
This is my native module code implementation
I'm getting base64 encoded string from server and passing this to my native module of pcm player to play audio
App.tsx
PcmPlayer.writeChunk(e.data);
PcmPlayer.swift
import AVFoundation
@objc(PcmPlayer)
class PcmPlayer: RCTEventEmitter {
private var engine: AVAudioEngine?
private var playerNode: AVAudioPlayerNode?
private var format: AVAudioFormat?
private var bufferQueue = [Data]()
private var isPlaying = false
private var hasEnded = false
private var scheduledBufferCount = 0
private let minBufferBytes = 50000
private let pcmQueue = DispatchQueue(label: "pcm.queue")
override init() {
super.init()
}
override func supportedEvents() -> [String]! {
return ["onStatus", "onMessage"]
}
@objc(initPlayer:channels:bitsPerSample:)
func initPlayer(_ sampleRate: NSNumber,
channels: NSNumber,
bitsPerSample: NSNumber) {
pcmQueue.async {
self.stopInternal()
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(.playback, mode: .default, options: [])
try session.setActive(true, options: .notifyOthersOnDeactivation)
try session.setMode(.default)
print("🔈 Audio session active. Output route:", session.currentRoute.outputs)
} catch {
print("❌ Audio session setup failed:", error)
return
}
self.engine = AVAudioEngine()
self.playerNode = AVAudioPlayerNode()
guard let engine = self.engine, let playerNode = self.playerNode else {
print("❌ Engine or playerNode is nil")
return
}
engine.attach(playerNode)
self.format = AVAudioFormat(commonFormat: .pcmFormatFloat32,
sampleRate: sampleRate.doubleValue,
channels: AVAudioChannelCount(channels.uintValue),
interleaved: false)
guard let format = self.format else {
print("❌ Failed to create AVAudioFormat")
return
}
engine.connect(playerNode, to: engine.mainMixerNode, format: format)
do {
try engine.start()
playerNode.play()
engine.mainMixerNode.outputVolume = 1.0
print("✅ AVAudioEngine started with format:", format)
} catch {
print("❌ Engine start failed:", error)
}
self.hasEnded = false
}
}
@objc(writeChunk:)
func writeChunk(_ base64Pcm: String) {
pcmQueue.async {
guard base64Pcm.count >= 10 else {
print("⚠️ Skipping short base64 string")
return
}
var padded = base64Pcm
let mod4 = base64Pcm.count % 4
if mod4 > 0 {
padded += String(repeating: "=", count: 4 - mod4)
}
guard let data = Data(base64Encoded: padded, options: .ignoreUnknownCharacters) else {
print("❌ Failed to decode base64")
return
}
self.bufferQueue.append(data)
print("📥 Received PCM chunk (\(data.count) bytes)")
print("📥 writeChunk called. isPlaying=\(self.isPlaying), bufferQueue.count=\(self.bufferQueue.count)")
if !self.isPlaying {
self.isPlaying = true
self.waitForBufferAndStartPlayback()
} else if self.scheduledBufferCount == 0 {
self.isPlaying = true
self.waitForBufferAndStartPlayback()
}
}
}
private func waitForBufferAndStartPlayback() {
DispatchQueue.global().async {
while self.queueSize() < self.minBufferBytes && !self.hasEnded {
Thread.sleep(forTimeInterval: 0.01)
}
self.writeLoop()
}
}
private func writeLoop() {
DispatchQueue.global().async {
writeLoop: while self.isPlaying {
if self.bufferQueue.isEmpty {
for _ in 0..<100 {
Thread.sleep(forTimeInterval: 0.01)
if !self.bufferQueue.isEmpty { break }
}
if self.bufferQueue.isEmpty {
print("🔇 No more data to play after waiting")
self.isPlaying = false
break writeLoop
}
}
var data: Data?
self.pcmQueue.sync {
if !self.bufferQueue.isEmpty {
data = self.bufferQueue.removeFirst()
}
}
guard let chunk = data else {
print("⚠️ No data to process")
continue
}
if let buffer = self.pcmBufferFromData(chunk) {
self.scheduledBufferCount += 1
self.playerNode?.scheduleBuffer(buffer, completionHandler: {
self.pcmQueue.async {
self.scheduledBufferCount -= 1
if self.bufferQueue.isEmpty && self.scheduledBufferCount == 0 {
print("ℹ️ Playback idle - waiting for more data")
self.isPlaying = false
}
}
})
}
}
}
}
private func pcmBufferFromData(_ data: Data) -> AVAudioPCMBuffer? {
guard let format = self.format else { return nil }
let frameCount = UInt32(data.count / 2)
guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCount) else {
print("❌ Failed to create AVAudioPCMBuffer")
return nil
}
buffer.frameLength = frameCount
guard let floatChannelData = buffer.floatChannelData?[0] else {
print("❌ floatChannelData is nil")
return nil
}
data.withUnsafeBytes { (rawBuffer: UnsafeRawBufferPointer) in
let int16Buffer = rawBuffer.bindMemory(to: Int16.self)
let count = min(int16Buffer.count, Int(frameCount))
for i in 0..<count {
floatChannelData[i] = Float32(int16Buffer[i]) / Float32(Int16.max)
}
}
return buffer
}
@objc(stopPlayer)
func stopPlayer() {
pcmQueue.async {
self.stopInternal()
}
}
private func stopInternal() {
print("🛑 stopInternal called")
self.playerNode?.stop()
self.engine?.stop()
self.engine?.reset()
self.playerNode = nil
self.engine = nil
self.format = nil
self.bufferQueue.removeAll()
self.isPlaying = false
self.hasEnded = true
self.scheduledBufferCount = 0
}
@objc(canWrite:rejecter:)
func canWrite(_ resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: RCTPromiseRejectBlock) {
pcmQueue.async {
resolve(self.bufferQueue.count < 20)
}
}
@objc(flushPlayer:rejecter:)
func flushPlayer(_ resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: RCTPromiseRejectBlock) {
pcmQueue.async {
self.bufferQueue.removeAll()
resolve(nil)
}
}
@objc
static override func requiresMainQueueSetup() -> Bool {
return false
}
private func queueSize() -> Int {
return pcmQueue.sync {
return self.bufferQueue.reduce(0) { $0 + $1.count }
}
}
}
I couldn't able to hear any audio via my real iOS device also it is working fine on emulator.
Topic:
Media Technologies
SubTopic:
Streaming
AVPictureInPictureControllerContentSource *contentSource = [[AVPictureInPictureControllerContentSource alloc] initWithSampleBufferDisplayLayer:self.renderView.sampleBufferDisplayLayer playbackDelegate:self];
AVPictureInPictureController *pictureInPictureController = [[AVPictureInPictureController alloc] initWithContentSource:contentSource];
pictureInPictureController.delegate = self;
(void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController failedToStartPictureInPictureWithError:(NSError *)error
{
//error NSError * domain: @"PGPegasusErrorDomain" - code: -1003 0x00000002819fe3a0
}
when first start the PiP play, I got the error "//error NSError * domain: @"PGPegasusErrorDomain" - code: -1003 0x00000002819fe3a0", why?
and second start is Ok.
We encounter issue with avplayer in case of EXT-X-DISCONTINUITY misalignment between audio and video produced after insertion of gaps.
The initial objective is to introduce an EXT-X-DISCONTINUITY in audio playlist after some missing segments (EXT-X-GAP) which durations are aligned to video segments durations, to handle irregular audio durations.
Please find below an example of corresponding video and audio playlists:
video:
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-MEDIA-SEQUENCE:872524632
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-TARGETDURATION:2
#USP-X-TIMESTAMP-MAP:MPEGTS=7096045027,LOCAL=2025-05-09T12:38:32.369100Z
#EXT-X-MAP:URI="hls/StreamingBasic-video=979200.m4s"
#EXT-X-PROGRAM-DATE-TIME:2025-05-09T12:38:32.369111Z
#EXTINF:2.002, no desc
hls/StreamingBasic-video=979200-872524632.m4s
#EXTINF:2.002, no desc
hls/StreamingBasic-video=979200-872524633.m4s
#EXTINF:2.002, no desc
hls/StreamingBasic-video=979200-872524634.m4s
#EXTINF:2.002, no desc
hls/StreamingBasic-video=979200-872524635.m4s
#EXTINF:2.002, no desc
hls/StreamingBasic-video=979200-872524636.m4s
## Media sequence discontinuity
#EXT-X-GAP
#EXTINF:2.002, no desc
hls/StreamingBasic-video=979200-872524637.m4s
## Media sequence discontinuity
#EXT-X-GAP
#EXTINF:2.002, no desc
hls/StreamingBasic-video=979200-872524638.m4s
#EXT-X-PROGRAM-DATE-TIME:2025-05-09T12:38:46.383111Z
#EXTINF:2.002, no desc
hls/StreamingBasic-video=979200-872524639.m4s
#EXTINF:2.002, no desc
hls/StreamingBasic-video=979200-872524640.m4s
audio:
EXTM3U
#EXT-X-VERSION:7
#EXT-X-MEDIA-SEQUENCE:872524632
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-TARGETDURATION:2
#USP-X-TIMESTAMP-MAP:MPEGTS=7096045867,LOCAL=2025-05-09T12:38:32.378400Z
#EXT-X-MAP:URI="hls/StreamingBasic-audio_99500_eng=98800.m4s"
#EXT-X-PROGRAM-DATE-TIME:2025-05-09T12:38:32.378444Z
#EXTINF:2.0053, no desc
hls/StreamingBasic-audio_99500_eng=98800-872524632.m4s
#EXTINF:2.0053, no desc
hls/StreamingBasic-audio_99500_eng=98800-872524633.m4s
#EXTINF:2.0053, no desc
hls/StreamingBasic-audio_99500_eng=98800-872524634.m4s
#EXTINF:1.984, no desc
hls/StreamingBasic-audio_99500_eng=98800-872524635.m4s
#EXTINF:2.0053, no desc
hls/StreamingBasic-audio_99500_eng=98800-872524636.m4s
## Media sequence discontinuity
#EXT-X-GAP
#EXTINF:2.002, no desc
hls/StreamingBasic-audio_99500_eng=98800-872524637.m4s
## Media sequence discontinuity
#EXT-X-GAP
#EXTINF:2.002, no desc
hls/StreamingBasic-audio_99500_eng=98800-872524638.m4s
#EXT-X-DISCONTINUITY
#EXT-X-PROGRAM-DATE-TIME:2025-05-09T12:38:46.778444Z
#EXTINF:1.6213, no desc
hls/StreamingBasic-audio_99500_eng=98800-872524639.m4s
#EXTINF:2.0053, no desc
hls/StreamingBasic-audio_99500_eng=98800-872524640.m4s
In this case playback is broken with avplayer.
Is it conformed to Http Live Streaming?
Is it an avplayer bug?
What are the guidelines to handle such gaps?
Hi everyone!
Here's what I observed so far:
On device it's reproducible on iOS/iPadOS18.5, but works on iPadOS17.7.
On iPhone16 iOS 18.5 simulator that I was extensively using for development it was reproducible until I reset content and settings.
On iPhone 16 iOS18.4 simulator, which was also used a lot during development it still works always, so I tend to think it's 18.5 issue.
Setting config.websiteDataStore = .nonPersistent() doesn't help.
Cleaning WKWebsiteDataStore doesn't help.
It works fine using direct URL from the embedded code (see the code below).
Can someone provide some insight on how this could be fixed?
Here's the code:
import SwiftUI
import WebKit
@main
struct IGVideoApp: App {
var body: some Scene {
WindowGroup {
WebView()
}
}
}
private struct WebView: UIViewRepresentable {
func makeUIView(context: Context) -> WKWebView {
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
return .init(frame: .zero, configuration: config)
}
func updateUIView(_ uiView: WKWebView, context: Context) {
let urlString = "https://www.instagram.com/reel/DKHFOGct3z7/?utm_source=ig_embed&utm_campaign=loading"
/// It works when loading from the data-instgrm-permalink URL directly
// uiView.load(.init(url: .init(string: "\(urlString)")!))
/// It doesn't work whith embedding
/// Note: the code part for embedding (<blockquote>...</blockquote>) is taken from my
/// Instagram post (https://www.instagram.com/p/DKHFOGct3z7/)
/// and stripped down. The urlString was also extracted for demonstration of direct loading.
let string = """
<!doctype html>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<html>
<head />
<body style="background-color:black; margin:0px">
<blockquote class="instagram-media"
data-instgrm-captioned
data-instgrm-version="14"
data-instgrm-permalink="\(urlString)">
</blockquote>
<script async src="https://www.instagram.com/embed.js"></script>
</body>
</html>
"""
uiView.loadHTMLString(string, baseURL: .init(string: "https://www.instagram.com"))
}
}
The documentation for the Apple Music API indicates that the genreNames field for a given artist (see https://developer.apple.com/documentation/applemusicapi/artists/attributes-data.dictionary) is an array of strings. However, it only appears as though you return ONE SINGLE GENRE per Artist, regardless of how many genres might be attached to that artist's albums.
Am I missing something? Is there an artist where multiple genres may be returned, or is this a bug in the documentation?
Hi everyone,
I noticed that Apple recently added a few new beta sample codes related to video encoding:
Encoding video for low-latency conferencing
Encoding video for live streaming
While experimenting with H.264 encoding, I came across some questions regarding certain configurations:
When I enable kVTVideoEncoderSpecification_EnableLowLatencyRateControl, is it still possible to use kVTCompressionPropertyKey_VariableBitRate? In my tests, I get an error.
It also seems that kVTVideoEncoderSpecification_EnableLowLatencyRateControl cannot be used together with kVTCompressionPropertyKey_ConstantBitRate when encoding H264. Is that expected?
When using kVTCompressionPropertyKey_ConstantBitRate with kVTCompressionPropertyKey_MaxKeyFrameInterval set to 2, the encoder outputs only keyframes, and the frame size keeps increasing, which doesn’t seem like the intended behavior.
Regarding the following code from the sample:
let byteLimit = (Double(bitrate) / 8) * 1.5 as CFNumber
let secLimit = Double(1.0) as CFNumber
let limitsArray = [ byteLimit, secLimit ] as CFArray // Each 1 second limit byte as bitrate
err = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_DataRateLimits, value: limitsArray)
This DataRateLimits setting doesn’t seem to have any effect in my tests. Whether I set it or not, the values remain unchanged.
Since the documentation on developer.apple.com/documentation
doesn’t clearly explain these cases, I’d like to ask if anyone has insights or recommendations about the proper usage of these settings.
Thanks in advance!
Hi there,
We're working on offline playback of DRM tracks. The persistent keys (also known as track licenses) for offline playback are stored locally on the device and are served from cache when a user initiates playback of a downloaded track.
Our persistent keys have a limited validity time and need to be refreshed when they expire. To prevent a situation where a persistent key expires while the user is offline, we've decided to eagerly refresh these keys one week before their expiration date. To make that happen we need to be able to obtain the expiration date of the given track license.
We've been attempting to use the makeSecureTokenForExpirationDateOfPersistableContentKey API to facilitate this process. The documentation states that this API returns a secret token representing the persistent key, which we can then exchange with our license server for the expiration date: https://developer.apple.com/documentation/avfoundation/avcontentkeysession/makesecuretokenforexpirationdate(ofpersistablecontentkey:completionhandler:)?language=objc
However, every time we call makeSecureTokenForExpirationDateOfPersistableContentKey, we receive an error with code -46250. We haven't been able to find any public references or documentation for this specific error code, which is preventing us from troubleshooting the issue. We are conducting our tests on a physical device, as the simulator does not support FairPlay playback. We don't use dual expiry approach.
Is our understanding of how to obtain the expiration timestamp correct? Are we using the makeSecureTokenForExpirationDateOfPersistableContentKey API as it was intended? What does the -46250 error code mean, and what steps should we take to fix our FairPlay implementation to make this work?
Thanks in advance for your assistance.
In iOS 26 When we download any DRM content first time it is downloading again when we edit audios and Video Quality and start downloading it is freezing complete app. Neither it is crashing not giving any error.
Hey there,
We're seeing a high rate of 403 - Invalid Authentication on this endpoint v1/me/library/artists since a few days.
Does anyone have the same issue ?
We have a Low-Latency HLS stream, and on iOS 26, even though the bandwidth is sufficient, it still selects a low-bandwidth resolution (e.g., RESOLUTION=640x360) for playback instead of using a higher-bandwidth resolution (e.g., RESOLUTION=1920x1080) when using AVPlayerViewController with AVPlayer.
This works fine on iOS version 18 and previous versions. What could be the solution to this issue?
Topic:
Media Technologies
SubTopic:
Streaming
The operation couldn’t be completed. (CoreMediaErrorDomain error -19156 - The operation couldn’t be completed. (CoreMediaErrorDomain error -19156.
For devices that are still on ios17, playing Fairplay encrypted content still works fine. For devices that I've upgraded to ios26 playing the same content in the same app no longer works. I can advance and see the stream frames by tapping +10 scrubbing so I know that the content is being decrypted but tapping the play button of AVPlayer for an AVPlayerItem now does nothing in ios26. Is this a breaking change or is there a stricter requirement that I now have to implement?
Hi, I submitted the FairPlay Streaming Credentials Approval request, but it's been 15 days and I haven't received a response yet. Do you happen to know how long they usually take to reply to these requests?
Hi,
While I don't normally use FairPlay,
I got this email that is so strangely worded I am wondering if it makes sense to people who do know it or if it has some typo or what.
You can only generate certificates for SDK 4, also SDK 4 is no longer supported?
(Also "will not expire" is imprecise phrasing, certificates presumably will expire, I think it meant to say are still valid / are not invalidated.)
Just updated my computer, phone, and dev tools to the latest versions of everything. Now when I run my app in a previously-working simulator (iPhone 16 w. iOS 18.5) I get:
Failed retrieving MusicKit tokens: fetching the developer token is not supported in the simulator when running on this version of macOS; please upgrade your Mac to macOS Ventura.
Also:
<ICCloudServiceStatusMonitor: 0x600003320e60>: Invoking 1 completion handler for MusicKit tokens. error=<ICError.DeveloperTokenFetchingFailed (-8200) "Failed to fetch media token from <AMSMediaTokenService: 0x6000029049a0>." { underlyingErrors: [ <AMSErrorDomain.300 "Token request encoding failed The token request encoder finished with an error." { userInfo: { AMSDescription : "Token request encoding failed", AMSFailureReason : "The token request encoder finished with an error." }; underlyingErrors: [ <AMSErrorDomain.5 "Anisette Failed Platform not supported" { userInfo: { AMSDescription : "Anisette Failed", AMSFailureReason : "Platform not supported" };
Anybody know what gives here? The Ventura message is absurd because I'm on Tahoe 26.1. The same code works on a physical phone running iOS 26.