Пример #1
0
void
ConsumerNode::HandleEvent(const media_timed_event *event,
                          bigtime_t lateness,
                          bool realTimeEvent)
{
    switch (event->type)
    {
    case BTimedEventQueue::B_HANDLE_BUFFER:
    {
        out("ConsumerNode::HandleEvent B_HANDLE_BUFFER\n");
        BBuffer* buffer = const_cast<BBuffer*>((BBuffer*) event->pointer);

        out("### sheduled time = %5.4f, current time = %5.4f, lateness = %5.4f\n",buffer->Header()->start_time / 1E6,TimeSource()->Now() / 1E6,lateness / 1E6);

        snooze((rand()*100) % 200000);

        if (buffer)
            buffer->Recycle();
    }
    break;

    case BTimedEventQueue::B_PARAMETER:
    {
        out("ConsumerNode::HandleEvent B_PARAMETER\n");
    }
    break;

    case BTimedEventQueue::B_START:
    {
        out("ConsumerNode::HandleEvent B_START\n");
    }
    break;

    case BTimedEventQueue::B_STOP:
    {
        out("ConsumerNode::HandleEvent B_STOP\n");
    }
        // stopping implies not handling any more buffers.  So, we flush all pending
        // buffers out of the event queue before returning to the event loop.
    EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
    break;

    case BTimedEventQueue::B_SEEK:
    {
        out("ConsumerNode::HandleEvent B_SEEK\n");
    }
    break;

    case BTimedEventQueue::B_WARP:
    {
        out("ConsumerNode::HandleEvent B_WARP\n");
    }
        // similarly, time warps aren't meaningful to the logger, so just record it and return
        //mLogger->Log(LOG_WARP_HANDLED, logMsg);
    break;

    case BTimedEventQueue::B_DATA_STATUS:
    {
        out("ConsumerNode::HandleEvent B_DATA_STATUS\n");
    }
    break;

    default:
    {
        out("ConsumerNode::HandleEvent default\n");
    }
    break;
    }
}
Пример #2
0
void 
LoggingConsumer::HandleEvent(const media_timed_event *event, bigtime_t /* lateness */, bool /* realTimeEvent */)
{
	log_message logMsg;
	logMsg.now = TimeSource()->Now();
	mLogger->Log(LOG_HANDLE_EVENT, logMsg);

	switch (event->type)
	{
	case BTimedEventQueue::B_HANDLE_BUFFER:
		{
			BBuffer* buffer = const_cast<BBuffer*>((BBuffer*) event->pointer);
			if (buffer)
			{
				media_header* hdr = buffer->Header();
				if (hdr->destination == mInput.destination.id)
				{
					bigtime_t now = TimeSource()->Now();
					bigtime_t perf_time = hdr->start_time;

					// the how_early calculated here doesn't include scheduling latency because
					// we've already been scheduled to handle the buffer
					bigtime_t how_early = perf_time - mLatency - now;

					// logMsg.now is already set
					logMsg.buffer_data.start_time = perf_time;
					logMsg.buffer_data.offset = how_early;
					mLogger->Log(LOG_BUFFER_HANDLED, logMsg);

					// if the buffer is late, we ignore it and report the fact to the producer
					// who sent it to us
					if (how_early < 0)
					{
						mLateBuffers++;
						NotifyLateProducer(mInput.source, -how_early, perf_time);
					}
					else
					{
						// burn some percentage of our stated latency in CPU time (controlled by
						// a BParameter).  this simulates a user-configurable amount of CPU cost
						// associated with the consumer.
						bigtime_t spin_start = ::system_time();
						bigtime_t spin_now = spin_start;
						bigtime_t usecToSpin = bigtime_t(mSpinPercentage / 100.0 * mLatency);
						while (spin_now - spin_start < usecToSpin)
						{
							for (long k = 0; k < 1000000; k++) { /* intentionally blank */ }
							spin_now = ::system_time();
						}
					}

					// we're done "processing the buffer;" now we recycle it and return to the loop
					buffer->Recycle();
				}
				else
				{
					//fprintf(stderr, "* Woah!  Got a buffer for a different destination!\n");
				}
			}
		}
		break;

	// !!! change to B_PARAMETER as soon as it's available
	
	// +++++ e.moon [16jun99]
	// !!! this can't be right: the parameter value is accessed by the pointer
	//     originally passed to SetParameterValue().  there's no guarantee that
	//     value's still valid, is there?
	
	case BTimedEventQueue::B_USER_EVENT:
		{
			size_t dataSize = size_t(event->data);
			int32 param = int32(event->bigdata);
			logMsg.param.id = param;

			// handle the message if there's sufficient data provided.  we only check against
			// sizeof(float) because all of our parameters happen to be 4 bytes.  if various
			// parameters took different amounts of data, we'd check the size on a per-parameter
			// basis.
			if (dataSize >= sizeof(float)) switch (param)
			{
			case LATENCY_PARAM:
				{
					float value = *((float*) event->pointer);
					mLatency = bigtime_t(value* 1000);
					mLastLatencyChange = logMsg.now;

					// my latency just changed, so reconfigure the BMediaEventLooper
					// to give me my events at the proper time
					SetEventLatency(mLatency);

					// tell the producer that my latency changed, and broadcast a message
					// about the parameter change to any applications that may be looking
					// for it through the BMediaRoster::StartWatching() mechanism.
					//
					// if we had more than one input, we'd need to tell *all* producers about
					// the change in our latency.
					SendLatencyChange(mInput.source, mInput.destination, EventLatency() + SchedulingLatency());
					BroadcastNewParameterValue(logMsg.now, param, &value, sizeof(value));

					// log the new latency value, for recordkeeping
					logMsg.param.value = value;
					mLogger->Log(LOG_SET_PARAM_HANDLED, logMsg);
				}
				break;

			case CPU_SPIN_PARAM:
				{
					float value = *((float*) event->pointer);
					mSpinPercentage = value;
					mLastSpinChange = logMsg.now;
					BroadcastNewParameterValue(logMsg.now, param, &value, sizeof(value));
					logMsg.param.value = value;
					mLogger->Log(LOG_SET_PARAM_HANDLED, logMsg);
				}
				break;

			case PRIORITY_PARAM:
				{
					mPriority = *((int32*) event->pointer);
					// DO NOT use ::set_thead_priority() to directly alter the node's control
					// thread priority.  BMediaEventLooper tracks the priority itself and recalculates
					// the node's scheduling latency whenever SetPriority() is called.  This is VERY
					// important for correct functioning of a node chain.  You should *only* alter a
					// BMediaEventLooper's priority by calling its SetPriority() method.
					SetPriority(mPriority);

					mLastPrioChange = logMsg.now;
					BroadcastNewParameterValue(logMsg.now, param, &mPriority, sizeof(mPriority));
					logMsg.param.value = (float) mPriority;
					mLogger->Log(LOG_SET_PARAM_HANDLED, logMsg);
				}
				break;

			// log the fact that we "handled" a "set parameter" event for a
			// nonexistent parameter
			default:
				mLogger->Log(LOG_INVALID_PARAM_HANDLED, logMsg);
				break;
			}
		}
		break;

	case BTimedEventQueue::B_START:
		// okay, let's go!
		mLogger->Log(LOG_START_HANDLED, logMsg);
		break;

	case BTimedEventQueue::B_STOP:
		mLogger->Log(LOG_STOP_HANDLED, logMsg);
		// stopping implies not handling any more buffers.  So, we flush all pending
		// buffers out of the event queue before returning to the event loop.
		EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
		break;

	case BTimedEventQueue::B_SEEK:
		// seeking the log doesn't make any sense, so we just log that we handled the seek
		// and return without doing anything else
		mLogger->Log(LOG_SEEK_HANDLED, logMsg);
		break;

	case BTimedEventQueue::B_WARP:
		// similarly, time warps aren't meaningful to the logger, so just record it and return
		mLogger->Log(LOG_WARP_HANDLED, logMsg);
		break;

	case BTimedEventQueue::B_DATA_STATUS:
		// we really don't care about the producer's data status, but this is where
		// we'd do something about it if we did.
		logMsg.data_status.status = event->data;
		mLogger->Log(LOG_DATA_STATUS_HANDLED, logMsg);
		break;

	default:
		// hmm, someone enqueued a message that we don't understand.  log and ignore it.
		logMsg.unknown.what = event->type;
		mLogger->Log(LOG_HANDLE_UNKNOWN, logMsg);
		break;
	}
}
Пример #3
0
int32
VideoProducer::_FrameGeneratorThread()
{
	bool forceSendingBuffer = true;
	int32 droppedFrames = 0;
	const int32 kMaxDroppedFrames = 15;
	bool running = true;
	while (running) {
		TRACE("_FrameGeneratorThread: loop: %Ld\n", fFrame);
		// lock the node manager
		status_t err = fManager->LockWithTimeout(10000);
		bool ignoreEvent = false;
		// Data to be retrieved from the node manager.
		bigtime_t performanceTime = 0;
		bigtime_t nextPerformanceTime = 0;
		bigtime_t waitUntil = 0;
		bigtime_t nextWaitUntil = 0;
		int32 playingDirection = 0;
		int64 playlistFrame = 0;
		switch (err) {
			case B_OK: {
				TRACE("_FrameGeneratorThread: node manager successfully "
					"locked\n");
				if (droppedFrames > 0)
					fManager->FrameDropped();
				// get the times for the current and the next frame
				performanceTime = fManager->TimeForFrame(fFrame);
				nextPerformanceTime = fManager->TimeForFrame(fFrame + 1);
				waitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase
					+ performanceTime, fBufferLatency);
				nextWaitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase
					+ nextPerformanceTime, fBufferLatency);
				// get playing direction and playlist frame for the current
				// frame
				bool newPlayingState;
				playlistFrame = fManager->PlaylistFrameAtFrame(fFrame,
					playingDirection, newPlayingState);
				TRACE("_FrameGeneratorThread: performance time: %Ld, "
					"playlist frame: %lld\n", performanceTime, playlistFrame);
				forceSendingBuffer |= newPlayingState;
				fManager->SetCurrentVideoTime(nextPerformanceTime);
				fManager->Unlock();
				break;
			}
			case B_TIMED_OUT:
				TRACE("_FrameGeneratorThread: Couldn't lock the node "
					"manager.\n");
				ignoreEvent = true;
				waitUntil = system_time() - 1;
				break;
			default:
				ERROR("_FrameGeneratorThread: Couldn't lock the node manager. "
					"Terminating video producer frame generator thread.\n");
				TRACE("_FrameGeneratorThread: frame generator thread done.\n");
				// do not access any member variables, since this could
				// also mean the Node has been deleted
				return B_OK;
		}

		TRACE("_FrameGeneratorThread: waiting (%Ld)...\n", waitUntil);
		// wait until...
		err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, waitUntil);
		// The only acceptable responses are B_OK and B_TIMED_OUT. Everything
		// else means the thread should quit. Deleting the semaphore, as in
		// VideoProducer::_HandleStop(), will trigger this behavior.
		switch (err) {
			case B_OK:
				TRACE("_FrameGeneratorThread: going back to sleep.\n");
				break;
			case B_TIMED_OUT:
				TRACE("_FrameGeneratorThread: timed out => event\n");
				// Catch the cases in which the node manager could not be
				// locked and we therefore have no valid data to work with,
				// or the producer is not running or enabled.
				if (ignoreEvent || !fRunning || !fEnabled) {
					TRACE("_FrameGeneratorThread: ignore event\n");
					// nothing to do
				} else if (!forceSendingBuffer
					&& nextWaitUntil < system_time() - fBufferLatency
					&& droppedFrames < kMaxDroppedFrames) {
					// Drop frame if it's at least a frame late.
					if (playingDirection > 0) {
						printf("VideoProducer: dropped frame (%" B_PRId64
							") (perf. time %" B_PRIdBIGTIME ")\n", fFrame,
							performanceTime);
					}
					// next frame
					droppedFrames++;
					fFrame++;
				} else if (playingDirection != 0 || forceSendingBuffer) {
					// Send buffers only, if playing, the node is running and
					// the output has been enabled
					TRACE("_FrameGeneratorThread: produce frame\n");
					BAutolock _(fLock);
					// Fetch a buffer from the buffer group
					fUsedBufferGroup->WaitForBuffers();
					BBuffer* buffer = fUsedBufferGroup->RequestBuffer(
						fConnectedFormat.display.bytes_per_row
						* fConnectedFormat.display.line_count, 0LL);
					if (buffer == NULL) {
						// Wait until a buffer becomes available again
						ERROR("_FrameGeneratorThread: no buffer!\n");
						break;
					}
					// Fill out the details about this buffer.
					media_header* h = buffer->Header();
					h->type = B_MEDIA_RAW_VIDEO;
					h->time_source = TimeSource()->ID();
					h->size_used = fConnectedFormat.display.bytes_per_row
						* fConnectedFormat.display.line_count;
					// For a buffer originating from a device, you might
					// want to calculate this based on the
					// PerformanceTimeFor the time your buffer arrived at
					// the hardware (plus any applicable adjustments).
					h->start_time = fPerformanceTimeBase + performanceTime;
					h->file_pos = 0;
					h->orig_size = 0;
					h->data_offset = 0;
					h->u.raw_video.field_gamma = 1.0;
					h->u.raw_video.field_sequence = fFrame;
					h->u.raw_video.field_number = 0;
					h->u.raw_video.pulldown_number = 0;
					h->u.raw_video.first_active_line = 1;
					h->u.raw_video.line_count
						= fConnectedFormat.display.line_count;
					// Fill in a frame
					TRACE("_FrameGeneratorThread: frame: %Ld, "
						"playlistFrame: %Ld\n", fFrame, playlistFrame);
					bool wasCached = false;
					err = fSupplier->FillBuffer(playlistFrame,
						buffer->Data(), fConnectedFormat, forceSendingBuffer,
						wasCached);
					if (err == B_TIMED_OUT) {
						// Don't send the buffer if there was insufficient
						// time for rendering, this will leave the last
						// valid frame on screen until we catch up, instead
						// of going black.
						wasCached = true;
						err = B_OK;
					}
					// clean the buffer if something went wrong
					if (err != B_OK) {
						// TODO: should use "back value" according
						// to color space!
						memset(buffer->Data(), 0, h->size_used);
						err = B_OK;
					}
					// Send the buffer on down to the consumer
					if (wasCached || (err = SendBuffer(buffer, fOutput.source,
							fOutput.destination) != B_OK)) {
						// If there is a problem sending the buffer,
						// or if we don't send the buffer because its
						// contents are the same as the last one,
						// return it to its buffer group.
						buffer->Recycle();
						// we tell the supplier to delete
						// its caches if there was a problem sending
						// the buffer
						if (err != B_OK) {
							ERROR("_FrameGeneratorThread: Error "
								"sending buffer\n");
							fSupplier->DeleteCaches();
						}
					}
					// Only if everything went fine we clear the flag
					// that forces us to send a buffer even if not
					// playing.
					if (err == B_OK)
						forceSendingBuffer = false;
					// next frame
					fFrame++;
					droppedFrames = 0;
				} else {
					TRACE("_FrameGeneratorThread: not playing\n");
					// next frame
					fFrame++;
				}
				break;
			default:
				TRACE("_FrameGeneratorThread: Couldn't acquire semaphore. "
					"Error: %s\n", strerror(err));
				running = false;
				break;
		}
	}
	TRACE("_FrameGeneratorThread: frame generator thread done.\n");
	return B_OK;
}