]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/audiounit_ios.c
demux: mp4: handle Qt v1 redefined samples tables (fix #12773)
[vlc] / modules / audio_output / audiounit_ios.c
index e0f969309a3c9ba82ee1970a1d6d3ab38a1a64b4..cd45d10e666f960cdc6e76958407d05ddb8c0f89 100644 (file)
@@ -48,7 +48,7 @@
     (unsigned int)sfm.mFramesPerPacket, (unsigned int)sfm.mBytesPerFrame, \
     (unsigned int)sfm.mChannelsPerFrame, (unsigned int)sfm.mBitsPerChannel
 
-#define kBufferLength 2048 * 8 * 8 * 4
+#define AUDIO_BUFFER_SIZE_IN_SECONDS (AOUT_MAX_ADVANCE_TIME / CLOCK_FREQ)
 
 /*****************************************************************************
  * aout_sys_t: private audio output method descriptor
  *****************************************************************************/
 struct aout_sys_t
 {
-    uint8_t                     chans_to_reorder;   /* do we need channel reordering */
-    uint8_t                     chan_table[AOUT_CHAN_MAX];
-
-    UInt32                      i_numberOfChannels;
     TPCircularBuffer            circular_buffer;    /* circular buffer to swap the audio data */
 
     /* AUHAL specific */
@@ -69,12 +65,12 @@ struct aout_sys_t
     AudioUnit                   au_unit;            /* The AudioUnit we use */
 
     int                         i_rate;             /* media sample rate */
-    mtime_t                     i_played_length;    /* how much did we play already */
-    mtime_t                     i_last_sample_time; /* last sample time played by the AudioUnit */
-    mtime_t                     i_first_time_stamp;
-    bool                        b_got_first_sample;
+    int                         i_bytes_per_sample;
+
+    bool                        b_paused;
 
     vlc_mutex_t                 lock;
+    vlc_cond_t                  cond;
 };
 
 #pragma mark -
@@ -92,6 +88,7 @@ static void     Flush                   (audio_output_t *, bool);
 static int      TimeGet                 (audio_output_t *, mtime_t *);
 static OSStatus RenderCallback    (vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
                                          UInt32 , UInt32, AudioBufferList *);
+
 vlc_module_begin ()
     set_shortname("audiounit_ios")
     set_description(N_("AudioUnit output for iOS"))
@@ -113,6 +110,8 @@ static int Open(vlc_object_t *obj)
         return VLC_ENOMEM;
 
     vlc_mutex_init(&sys->lock);
+    vlc_cond_init(&sys->cond);
+    sys->b_paused = false;
 
     aout->sys = sys;
     aout->start = Start;
@@ -127,6 +126,7 @@ static void Close(vlc_object_t *obj)
     aout_sys_t *sys = aout->sys;
 
     vlc_mutex_destroy(&sys->lock);
+    vlc_cond_destroy(&sys->cond);
 
     free(sys);
 }
@@ -138,6 +138,7 @@ static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
     p_sys = p_aout->sys;
     p_sys->au_component = NULL;
     p_sys->au_unit = NULL;
+    p_sys->i_bytes_per_sample = 0;
 
     aout_FormatPrint(p_aout, "VLC is looking for:", fmt);
 
@@ -164,7 +165,6 @@ static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
     UInt32                      i_param_size = 0;
     AudioComponentDescription   desc;
     AURenderCallbackStruct      callback;
-    p_aout->sys->chans_to_reorder = 0;
     OSStatus status;
 
     /* Lets go find our Component */
@@ -182,7 +182,7 @@ static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
 
     status = AudioComponentInstanceNew(p_sys->au_component, &p_sys->au_unit);
     if (status != noErr) {
-        msg_Warn(p_aout, "we cannot open our audio component (%li)", status);
+        msg_Warn(p_aout, "we cannot open our audio component (%i)", (int)status);
         return false;
     }
 
