Esempio n. 1
0
 NS_IMETHOD Run()
 {
     char aLocal;
     STREAM_LOG(LogLevel::Debug, ("Starting system thread"));
     profiler_register_thread("MediaStreamGraph", &aLocal);
     LIFECYCLE_LOG("Starting a new system driver for graph %p\n",
                   mDriver->mGraphImpl);
     if (mDriver->mPreviousDriver) {
         LIFECYCLE_LOG("%p releasing an AudioCallbackDriver(%p), for graph %p\n",
                       mDriver,
                       mDriver->mPreviousDriver.get(),
                       mDriver->GraphImpl());
         MOZ_ASSERT(!mDriver->AsAudioCallbackDriver());
         // Stop and release the previous driver off-main-thread, but only if we're
         // not in the situation where we've fallen back to a system clock driver
         // because the osx audio stack is currently switching output device.
         if (!mDriver->mPreviousDriver->AsAudioCallbackDriver()->IsSwitchingDevice()) {
             RefPtr<AsyncCubebTask> releaseEvent =
                 new AsyncCubebTask(mDriver->mPreviousDriver->AsAudioCallbackDriver(), AsyncCubebOperation::SHUTDOWN);
             mDriver->mPreviousDriver = nullptr;
             releaseEvent->Dispatch();
         }
     } else {
         MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
         MOZ_ASSERT(mDriver->mGraphImpl->MessagesQueued(), "Don't start a graph without messages queued.");
         mDriver->mGraphImpl->SwapMessageQueues();
     }
     mDriver->RunThread();
     return NS_OK;
 }
Esempio n. 2
0
 NS_IMETHOD Run()
 {
   char aLocal;
   STREAM_LOG(PR_LOG_DEBUG, ("Starting system thread"));
   profiler_register_thread("MediaStreamGraph", &aLocal);
   LIFECYCLE_LOG("Starting a new system driver for graph %p\n",
                 mDriver->mGraphImpl);
   if (mDriver->mPreviousDriver) {
     LIFECYCLE_LOG("%p releasing an AudioCallbackDriver(%p), for graph %p\n",
                   mDriver,
                   mDriver->mPreviousDriver.get(),
                   mDriver->GraphImpl());
     MOZ_ASSERT(!mDriver->AsAudioCallbackDriver());
     // Stop and release the previous driver off-main-thread.
     nsRefPtr<AsyncCubebTask> releaseEvent =
       new AsyncCubebTask(mDriver->mPreviousDriver->AsAudioCallbackDriver(), AsyncCubebTask::SHUTDOWN);
     mDriver->mPreviousDriver = nullptr;
     releaseEvent->Dispatch();
   } else {
     MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
     MOZ_ASSERT(mDriver->mGraphImpl->MessagesQueued(), "Don't start a graph without messages queued.");
     mDriver->mGraphImpl->SwapMessageQueues();
   }
   mDriver->RunThread();
   return NS_OK;
 }