@@ -194,15 +194,16 @@ static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
                                   &flag,
                                   sizeof(flag));
     if (status != noErr)
-        msg_Warn(p_aout, "failed to set IO mode (%li)", status);
+        msg_Warn(p_aout, "failed to set IO mode (%i)", (int)status);
 
     /* Get the current format */
     AudioStreamBasicDescription streamDescription;
     streamDescription.mSampleRate = fmt->i_rate;
     fmt->i_format = VLC_CODEC_FL32;
+    fmt->i_physical_channels = AOUT_CHANS_STEREO;
     streamDescription.mFormatID = kAudioFormatLinearPCM;
     streamDescription.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; // FL32
-    streamDescription.mChannelsPerFrame = 2;
+    streamDescription.mChannelsPerFrame = aout_FormatNbChannels(fmt);
     streamDescription.mFramesPerPacket = 1;
     streamDescription.mBitsPerChannel = 32;
     streamDescription.mBytesPerFrame = streamDescription.mBitsPerChannel * streamDescription.mChannelsPerFrame / 8;
@@ -219,7 +220,7 @@ static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
                                   &streamDescription,
                                   i_param_size);
     if (status != noErr) {
-        msg_Err(p_aout, "failed to set stream format (%li)", status);
+        msg_Err(p_aout, "failed to set stream format (%i)", (int)status);
         return false;
     }
     msg_Dbg(p_aout, STREAM_FORMAT_MSG("we set the AU format: " , streamDescription));
@@ -232,7 +233,7 @@ static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
                                   &streamDescription,
                                   &i_param_size);
     if (status != noErr)
-        msg_Warn(p_aout, "failed to verify stream format (%li)", status);
+        msg_Warn(p_aout, "failed to verify stream format (%i)", (int)status);
     msg_Dbg(p_aout, STREAM_FORMAT_MSG("the actual set AU format is " , streamDescription));
 
     /* Do the last VLC aout setups */
@@ -247,24 +248,34 @@ static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
                             kAudioUnitScope_Input,
                             0, &callback, sizeof(callback));
     if (status != noErr) {
-        msg_Err(p_aout, "render callback setup failed (%li)", status);
+        msg_Err(p_aout, "render callback setup failed (%i)", (int)status);
         return false;
     }
 
-    /* AU initiliaze */
+    /* AU init */
     status = AudioUnitInitialize(p_sys->au_unit);
     if (status != noErr) {
-        msg_Err(p_aout, "failed to init AudioUnit (%li)", status);
+        msg_Err(p_aout, "failed to init AudioUnit (%i)", (int)status);
         return false;
     }
 
     /* setup circular buffer */
-    TPCircularBufferInit(&p_sys->circular_buffer, kBufferLength);
+    TPCircularBufferInit(&p_sys->circular_buffer, AUDIO_BUFFER_SIZE_IN_SECONDS * fmt->i_rate * fmt->i_bytes_per_frame);
+
+    /* start audio session so playback continues if mute switch is on */
+    AudioSessionInitialize (NULL,
+                            kCFRunLoopCommonModes,
+                            NULL,
+                            NULL);
+
+       /* Set audio session to mediaplayback */
+       UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
+       AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory),&sessionCategory);
+       AudioSessionSetActive(true);
 
-    p_sys->b_got_first_sample = false;
-    p_sys->i_played_length = 0;
-    p_sys->i_last_sample_time = 0;
-    p_sys->i_first_time_stamp = 0;
+    /* start the unit */
+    status = AudioOutputUnitStart(p_sys->au_unit);
+    msg_Dbg(p_aout, "audio output unit started: %i", (int)status);
 
     return true;
 }
@@ -274,19 +285,18 @@ static void Stop(audio_output_t *p_aout)
     struct aout_sys_t   *p_sys = p_aout->sys;
     OSStatus status;
 
+    AudioSessionSetActive(false);
+
     if (p_sys->au_unit) {
         status = AudioOutputUnitStop(p_sys->au_unit);
         if (status != noErr)
-            msg_Warn(p_aout, "failed to stop AudioUnit (%li)", status);
+            msg_Warn(p_aout, "failed to stop AudioUnit (%i)", (int)status);
 
         status = AudioComponentInstanceDispose(p_sys->au_unit);
         if (status != noErr)
-            msg_Warn(p_aout, "failed to dispose Audio Component instance (%li)", status);
+            msg_Warn(p_aout, "failed to dispose Audio Component instance (%i)", (int)status);
     }
-
-    p_sys->i_played_length = 0;
-    p_sys->i_last_sample_time = 0;
-    p_sys->i_first_time_stamp = 0;
+    p_sys->i_bytes_per_sample = 0;
 
     /* clean-up circular buffer */
     TPCircularBufferCleanup(&p_sys->circular_buffer);
@@ -300,27 +310,12 @@ static void Play (audio_output_t * p_aout, block_t * p_block)
     struct aout_sys_t *p_sys = p_aout->sys;
 
     if (p_block->i_nb_samples > 0) {
-        if (!p_sys->b_got_first_sample) {
-            /* Start the AU */
-            OSStatus status = AudioOutputUnitStart(p_sys->au_unit);
-            msg_Dbg(p_aout, "audio output unit started: %li", status);
-            p_sys->b_got_first_sample = true;
-        }
-
-        /* Do the channel reordering */
-        if (p_sys->chans_to_reorder) {
-           aout_ChannelReorder(p_block->p_buffer,
-                               p_block->i_buffer,
-                               p_sys->chans_to_reorder,
-                               p_sys->chan_table,
-                               VLC_CODEC_FL32);
-        }
-
-        /* keep track of the played data */
-        p_aout->sys->i_played_length += p_block->i_length;
-
         /* move data to buffer */
-        TPCircularBufferProduceBytes(&p_sys->circular_buffer, p_block->p_buffer, p_block->i_buffer);
+        if (unlikely(!TPCircularBufferProduceBytes(&p_sys->circular_buffer, p_block->p_buffer, p_block->i_buffer)))
+            msg_Warn(p_aout, "Audio buffer was dropped");
+
+        if (!p_sys->i_bytes_per_sample)
+            p_sys->i_bytes_per_sample = p_block->i_buffer / p_block->i_nb_samples;
     }
 
     block_Release(p_block);
@@ -331,42 +326,62 @@ static void Pause (audio_output_t *p_aout, bool pause, mtime_t date)
     struct aout_sys_t * p_sys = p_aout->sys;
     VLC_UNUSED(date);
 