Esempio n. 3
0
NS_IMETHODIMP
AsyncCubebTask::Run()
{
  MOZ_ASSERT(mThread);
  if (NS_IsMainThread()) {
    mThread->Shutdown(); // can't shutdown from the thread itself, darn
    // don't null out mThread!
    // See bug 999104.  we must hold a ref to the thread across Dispatch()
    // since the internal mthread ref could be released while processing
    // the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
    // assumes the caller does.
    return NS_OK;
  }

  MOZ_ASSERT(mDriver);

  switch(mOperation) {
    case AsyncCubebOperation::INIT:
      LIFECYCLE_LOG("AsyncCubebOperation::INIT\n");
      mDriver->Init();
      break;
    case AsyncCubebOperation::SHUTDOWN:
      LIFECYCLE_LOG("AsyncCubebOperation::SHUTDOWN\n");
      mDriver->Stop();
      mDriver = nullptr;
      mShutdownGrip = nullptr;
      break;
    case AsyncCubebOperation::SLEEP: {
      {
        LIFECYCLE_LOG("AsyncCubebOperation::SLEEP\n");
        MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
        // We might just have been awoken
        if (mDriver->mNeedAnotherIteration) {
          mDriver->mPauseRequested = false;
          mDriver->mWaitState = AudioCallbackDriver::WAITSTATE_RUNNING;
          break;
        }
        mDriver->Stop();
        mDriver->mWaitState = AudioCallbackDriver::WAITSTATE_WAITING_INDEFINITELY;
        mDriver->mPauseRequested = false;
        mDriver->mGraphImpl->GetMonitor().Wait(PR_INTERVAL_NO_TIMEOUT);
      }
      STREAM_LOG(PR_LOG_DEBUG, ("Restarting audio stream from sleep."));
      mDriver->StartStream();
      break;
    }
    default:
      MOZ_CRASH("Operation not implemented.");
  }

  // and now kill this thread
  NS_DispatchToMainThread(this);

  return NS_OK;
}
Esempio n. 4
0
void
ThreadedDriver::Start()
{
  LIFECYCLE_LOG("Starting thread for a SystemClockDriver  %p\n", mGraphImpl);
  nsCOMPtr<nsIRunnable> event = new MediaStreamGraphInitThreadRunnable(this);
  NS_NewNamedThread("MediaStreamGrph", getter_AddRefs(mThread), event);
}
Esempio n. 5
0
void GraphDriver::Shutdown()
{
  if (AsAudioCallbackDriver()) {
    LIFECYCLE_LOG("Releasing audio driver off main thread (GraphDriver::Shutdown).\n");
    nsRefPtr<AsyncCubebTask> releaseEvent =
      new AsyncCubebTask(AsAudioCallbackDriver(), AsyncCubebTask::SHUTDOWN);
    releaseEvent->Dispatch();
  }
}
Esempio n. 6
0
NS_IMETHODIMP
AsyncCubebTask::Run()
{
    MOZ_ASSERT(mThread);
    if (NS_IsMainThread()) {
        mThread->Shutdown(); // can't shutdown from the thread itself, darn
        // don't null out mThread!
        // See bug 999104.  we must hold a ref to the thread across Dispatch()
        // since the internal mthread ref could be released while processing
        // the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
        // assumes the caller does.
        return NS_OK;
    }

    MOZ_ASSERT(mDriver);

    switch(mOperation) {
    case AsyncCubebOperation::INIT: {
        LIFECYCLE_LOG("AsyncCubebOperation::INIT\n");
        mDriver->Init();
        mDriver->CompleteAudioContextOperations(mOperation);
        break;
    }
    case AsyncCubebOperation::SHUTDOWN: {
        LIFECYCLE_LOG("AsyncCubebOperation::SHUTDOWN\n");
        mDriver->Stop();

        mDriver->CompleteAudioContextOperations(mOperation);

        mDriver = nullptr;
        mShutdownGrip = nullptr;
        break;
    }
    default:
        MOZ_CRASH("Operation not implemented.");
    }

    // and now kill this thread
    NS_DispatchToMainThread(this);

    return NS_OK;
}
Esempio n. 7
0
void
ThreadedDriver::Start()
{
    LIFECYCLE_LOG("Starting thread for a SystemClockDriver  %p\n", mGraphImpl);
    nsCOMPtr<nsIRunnable> event = new MediaStreamGraphInitThreadRunnable(this);
    // Note: mThread may be null during event->Run() if we pass to NewNamedThread!  See AudioInitTask
    nsresult rv = NS_NewNamedThread("MediaStreamGrph", getter_AddRefs(mThread));
    if (NS_SUCCEEDED(rv)) {
        mThread->Dispatch(event, NS_DISPATCH_NORMAL);
    }
}
Esempio n. 8
0
void GraphDriver::SwitchAtNextIteration(GraphDriver* aNextDriver)
{

  LIFECYCLE_LOG("Switching to new driver: %p (%s)",
      aNextDriver, aNextDriver->AsAudioCallbackDriver() ?
      "AudioCallbackDriver" : "SystemClockDriver");
  // Sometimes we switch twice to a new driver per iteration, this is probably a
  // bug.
  MOZ_ASSERT(!mNextDriver || mNextDriver->AsAudioCallbackDriver());
  mNextDriver = aNextDriver;
}
Esempio n. 9
0
    NS_IMETHOD Run()
    {
        MOZ_ASSERT(NS_IsMainThread());

        LIFECYCLE_LOG("MediaStreamGraphShutdownThreadRunnable for graph %p",
                      mDriver->GraphImpl());
        // We can't release an audio driver on the main thread, because it can be
        // blocking.
        if (mDriver->AsAudioCallbackDriver()) {
            LIFECYCLE_LOG("Releasing audio driver off main thread.");
            RefPtr<AsyncCubebTask> releaseEvent =
                new AsyncCubebTask(mDriver->AsAudioCallbackDriver(),
                                   AsyncCubebOperation::SHUTDOWN);
            mDriver = nullptr;
            releaseEvent->Dispatch();
        } else {
            LIFECYCLE_LOG("Dropping driver reference for SystemClockDriver.");
            mDriver = nullptr;
        }
        return NS_OK;
    }
Esempio n. 10
0
void GraphDriver::SwitchAtNextIteration(GraphDriver* aNextDriver)
{
    // This is the situation where `mPreviousDriver` is an AudioCallbackDriver
    // that is switching device, and the graph has found the current driver is not
    // an AudioCallbackDriver, but tries to switch to a _new_ AudioCallbackDriver
    // because it found audio has to be output. In this case, simply ignore the
    // request to switch, since we know we will switch back to the old
    // AudioCallbackDriver when it has recovered from the device switching.
    if (aNextDriver->AsAudioCallbackDriver() &&
            mPreviousDriver &&
            mPreviousDriver->AsAudioCallbackDriver()->IsSwitchingDevice() &&
            mPreviousDriver != aNextDriver) {
        return;
    }
    LIFECYCLE_LOG("Switching to new driver: %p (%s)",
                  aNextDriver, aNextDriver->AsAudioCallbackDriver() ?
                  "AudioCallbackDriver" : "SystemClockDriver");
    mNextDriver = aNextDriver;
}
Esempio n. 11
0
long
AudioCallbackDriver::DataCallback(AudioDataValue* aBuffer, long aFrames)
{
    bool stillProcessing;

    if (mPauseRequested) {
        PodZero(aBuffer, aFrames * mGraphImpl->AudioChannelCount());
        return aFrames;
    }

#ifdef XP_MACOSX
    if (OSXDeviceSwitchingWorkaround()) {
        PodZero(aBuffer, aFrames * mGraphImpl->AudioChannelCount());
        return aFrames;
    }
#endif

#ifdef DEBUG
    // DebugOnly<> doesn't work here... it forces an initialization that will cause
    // mInCallback to be set back to false before we exit the statement.  Do it by
    // hand instead.
    AutoInCallback aic(this);
#endif

    GraphTime stateComputedTime = StateComputedTime();
    if (stateComputedTime == 0) {
        MonitorAutoLock mon(mGraphImpl->GetMonitor());
        // Because this function is called during cubeb_stream_init (to prefill the
        // audio buffers), it can be that we don't have a message here (because this
        // driver is the first one for this graph), and the graph would exit. Simply
        // return here until we have messages.
        if (!mGraphImpl->MessagesQueued()) {
            PodZero(aBuffer, aFrames * mGraphImpl->AudioChannelCount());
            return aFrames;
        }
        mGraphImpl->SwapMessageQueues();
    }

    uint32_t durationMS = aFrames * 1000 / mSampleRate;

    // For now, simply average the duration with the previous
    // duration so there is some damping against sudden changes.
    if (!mIterationDurationMS) {
        mIterationDurationMS = durationMS;
    } else {
        mIterationDurationMS = (mIterationDurationMS*3) + durationMS;
        mIterationDurationMS /= 4;
    }

    mBuffer.SetBuffer(aBuffer, aFrames);
    // fill part or all with leftover data from last iteration (since we
    // align to Audio blocks)
    mScratchBuffer.Empty(mBuffer);
    // if we totally filled the buffer (and mScratchBuffer isn't empty),
    // we don't need to run an iteration and if we do so we may overflow.
    if (mBuffer.Available()) {

        // State computed time is decided by the audio callback's buffer length. We
        // compute the iteration start and end from there, trying to keep the amount
        // of buffering in the graph constant.
        GraphTime nextStateComputedTime =
            mGraphImpl->RoundUpToNextAudioBlock(stateComputedTime + mBuffer.Available());

        mIterationStart = mIterationEnd;
        // inGraph is the number of audio frames there is between the state time and
        // the current time, i.e. the maximum theoretical length of the interval we
        // could use as [mIterationStart; mIterationEnd].
        GraphTime inGraph = stateComputedTime - mIterationStart;
        // We want the interval [mIterationStart; mIterationEnd] to be before the
        // interval [stateComputedTime; nextStateComputedTime]. We also want
        // the distance between these intervals to be roughly equivalent each time, to
        // ensure there is no clock drift between current time and state time. Since
        // we can't act on the state time because we have to fill the audio buffer, we
        // reclock the current time against the state time, here.
        mIterationEnd = mIterationStart + 0.8 * inGraph;

        STREAM_LOG(LogLevel::Debug, ("interval[%ld; %ld] state[%ld; %ld] (frames: %ld) (durationMS: %u) (duration ticks: %ld)\n",
                                     (long)mIterationStart, (long)mIterationEnd,
                                     (long)stateComputedTime, (long)nextStateComputedTime,
                                     (long)aFrames, (uint32_t)durationMS,
                                     (long)(nextStateComputedTime - stateComputedTime)));

        mCurrentTimeStamp = TimeStamp::Now();

        if (stateComputedTime < mIterationEnd) {
            STREAM_LOG(LogLevel::Warning, ("Media graph global underrun detected"));
            mIterationEnd = stateComputedTime;
        }

        stillProcessing = mGraphImpl->OneIteration(nextStateComputedTime);
    } else {
        NS_WARNING("DataCallback buffer filled entirely from scratch buffer, skipping iteration.");
        stillProcessing = true;
    }

    mBuffer.BufferFilled();

    if (mNextDriver && stillProcessing) {
        {
            // If the audio stream has not been started by the previous driver or
            // the graph itself, keep it alive.
            MonitorAutoLock mon(mGraphImpl->GetMonitor());
            if (!IsStarted()) {
                return aFrames;
            }
        }
        STREAM_LOG(LogLevel::Debug, ("Switching to system driver."));
        mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd);
        mGraphImpl->SetCurrentDriver(mNextDriver);
        mNextDriver->Start();
        // Returning less than aFrames starts the draining and eventually stops the
        // audio thread. This function will never get called again.
        return aFrames - 1;
    }

    if (!stillProcessing) {
        LIFECYCLE_LOG("Stopping audio thread for MediaStreamGraph %p", this);
        return aFrames - 1;
    }
    return aFrames;
}
Esempio n. 12
0
long
AudioCallbackDriver::DataCallback(AudioDataValue* aBuffer, long aFrames)
{
  bool stillProcessing;

  if (mPauseRequested) {
    PodZero(aBuffer, aFrames * mGraphImpl->AudioChannelCount());
    return aFrames;
  }

  DebugOnly<AutoInCallback> aic(AutoInCallback(this));

  if (mStateComputedTime == 0) {
    MonitorAutoLock mon(mGraphImpl->GetMonitor());
    // Because this function is called during cubeb_stream_init (to prefill the
    // audio buffers), it can be that we don't have a message here (because this
    // driver is the first one for this graph), and the graph would exit. Simply
    // return here until we have messages.
    if (!mGraphImpl->MessagesQueued()) {
      PodZero(aBuffer, aFrames * mGraphImpl->AudioChannelCount());
      return aFrames;
    }
    mGraphImpl->SwapMessageQueues();
  }

  uint32_t durationMS = aFrames * 1000 / mSampleRate;

  // For now, simply average the duration with the previous
  // duration so there is some damping against sudden changes.
  if (!mIterationDurationMS) {
    mIterationDurationMS = durationMS;
  } else {
    mIterationDurationMS += durationMS;
    mIterationDurationMS /= 2;
  }

  mBuffer.SetBuffer(aBuffer, aFrames);

  mScratchBuffer.Empty(mBuffer);

  mStateComputedTime = mNextStateComputedTime;

  // State computed time is decided by the audio callback's buffer length. We
  // compute the iteration start and end from there, trying to keep the amount
  // of buffering in the graph constant.
  mNextStateComputedTime =
    mGraphImpl->RoundUpToNextAudioBlock(mStateComputedTime + mBuffer.Available());

  mIterationStart = mIterationEnd;
  // inGraph is the number of audio frames there is between the state time and
  // the current time, i.e. the maximum theoretical length of the interval we
  // could use as [mIterationStart; mIterationEnd].
  GraphTime inGraph = mStateComputedTime - mIterationStart;
  // We want the interval [mIterationStart; mIterationEnd] to be before the
  // interval [mStateComputedTime; mNextStateComputedTime]. We also want
  // the distance between these intervals to be roughly equivalent each time, to
  // ensure there is no clock drift between current time and state time. Since
  // we can't act on the state time because we have to fill the audio buffer, we
  // reclock the current time against the state time, here.
  mIterationEnd = mIterationStart + 0.8 * inGraph;

  STREAM_LOG(PR_LOG_DEBUG, ("interval[%ld; %ld] state[%ld; %ld] (frames: %ld) (durationMS: %u) (duration ticks: %ld)\n",
             (long)mIterationStart, (long)mIterationEnd,
             (long)mStateComputedTime, (long)mNextStateComputedTime,
             (long)aFrames, (uint32_t)durationMS,
             (long)(mNextStateComputedTime - mStateComputedTime)));

  mCurrentTimeStamp = TimeStamp::Now();

  if (mStateComputedTime < mIterationEnd) {
    STREAM_LOG(PR_LOG_WARNING, ("Media graph global underrun detected"));
    mIterationEnd = mStateComputedTime;
  }

  stillProcessing = mGraphImpl->OneIteration(mIterationStart,
                                             mIterationEnd,
                                             mStateComputedTime,
                                             mNextStateComputedTime);

  mBuffer.BufferFilled();

  if (mNextDriver && stillProcessing) {
    {
      // If the audio stream has not been started by the previous driver or
      // the graph itself, keep it alive.
      MonitorAutoLock mon(mGraphImpl->GetMonitor());
      if (!IsStarted()) {
        return aFrames;
      }
    }
    STREAM_LOG(PR_LOG_DEBUG, ("Switching to system driver."));
    mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd,
                               mStateComputedTime, mNextStateComputedTime);
    mGraphImpl->SetCurrentDriver(mNextDriver);
    mNextDriver->Start();
    // Returning less than aFrames starts the draining and eventually stops the
    // audio thread. This function will never get called again.
    return aFrames - 1;
  }

  if (!stillProcessing) {
    LIFECYCLE_LOG("Stopping audio thread for MediaStreamGraph %p", this);
    return aFrames - 1;
  }
  return aFrames;
}