-    if (pause)
+    vlc_mutex_lock(&p_sys->lock);
+    p_sys->b_paused = pause;
+    vlc_mutex_unlock(&p_sys->lock);
+
+    /* we need to start / stop the audio unit here because otherwise
+     * the OS won't believe us that we stopped the audio output
+     * so in case of an interruption, our unit would be permanently
+     * silenced.
+     * in case of multi-tasking, the multi-tasking view would still
+     * show a playing state despite we are paused, same for lock screen */
+    if (pause) {
         AudioOutputUnitStop(p_sys->au_unit);
-    else
+        AudioSessionSetActive(false);
+    } else {
         AudioOutputUnitStart(p_sys->au_unit);
+        UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
+        AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory),&sessionCategory);
+        AudioSessionSetActive(true);
+    }
 }
 
 static void Flush(audio_output_t *p_aout, bool wait)
 {
-    struct aout_sys_t * p_sys = p_aout->sys;
-    VLC_UNUSED(wait);
+    struct aout_sys_t *p_sys = p_aout->sys;
 
-    p_sys->b_got_first_sample = false;
+    int32_t availableBytes;
+    vlc_mutex_lock(&p_sys->lock);
+    TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
 
-    /* flush circular buffer */
-    AudioOutputUnitStop(p_aout->sys->au_unit);
-    TPCircularBufferClear(&p_aout->sys->circular_buffer);
+    if (wait) {
+        while (availableBytes > 0) {
+            vlc_cond_wait(&p_sys->cond, &p_sys->lock);
+            TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
+        }
+    } else {
+        /* flush circular buffer if data is left */
+        if (availableBytes > 0)
+            TPCircularBufferClear(&p_aout->sys->circular_buffer);
+    }
 
-    p_sys->i_played_length = 0;
-    p_sys->i_last_sample_time = 0;
-    p_sys->i_first_time_stamp = 0;
+    vlc_mutex_unlock(&p_sys->lock);
 }
 
 static int TimeGet(audio_output_t *p_aout, mtime_t *delay)
 {
     struct aout_sys_t * p_sys = p_aout->sys;
 
-    vlc_mutex_lock(&p_sys->lock);
-    mtime_t i_pos = (p_sys->i_last_sample_time - p_sys->i_first_time_stamp) * CLOCK_FREQ / p_sys->i_rate;
-    vlc_mutex_unlock(&p_sys->lock);
-
-    if (i_pos > 0) {
-        *delay = p_aout->sys->i_played_length - i_pos;
-        return 0;
-    }
-    else
+    if (!p_sys->i_bytes_per_sample)
         return -1;
+
+    int32_t availableBytes;
+    TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
+
+    *delay = (availableBytes / p_sys->i_bytes_per_sample) * CLOCK_FREQ / p_sys->i_rate;
+
+    return 0;
 }
 
 /*****************************************************************************
@@ -384,33 +399,36 @@ static OSStatus RenderCallback(vlc_object_t *p_obj,
     VLC_UNUSED(ioActionFlags);
     VLC_UNUSED(inTimeStamp);
     VLC_UNUSED(inBusNumber);
+    VLC_UNUSED(inNumberFrames);
 
     audio_output_t * p_aout = (audio_output_t *)p_obj;
     struct aout_sys_t * p_sys = p_aout->sys;
 
-    int bytesToCopy = ioData->mBuffers[0].mDataByteSize;
+    int bytesRequested = ioData->mBuffers[0].mDataByteSize;
     Float32 *targetBuffer = (Float32*)ioData->mBuffers[0].mData;
 
+    vlc_mutex_lock(&p_sys->lock);
     /* Pull audio from buffer */
-    int32_t availableBytes = 0;
+    int32_t availableBytes;
     Float32 *buffer = TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
 
     /* check if we have enough data */
-    if (!availableBytes) {
+    if (!availableBytes || p_sys->b_paused) {
         /* return an empty buffer so silence is played until we have data */
-        for (UInt32 j = 0; j < inNumberFrames; j++)
-            targetBuffer[j] = 0.;
+        memset(targetBuffer, 0, ioData->mBuffers[0].mDataByteSize);
     } else {
-        memcpy(targetBuffer, buffer, __MIN(bytesToCopy, availableBytes));
-        TPCircularBufferConsume(&p_sys->circular_buffer, __MIN(bytesToCopy, availableBytes));
-        VLC_UNUSED(inNumberFrames);
-        vlc_mutex_lock(&p_sys->lock);
-        p_sys->i_last_sample_time = inTimeStamp->mSampleTime;
-        vlc_mutex_unlock(&p_sys->lock);
-        if (p_sys->i_first_time_stamp == 0)
-            p_sys->i_first_time_stamp = inTimeStamp->mSampleTime;
+        int32_t bytesToCopy = __MIN(bytesRequested, availableBytes);
+
+        if (likely(bytesToCopy > 0)) {
+            memcpy(targetBuffer, buffer, bytesToCopy);
+            TPCircularBufferConsume(&p_sys->circular_buffer, bytesToCopy);
+            ioData->mBuffers[0].mDataByteSize = bytesToCopy;
+        }
     }
 
+    vlc_cond_signal(&p_sys->cond);
+    vlc_mutex_unlock(&p_sys->lock);
+
     return noErr;
 }