Ejemplo n.º 1
0
void
TimeComputer::AddTimeStamp(bigtime_t realTime, uint64 frames)
{
	bigtime_t estimatedPerformanceTime = fPerformanceTime
		+ bigtime_t((realTime - fRealTime) * fDrift);

	fRealTime = realTime;

	if (fResetTimeBase) {
		// use the extrapolated performance time at the given real time
		fPerformanceTime = estimatedPerformanceTime;
		fPerformanceTimeBase = estimatedPerformanceTime;
		fFrameBase = frames;
		fResetTimeBase = false;
		_AddEntry(fRealTime, fPerformanceTime);
		return;
	}

	// add entry
	bigtime_t performanceTime = fPerformanceTimeBase
		+ bigtime_t((frames - fFrameBase) * fUsecsPerFrame);
	_AddEntry(realTime, performanceTime);

	// Update performance time and drift. We don't use the given
	// performance time directly, but average it with the estimated
	// performance time.
	fPerformanceTime = (performanceTime + estimatedPerformanceTime) / 2;

	Entry& entry = fEntries[fFirstEntry];
	fDrift = double(fPerformanceTime - entry.performanceTime)
		/ double(fRealTime - entry.realTime);
}
Ejemplo n.º 2
0
status_t
AVFormatReader::GetMetaData(BMessage* _data)
{
	// The first cookie is always there!
	const AVFormatContext* context = fStreams[0]->Context();

	if (context == NULL)
		return B_NO_INIT;

	avdictionary_to_message(context->metadata, _data);

	// Add chapter info
	for (unsigned i = 0; i < context->nb_chapters; i++) {
		AVChapter* chapter = context->chapters[i];
		BMessage chapterData;
		chapterData.AddInt64("start", bigtime_t(1000000.0
			* chapter->start * chapter->time_base.num
			/ chapter->time_base.den + 0.5));
		chapterData.AddInt64("end", bigtime_t(1000000.0
			* chapter->end * chapter->time_base.num
			/ chapter->time_base.den + 0.5));

		avdictionary_to_message(chapter->metadata, &chapterData);
		_data->AddMessage("be:chapter", &chapterData);
	}

	// Add program info
	for (unsigned i = 0; i < context->nb_programs; i++) {
		BMessage programData;
		avdictionary_to_message(context->programs[i]->metadata, &programData);
		_data->AddMessage("be:program", &programData);
	}

	return B_OK;
}
Ejemplo n.º 3
0
status_t
GameSoundBuffer::GetAttributes(gs_attribute * attributes,
							   size_t attributeCount)
{
	for (size_t i = 0; i < attributeCount; i++) {
		switch (attributes[i].attribute) {
			case B_GS_GAIN:
				attributes[i].value = fGain;
				if (fGainRamp) 
					attributes[i].duration = fGainRamp->duration;
				break;
			
			case B_GS_PAN:
				attributes[i].value = fPan;
				if (fPanRamp) 
					attributes[i].duration = fPanRamp->duration;
				break;

			case B_GS_LOOPING:
				attributes[i].value = (fLooping) ? -1.0 : 0.0;
				attributes[i].duration = bigtime_t(0);
				break;		
			
			default:
				attributes[i].value = 0.0;
				attributes[i].duration = bigtime_t(0);
				break;
		}
	}
	
	return B_OK;	 	
}
Ejemplo n.º 4
0
void 
GameProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
{
	// If something earlier failed, Connect() might still be called, but with a non-zero
	// error code.  When that happens we simply unreserve the connection and do
	// nothing else.
	if (error) {
		fOutput.destination = media_destination::null;
		fOutput.format = fPreferredFormat;
		return;
	}

	// Okay, the connection has been confirmed.  Record the destination and format
	// that we agreed on, and report our connection name again.
	fOutput.destination = destination;
	fOutput.format = format;
	strlcpy(io_name, fOutput.name, B_MEDIA_NAME_LENGTH);

	// Now that we're connected, we can determine our downstream latency.
	// Do so, then make sure we get our events early enough.
	media_node_id id;
	FindLatencyFor(fOutput.destination, &fLatency, &id);

	if (!fBufferGroup)
		fBufferSize = fOutput.format.u.raw_audio.buffer_size;
			// Have to set it before latency calculating

	// Use a dry run to see how long it takes me to fill a buffer of data
		
	// The first step to setup the buffer
	bigtime_t start, produceLatency;
	int32 frames = int32(fBufferSize / fFrameSize);
	float* data = new float[frames * 2];
	
	// Second, fill the buffer
	start = ::system_time();
	for (int32 i = 0; i < frames; i++) {
		data[i*2] = 0.8 * float(i/frames);
		data[i*2+1] = 0.8 * float(i/frames);
	}
	produceLatency = ::system_time();
		
	// Third, calculate the latency
	fInternalLatency = produceLatency - start;
	SetEventLatency(fLatency + fInternalLatency);
			
	// Finaily, clean up
	delete [] data;
		
	// reset our buffer duration, etc. to avoid later calculations
	bigtime_t duration = bigtime_t(1000000) * frames / bigtime_t(fOutput.format.u.raw_audio.frame_rate);
	SetBufferDuration(duration);

	// Set up the buffer group for our connection, as long as nobody handed us a
	// buffer group (via SetBufferGroup()) prior to this.  
	if (!fBufferGroup) {
		int32 count = int32(fLatency / BufferDuration() + 2);
		fBufferGroup = new BBufferGroup(fBufferSize, count);
	}
}
Ejemplo n.º 5
0
bigtime_t
SoundPlayNode::CurrentTime()
{
	int frameRate = (int)fOutput.format.u.raw_audio.frame_rate;
	return frameRate == 0 ? 0
		: bigtime_t((1000000LL * fFramesSent) / frameRate);
}
Ejemplo n.º 6
0
bigtime_t
StreamBase::_ConvertFromStreamTimeBase(int64_t time) const
{
	if (fStream->start_time != kNoPTSValue)
		time -= fStream->start_time;

	return bigtime_t(1000000.0 * time * fStream->time_base.num
		/ fStream->time_base.den + 0.5);
}
Ejemplo n.º 7
0
// returns result in # of microseconds
bigtime_t MediaOutputInfo::ComputeBufferPeriod(const media_format & format) {
	bigtime_t bufferPeriod = 25*1000; // default 25 milliseconds
	switch (format.type) {
	case B_MEDIA_MULTISTREAM:
		// given a buffer size of 8192 bytes
		// and a bitrate of 1024 kilobits/millisecond (= 128 bytes/millisecond)
		// we need to produce a buffer every 64 milliseconds (= every 64000 microseconds)
		bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format)
								 / format.u.multistream.max_bit_rate);
		break;
	case B_MEDIA_ENCODED_VIDEO:
		bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format)
								 / format.u.encoded_video.max_bit_rate);
		break;
	case B_MEDIA_ENCODED_AUDIO:
		bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format)
								 / format.u.encoded_audio.bit_rate);
		break;
	case B_MEDIA_RAW_VIDEO:
		// Given a field rate of 50.00 fields per second, (PAL)
		// we need to generate a field/buffer
		// every 1/50 of a second = 20000 microseconds.
		bufferPeriod = bigtime_t(1000000.0
								 / format.u.raw_video.field_rate);
		break;
	case B_MEDIA_RAW_AUDIO:
		// Given a sample size of 4 bytes [B_AUDIO_INT]
		// and a channel count of 2 and a buffer_size
		// of 256 bytes and a frame_rate of 44100 Hertz (1/sec)
		// 1 frame = 1 sample/channel.
		// comes to ??
		// this is a guess:
		bufferPeriod = bigtime_t(1000000.0 * ComputeBufferSize(format)
								 / (format.u.raw_audio.format
								    & media_raw_audio_format::B_AUDIO_SIZE_MASK)
								 / format.u.raw_audio.channel_count
							     / format.u.raw_audio.frame_rate);
		break;
	default:
		break;
	}
	return bufferPeriod;
}
Ejemplo n.º 8
0
status_t find_rel_time(const BMessage& msg, const char* name, int32 i, AmTime* timeVal)
{
	double v;
	status_t res = msg.FindDouble(name, i, &v);
	if (res == B_OK) {
		*timeVal = bigtime_t(v*PPQN + .5);
		return res;
	}
	return find_time(msg, name, i, timeVal);
}
Ejemplo n.º 9
0
// Win32 sNow() cannot be inlined due to bug in CW compiler. -ec 01.01.03
bigtime_t ZTicks::sNow()
	{
	uint64 frequency;
	::QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&frequency));
	if (!frequency)
		{
		// A zero frequency indicates there's no performance counter support,
		// so we fall back to GetTickCount.
		return bigtime_t(::GetTickCount()) * 1000;
		}

	uint64 time;
	::QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&time));
	return double(time) / double(frequency) * 1e6;
	}
Ejemplo n.º 10
0
BBuffer*
GameProducer::FillNextBuffer(bigtime_t event_time)
{
	// get a buffer from our buffer group
	BBuffer* buf = fBufferGroup->RequestBuffer(fBufferSize, BufferDuration());

	// if we fail to get a buffer (for example, if the request times out), we
	// skip this buffer and go on to the next, to avoid locking up the control
	// thread.
	if (!buf)
		return NULL;

	// we need to discribe the buffer
	int64 frames = int64(fBufferSize / fFrameSize);
	memset(buf->Data(), 0, fBufferSize);

	// now fill the buffer with data, continuing where the last buffer left off
	fObject->Play(buf->Data(), frames);

	// fill in the buffer header
	media_header* hdr = buf->Header();
	hdr->type = B_MEDIA_RAW_AUDIO;
	hdr->size_used = fBufferSize;
	hdr->time_source = TimeSource()->ID();

	bigtime_t stamp;
	if (RunMode() == B_RECORDING) {
		// In B_RECORDING mode, we stamp with the capture time.  We're not
		// really a hardware capture node, but we simulate it by using the
		// (precalculated) time at which this buffer "should" have been created.
		stamp = event_time;
	} else {
		// okay, we're in one of the "live" performance run modes.  in these
		// modes, we stamp the buffer with the time at which the buffer should
		// be rendered to the output, not with the capture time. fStartTime is
		// the cached value of the first buffer's performance time; we calculate
		// this buffer's performance time as an offset from that time, based on
		// the amount of media we've created so far.
		// Recalculating every buffer like this avoids accumulation of error.
		stamp = fStartTime + bigtime_t(double(fFramesSent)
			/ double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0);
	}
	hdr->start_time = stamp;

	return buf;
}
Ejemplo n.º 11
0
status_t 
MusePackReader::GetStreamInfo(void *cookie, int64 *_frameCount, bigtime_t *_duration,
	media_format *format, const void **_infoBuffer, size_t *_infoSize)
{
	if (cookie != NULL)
		return B_BAD_VALUE;

	*_frameCount = FRAMELEN * (int64)fInfo.simple.Frames;
	*_duration = bigtime_t(1000.0 * fInfo.simple.Frames * FRAMELEN
		/ (fInfo.simple.SampleFreq / 1000.0) + 0.5);

	*format = fFormat;

	// we provide the stream info in this place
	*_infoBuffer = (void *)fDecoder;
	*_infoSize = sizeof(MPC_decoder);

	return B_OK;
}
Ejemplo n.º 12
0
_gs_ramp* 
InitRamp(float* value, float set, float frames, bigtime_t duration)
{
	float diff = (set > *value) ? set - *value : *value - set;
	bigtime_t sec = bigtime_t(duration / 1000000.0);
	float inc = diff * 200;
	
	_gs_ramp* ramp = new (std::nothrow) _gs_ramp;
	if (ramp != NULL) {
		ramp->value = value;
	
		ramp->frame_total = frames * sec;
		ramp->frame_inc = int(ramp->frame_total / inc);
	
		ramp->inc = (set - *value) / inc;
	
		ramp->frame_count = 0;
		ramp->frame_inc_count = 0;
	
		ramp->duration = duration;
	}
	return ramp;
}
Ejemplo n.º 13
0
BBuffer*
ClientNode::FillNextBuffer(bigtime_t eventTime, JackPort* port)
{
    //printf("FillNextBuffer\n");

    BBuffer* buffer = port->CurrentBuffer();

    media_header* header = buffer->Header();
    header->type = B_MEDIA_RAW_AUDIO;
    header->size_used = fFormat.u.raw_audio.buffer_size;
    header->time_source = TimeSource()->ID();

    bigtime_t start;
    if (RunMode() == B_RECORDING)
        start = eventTime;
    else
        start = fTime + bigtime_t(double(fFramesSent)
                                  / double(fFormat.u.raw_audio.frame_rate) * 1000000.0);

    header->start_time = start;

    return buffer;
}
Ejemplo n.º 14
0
Archivo: Clock.cpp Proyecto: dakyri/qua
bigtime_t
Clock::USec2Count(bigtime_t us)
{
	return bigtime_t((us/1000000.0)*performanceFrequency);
}
Ejemplo n.º 15
0
Archivo: Clock.cpp Proyecto: dakyri/qua
bigtime_t
Clock::MSec2Count(double ms)
{
	return bigtime_t((ms/1000)*performanceFrequency);
}
Ejemplo n.º 16
0
Archivo: Clock.cpp Proyecto: dakyri/qua
void
Clock::Set(float time_to)
{
	startTime = Counter() - bigtime_t(time_to*performanceFrequency);
}
Ejemplo n.º 17
0
/*! \brief Decodes next video frame.

    We decode exactly one video frame into fDecodedData. To achieve this goal,
    we might need to request several chunks of encoded data resulting in a
    variable execution time of this function.

    The length of the decoded video frame is stored in
    fDecodedDataSizeInBytes. If this variable is greater than zero, you can
    assert that there is a valid video frame available in fDecodedData.

    The decoded video frame in fDecodedData has color space conversion and
    deinterlacing already applied.

    To every decoded video frame there is a media_header populated in
    fHeader, containing the corresponding video frame properties.

	Normally every decoded video frame has a start_time field populated in the
	associated fHeader, that determines the presentation time of the frame.
	This relationship will only hold true, when each data chunk that is
	provided via GetNextChunk() contains data for exactly one encoded video
	frame (one complete frame) - not more and not less.

	We can decode data chunks that contain partial video frame data, too. In
	that case, you cannot trust the value of the start_time field in fHeader.
	We simply have no logic in place to establish a meaningful relationship
	between an incomplete frame and the start time it should be presented.
	Though this	might change in the future.

	We can decode data chunks that contain more than one video frame, too. In
	that case, you cannot trust the value of the start_time field in fHeader.
	We simply have no logic in place to track the start_time across multiple
	video frames. So a meaningful relationship between the 2nd, 3rd, ... frame
	and the start time it should be presented isn't established at the moment.
	Though this	might change in the future.

    More over the fOutputFrameRate variable is updated for every decoded video
    frame.

	On first call the member variables fSwsContext / fFormatConversionFunc	are
	initialized.

	\returns B_OK when we successfully decoded one video frame
	\returns B_LAST_BUFFER_ERROR when there are no more video frames available.
	\returns B_NO_MEMORY when we have no memory left for correct operation.
	\returns Other Errors
 */
status_t
AVCodecDecoder::_DecodeNextVideoFrame()
{
	assert(fTempPacket.size >= 0);

	while (true) {
		status_t loadingChunkStatus
			= _LoadNextVideoChunkIfNeededAndAssignStartTime();

		if (loadingChunkStatus == B_LAST_BUFFER_ERROR)
			return _FlushOneVideoFrameFromDecoderBuffer();

		if (loadingChunkStatus != B_OK) {
			TRACE("AVCodecDecoder::_DecodeNextVideoFrame(): error from "
				"GetNextChunk(): %s\n", strerror(err));
			return loadingChunkStatus;
		}

#if DO_PROFILING
		bigtime_t startTime = system_time();
#endif

		// NOTE: In the FFMPEG 0.10.2 code example decoding_encoding.c, the
		// length returned by avcodec_decode_video2() is used to update the
		// packet buffer size (here it is fTempPacket.size). This way the
		// packet buffer is allowed to contain incomplete frames so we are
		// required to buffer the packets between different calls to
		// _DecodeNextVideoFrame().
		int gotVideoFrame = 0;
		int decodedDataSizeInBytes = avcodec_decode_video2(fContext,
			fRawDecodedPicture, &gotVideoFrame, &fTempPacket);
		if (decodedDataSizeInBytes < 0) {
			TRACE("[v] AVCodecDecoder: ignoring error in decoding frame %lld:"
				" %d\n", fFrame, len);
			// NOTE: An error from avcodec_decode_video2() is ignored by the
			// FFMPEG 0.10.2 example decoding_encoding.c. Only the packet
			// buffers are flushed accordingly
			fTempPacket.data = NULL;
			fTempPacket.size = 0;
			continue;
		}

		fTempPacket.size -= decodedDataSizeInBytes;
		fTempPacket.data += decodedDataSizeInBytes;

		bool gotNoVideoFrame = gotVideoFrame == 0;
		if (gotNoVideoFrame) {
			TRACE("frame %lld - no picture yet, decodedDataSizeInBytes: %d, "
				"chunk size: %ld\n", fFrame, decodedDataSizeInBytes,
				fChunkBufferSize);
			continue;
		}

#if DO_PROFILING
		bigtime_t formatConversionStart = system_time();
#endif

		_HandleNewVideoFrameAndUpdateSystemState();

#if DO_PROFILING
		bigtime_t doneTime = system_time();
		decodingTime += formatConversionStart - startTime;
		conversionTime += doneTime - formatConversionStart;
		profileCounter++;
		if (!(fFrame % 5)) {
			if (info) {
				printf("[v] profile: d1 = %lld, d2 = %lld (%lld) required "
					"%Ld\n",
					decodingTime / profileCounter,
					conversionTime / profileCounter,
					fFrame, info->time_to_decode);
			} else {
				printf("[v] profile: d1 = %lld, d2 = %lld (%lld) required "
					"%Ld\n",
					decodingTime / profileCounter,
					conversionTime / profileCounter,
					fFrame, bigtime_t(1000000LL / fOutputFrameRate));
			}
			decodingTime = 0;
			conversionTime = 0;
			profileCounter = 0;
		}
#endif
		return B_OK;
	}
}
Ejemplo n.º 18
0
BBuffer*
AudioProducer::_FillNextBuffer(bigtime_t eventTime)
{
	BBuffer* buffer = fBufferGroup->RequestBuffer(
		fOutput.format.u.raw_audio.buffer_size, BufferDuration());

	if (!buffer) {
		ERROR("AudioProducer::_FillNextBuffer() - no buffer\n");
		return NULL;
	}

	size_t sampleSize = fOutput.format.u.raw_audio.format
		& media_raw_audio_format::B_AUDIO_SIZE_MASK;
	size_t numSamples = fOutput.format.u.raw_audio.buffer_size / sampleSize;
		// number of sample in the buffer

	// fill in the buffer header
	media_header* header = buffer->Header();
	header->type = B_MEDIA_RAW_AUDIO;
	header->time_source = TimeSource()->ID();
	buffer->SetSizeUsed(fOutput.format.u.raw_audio.buffer_size);

	bigtime_t performanceTime = bigtime_t(double(fFramesSent)
		* 1000000.0 / double(fOutput.format.u.raw_audio.frame_rate));

	// fill in data from audio supplier
	int64 frameCount = numSamples / fOutput.format.u.raw_audio.channel_count;
	bigtime_t startTime = performanceTime;
	bigtime_t endTime = bigtime_t(double(fFramesSent + frameCount)
		* 1000000.0 / fOutput.format.u.raw_audio.frame_rate);

	if (!fSupplier || fSupplier->InitCheck() != B_OK
		|| fSupplier->GetFrames(buffer->Data(), frameCount, startTime,
			endTime) != B_OK) {
		ERROR("AudioProducer::_FillNextBuffer() - supplier error -> silence\n");
		memset(buffer->Data(), 0, buffer->SizeUsed());
	}

	// stamp buffer
	if (RunMode() == B_RECORDING) {
		header->start_time = eventTime;
	} else {
		header->start_time = fStartTime + performanceTime;
	}

#if DEBUG_TO_FILE
	BMediaTrack* track;
	if (BMediaFile* file = init_media_file(fOutput.format, &track)) {
		track->WriteFrames(buffer->Data(), frameCount);
	}
#endif // DEBUG_TO_FILE

	if (fPeakListener
		&& fOutput.format.u.raw_audio.format
			== media_raw_audio_format::B_AUDIO_FLOAT) {
		// TODO: extend the peak notifier for other sample formats
		int32 channels = fOutput.format.u.raw_audio.channel_count;
		float max[channels];
		float min[channels];
		for (int32 i = 0; i < channels; i++) {
			max[i] = -1.0;
			min[i] = 1.0;
		}
		float* sample = (float*)buffer->Data();
		for (uint32 i = 0; i < frameCount; i++) {
			for (int32 k = 0; k < channels; k++) {
				if (*sample < min[k])
					min[k] = *sample;
				if (*sample > max[k])
					max[k] = *sample;
				sample++;
			}
		}
		BMessage message(MSG_PEAK_NOTIFICATION);
		for (int32 i = 0; i < channels; i++) {
			float maxAbs = max_c(fabs(min[i]), fabs(max[i]));
			message.AddFloat("max", maxAbs);
		}
		bigtime_t realTime = TimeSource()->RealTimeFor(
			fStartTime + performanceTime, 0);
		MessageEvent* event = new (std::nothrow) MessageEvent(realTime,
			fPeakListener, message);
		if (event != NULL)
			EventQueue::Default().AddEvent(event);
	}

	return buffer;
}
Ejemplo n.º 19
0
void
AudioProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
	bool realTimeEvent)
{
	TRACE_BUFFER("%p->AudioProducer::HandleEvent()\n", this);

	switch (event->type) {
		case BTimedEventQueue::B_START:
			TRACE("AudioProducer::HandleEvent(B_START)\n");
			if (RunState() != B_STARTED) {
				fFramesSent = 0;
				fStartTime = event->event_time + fSupplier->InitialLatency();
printf("B_START: start time: %lld\n", fStartTime);
				media_timed_event firstBufferEvent(
					fStartTime - fSupplier->InitialLatency(),
					BTimedEventQueue::B_HANDLE_BUFFER);
				EventQueue()->AddEvent(firstBufferEvent);
			}
			TRACE("AudioProducer::HandleEvent(B_START) done\n");
			break;

		case BTimedEventQueue::B_STOP:
			TRACE("AudioProducer::HandleEvent(B_STOP)\n");
			EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
				BTimedEventQueue::B_HANDLE_BUFFER);
			TRACE("AudioProducer::HandleEvent(B_STOP) done\n");
			break;

		case BTimedEventQueue::B_HANDLE_BUFFER:
		{
			TRACE_BUFFER("AudioProducer::HandleEvent(B_HANDLE_BUFFER)\n");
			if (RunState() == BMediaEventLooper::B_STARTED
				&& fOutput.destination != media_destination::null) {
				BBuffer* buffer = _FillNextBuffer(event->event_time);
				if (buffer) {
					status_t err = B_ERROR;
					if (fOutputEnabled) {
						err = SendBuffer(buffer, fOutput.source,
							fOutput.destination);
					}
					if (err)
						buffer->Recycle();
				}
				size_t sampleSize = fOutput.format.u.raw_audio.format
						& media_raw_audio_format::B_AUDIO_SIZE_MASK;

				size_t nFrames = fOutput.format.u.raw_audio.buffer_size
					/ (sampleSize * fOutput.format.u.raw_audio.channel_count);
				fFramesSent += nFrames;

				fNextScheduledBuffer = fStartTime
					+ bigtime_t(double(fFramesSent) * 1000000.0
						/ double(fOutput.format.u.raw_audio.frame_rate));
				media_timed_event nextBufferEvent(fNextScheduledBuffer,
					BTimedEventQueue::B_HANDLE_BUFFER);
				EventQueue()->AddEvent(nextBufferEvent);
			} else {
				ERROR("B_HANDLE_BUFFER, but not started!\n");
			}
			TRACE_BUFFER("AudioProducer::HandleEvent(B_HANDLE_BUFFER) done\n");
			break;
		}
		default:
			break;
	}
}
Ejemplo n.º 20
0
/*! \brief Decode next video frame

    We decode exactly one video frame into fDecodedData. To achieve this goal,
    we might need to request several chunks of encoded data resulting in a
    variable execution time of this function.

    The length of the decoded video frame is stored in
    fDecodedDataSizeInBytes. If this variable is greater than zero, you can
    assert that there is a valid video frame available in fDecodedData.

    The decoded video frame in fDecodedData has color space conversion and
    deinterlacing already applied.

    To every decoded video frame there is a media_header populated in
    fHeader, containing the corresponding video frame properties.
    
	Normally every decoded video frame has a start_time field populated in the
	associated fHeader, that determines the presentation time of the frame.
	This relationship will only hold true, when each data chunk that is
	provided via GetNextChunk() contains data for exactly one encoded video
	frame (one complete frame) - not more and not less.

	We can decode data chunks that contain partial video frame data, too. In
	that case, you cannot trust the value of the start_time field in fHeader.
	We simply have no logic in place to establish a meaningful relationship
	between an incomplete frame and the start time it should be presented.
	Though this	might change in the future.

	We can decode data chunks that contain more than one video frame, too. In
	that case, you cannot trust the value of the start_time field in fHeader.
	We simply have no logic in place to track the start_time across multiple
	video frames. So a meaningful relationship between the 2nd, 3rd, ... frame
	and the start time it should be presented isn't established at the moment.
	Though this	might change in the future.

    More over the fOutputFrameRate variable is updated for every decoded video
    frame.

	On first call, the member variables 	fSwsContext / fFormatConversionFunc
	are initialized.

	\return B_OK, when we successfully decoded one video frame
 */
status_t
AVCodecDecoder::_DecodeNextVideoFrame()
{
	assert(fTempPacket.size >= 0);

	while (true) {
		media_header chunkMediaHeader;

		if (fTempPacket.size == 0) {
			// Our packet buffer is empty, so fill it now.
			status_t getNextChunkStatus = GetNextChunk(&fChunkBuffer,
				&fChunkBufferSize, &chunkMediaHeader);
			switch (getNextChunkStatus) {
				case B_OK:
					break;

				case B_LAST_BUFFER_ERROR:
					return _FlushOneVideoFrameFromDecoderBuffer();

				default:
					TRACE("AVCodecDecoder::_DecodeNextVideoFrame(): error from "
						"GetNextChunk(): %s\n", strerror(err));
					return getNextChunkStatus;
			}

			fTempPacket.data = static_cast<uint8_t*>(const_cast<void*>(
				fChunkBuffer));
			fTempPacket.size = fChunkBufferSize;

			fContext->reordered_opaque = chunkMediaHeader.start_time;
				// Let ffmpeg handle the relationship between start_time and
				// decoded video frame.
				//
				// Explanation:
				// The received chunk buffer may not contain the next video
				// frame to be decoded, due to frame reordering (e.g. MPEG1/2
				// provides encoded video frames in a different order than the
				// decoded video frame).
				//
				// FIXME: Research how to establish a meaningful relationship
				// between start_time and decoded video frame when the received
				// chunk buffer contains partial video frames. Maybe some data
				// formats contain time stamps (ake pts / dts fields) that can
				// be evaluated by FFMPEG. But as long as I don't have such
				// video data to test it, it makes no sense to implement it.
				//
				// FIXME: Implement tracking start_time of video frames
				// originating in data chunks that encode more than one video
				// frame at a time. In that case on would increment the
				// start_time for each consecutive frame of such a data chunk
				// (like it is done for audio frame decoding). But as long as
				// I don't have such video data to test it, it makes no sense
				// to implement it.

#ifdef LOG_STREAM_TO_FILE
			if (sDumpedPackets < 100) {
				sStreamLogFile.Write(fChunkBuffer, fChunkBufferSize);
				printf("wrote %ld bytes\n", fChunkBufferSize);
				sDumpedPackets++;
			} else if (sDumpedPackets == 100)
				sStreamLogFile.Unset();
#endif
		}

#if DO_PROFILING
		bigtime_t startTime = system_time();
#endif

		// NOTE: In the FFMPEG 0.10.2 code example decoding_encoding.c, the
		// length returned by avcodec_decode_video2() is used to update the
		// packet buffer size (here it is fTempPacket.size). This way the
		// packet buffer is allowed to contain incomplete frames so we are
		// required to buffer the packets between different calls to
		// _DecodeNextVideoFrame().
		int gotPicture = 0;
		int decodedDataSizeInBytes = avcodec_decode_video2(fContext,
			fRawDecodedPicture, &gotPicture, &fTempPacket);
		if (decodedDataSizeInBytes < 0) {
			TRACE("[v] AVCodecDecoder: ignoring error in decoding frame %lld:"
				" %d\n", fFrame, len);
			// NOTE: An error from avcodec_decode_video2() is ignored by the
			// FFMPEG 0.10.2 example decoding_encoding.c. Only the packet
			// buffers are flushed accordingly
			fTempPacket.data = NULL;
			fTempPacket.size = 0;
			continue;
		}

		fTempPacket.size -= decodedDataSizeInBytes;
		fTempPacket.data += decodedDataSizeInBytes;

		bool gotNoPictureYet = gotPicture == 0;
		if (gotNoPictureYet) {
			TRACE("frame %lld - no picture yet, decodedDataSizeInBytes: %d, "
				"chunk size: %ld\n", fFrame, decodedDataSizeInBytes,
				fChunkBufferSize);
			continue;
		}

#if DO_PROFILING
		bigtime_t formatConversionStart = system_time();
#endif

		_HandleNewVideoFrameAndUpdateSystemState();

#if DO_PROFILING
		bigtime_t doneTime = system_time();
		decodingTime += formatConversionStart - startTime;
		conversionTime += doneTime - formatConversionStart;
		profileCounter++;
		if (!(fFrame % 5)) {
			if (info) {
				printf("[v] profile: d1 = %lld, d2 = %lld (%lld) required "
					"%Ld\n",
					decodingTime / profileCounter,
					conversionTime / profileCounter,
					fFrame, info->time_to_decode);
			} else {
				printf("[v] profile: d1 = %lld, d2 = %lld (%lld) required "
					"%Ld\n",
					decodingTime / profileCounter,
					conversionTime / profileCounter,
					fFrame, bigtime_t(1000000LL / fOutputFrameRate));
			}
			decodingTime = 0;
			conversionTime = 0;
			profileCounter = 0;
		}
#endif
		return B_OK;
	}
}
Ejemplo n.º 21
0
BBuffer*
ToneProducer::FillNextBuffer(bigtime_t event_time)
{
	// get a buffer from our buffer group
	BBuffer* buf = mBufferGroup->RequestBuffer(mOutput.format.u.raw_audio.buffer_size, BufferDuration());

	// if we fail to get a buffer (for example, if the request times out), we skip this
	// buffer and go on to the next, to avoid locking up the control thread
	if (!buf)
	{
		return NULL;
	}

	// now fill it with data, continuing where the last buffer left off
	// 20sep99: multichannel support

	size_t numFrames =
		mOutput.format.u.raw_audio.buffer_size /
		(sizeof(float)*mOutput.format.u.raw_audio.channel_count);
	bool stereo = (mOutput.format.u.raw_audio.channel_count == 2);
	if(!stereo) {
		ASSERT(mOutput.format.u.raw_audio.channel_count == 1);
	}
//	PRINT(("buffer: %ld, %ld frames, %s\n", mOutput.format.u.raw_audio.buffer_size, numFrames, stereo ? "stereo" : "mono"));

	float* data = (float*) buf->Data();

	switch (mWaveform)
	{
	case SINE_WAVE:
		FillSineBuffer(data, numFrames, stereo);
		break;

	case TRIANGLE_WAVE:
		FillTriangleBuffer(data, numFrames, stereo);
		break;

	case SAWTOOTH_WAVE:
		FillSawtoothBuffer(data, numFrames, stereo);
		break;
	}

	// fill in the buffer header
	media_header* hdr = buf->Header();
	hdr->type = B_MEDIA_RAW_AUDIO;
	hdr->size_used = mOutput.format.u.raw_audio.buffer_size;
	hdr->time_source = TimeSource()->ID();

	bigtime_t stamp;
	if (RunMode() == B_RECORDING)
	{
		// In B_RECORDING mode, we stamp with the capture time.  We're not
		// really a hardware capture node, but we simulate it by using the (precalculated)
		// time at which this buffer "should" have been created.
		stamp = event_time;
	}
	else
	{
		// okay, we're in one of the "live" performance run modes.  in these modes, we
		// stamp the buffer with the time at which the buffer should be rendered to the
		// output, not with the capture time.  mStartTime is the cached value of the
		// first buffer's performance time; we calculate this buffer's performance time as
		// an offset from that time, based on the amount of media we've created so far.
		// Recalculating every buffer like this avoids accumulation of error.
		stamp = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
	}
	hdr->start_time = stamp;

	return buf;
}
Ejemplo n.º 22
0
void
ToneProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent)
{
//	FPRINTF(stderr, "ToneProducer::HandleEvent\n");
	switch (event->type)
	{
	case BTimedEventQueue::B_START:
		// don't do anything if we're already running
		if (RunState() != B_STARTED)
		{
			// We want to start sending buffers now, so we set up the buffer-sending bookkeeping
			// and fire off the first "produce a buffer" event.
			mFramesSent = 0;
			mTheta = 0;
			mStartTime = event->event_time;
			media_timed_event firstBufferEvent(mStartTime, BTimedEventQueue::B_HANDLE_BUFFER);

			// Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through
			// the event queue, like this:
			//
			//		this->HandleEvent(&firstBufferEvent, 0, false);
			//
			EventQueue()->AddEvent(firstBufferEvent);
		}
		break;

	case BTimedEventQueue::B_STOP:
		FPRINTF(stderr, "Handling B_STOP event\n");

		// When we handle a stop, we must ensure that downstream consumers don't
		// get any more buffers from us.  This means we have to flush any pending
		// buffer-producing events from the queue.
		EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
		break;

	case _PARAMETER_EVENT:
		{
			size_t dataSize = size_t(event->data);
			int32 param = int32(event->bigdata);
			if (dataSize >= sizeof(float)) switch (param)
			{
			case FREQUENCY_PARAM:
				{
					float newValue = *((float*) event->user_data);
					if (mFrequency != newValue)		// an actual change in the value?
					{
						mFrequency = newValue;
						mFreqLastChanged = TimeSource()->Now();
						BroadcastNewParameterValue(mFreqLastChanged, param, &mFrequency, sizeof(mFrequency));
					}
				}
				break;

			case GAIN_PARAM:
				{
					float newValue = *((float*) event->user_data);
					if (mGain != newValue)
					{
						mGain = newValue;
						mGainLastChanged = TimeSource()->Now();
						BroadcastNewParameterValue(mGainLastChanged, param, &mGain, sizeof(mGain));
					}
				}
				break;

			case WAVEFORM_PARAM:
				{
					int32 newValue = *((int32*) event->user_data);
					if (mWaveform != newValue)
					{
						mWaveform = newValue;
						mTheta = 0;			// reset the generator parameters when we change waveforms
						mWaveAscending = true;
						mWaveLastChanged = TimeSource()->Now();
						BroadcastNewParameterValue(mWaveLastChanged, param, &mWaveform, sizeof(mWaveform));
					}
				}
				break;

			default:
				FPRINTF(stderr, "Hmmm... got a B_PARAMETER event for a parameter we don't have? (%" B_PRId32 ")\n", param);
				break;
			}
		}
		break;

	case BTimedEventQueue::B_HANDLE_BUFFER:
		{
			// make sure we're both started *and* connected before delivering a buffer
			if (RunState() == BMediaEventLooper::B_STARTED
				&& mOutput.destination != media_destination::null) {
				// Get the next buffer of data
				BBuffer* buffer = FillNextBuffer(event->event_time);
				if (buffer) {
					// send the buffer downstream if and only if output is enabled
					status_t err = B_ERROR;
					if (mOutputEnabled) {
						err = SendBuffer(buffer, mOutput.source,
							mOutput.destination);
					}
					if (err) {
						// we need to recycle the buffer ourselves if output is disabled or
						// if the call to SendBuffer() fails
						buffer->Recycle();
					}
				}

				// track how much media we've delivered so far
				size_t nFrames = mOutput.format.u.raw_audio.buffer_size /
					(sizeof(float) * mOutput.format.u.raw_audio.channel_count);
				mFramesSent += nFrames;

				// The buffer is on its way; now schedule the next one to go
				bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent)
					/ double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
				media_timed_event nextBufferEvent(nextEvent,
					BTimedEventQueue::B_HANDLE_BUFFER);
				EventQueue()->AddEvent(nextBufferEvent);
			}
		}
		break;

	default:
		break;
	}
}
Ejemplo n.º 23
0
void
ToneProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
{
	FPRINTF(stderr, "ToneProducer::Connect\n");

	// If something earlier failed, Connect() might still be called, but with a non-zero
	// error code.  When that happens we simply unreserve the connection and do
	// nothing else.
	if (error)
	{
		mOutput.destination = media_destination::null;
		mOutput.format = mPreferredFormat;
		return;
	}

// old workaround for format bug: Connect() receives the format data from the
// input returned from BBufferConsumer::Connected().
//
//	char formatStr[256];
//	string_for_format(format, formatStr, 255);
//	FPRINTF(stderr, "\trequested format: %s\n", formatStr);
//	if(format.type != B_MEDIA_RAW_AUDIO) {
//		// +++++ this is NOT proper behavior
//		//       but it works
//		FPRINTF(stderr, "\tcorrupted format; falling back to last suggested format\n");
//		format = mOutput.format;
//	}
//

	// Okay, the connection has been confirmed.  Record the destination and format
	// that we agreed on, and report our connection name again.
	mOutput.destination = destination;
	mOutput.format = format;
	strncpy(io_name, mOutput.name, B_MEDIA_NAME_LENGTH);

	// Now that we're connected, we can determine our downstream latency.
	// Do so, then make sure we get our events early enough.
	media_node_id id;
	FindLatencyFor(mOutput.destination, &mLatency, &id);
	FPRINTF(stderr, "\tdownstream latency = %" B_PRIdBIGTIME "\n", mLatency);

	// Use a dry run to see how long it takes me to fill a buffer of data
	bigtime_t start, produceLatency;
	size_t samplesPerBuffer = mOutput.format.u.raw_audio.buffer_size / sizeof(float);
	size_t framesPerBuffer = samplesPerBuffer / mOutput.format.u.raw_audio.channel_count;
	float* data = new float[samplesPerBuffer];
	mTheta = 0;
	start = ::system_time();
	FillSineBuffer(data, framesPerBuffer, mOutput.format.u.raw_audio.channel_count==2);
	produceLatency = ::system_time();
	mInternalLatency = produceLatency - start;

	// +++++ e.moon [13jun99]: fiddling with latency, ick
	mInternalLatency += 20000LL;

	delete [] data;
	FPRINTF(stderr, "\tbuffer-filling took %" B_PRIdBIGTIME
			" usec on this machine\n", mInternalLatency);
	SetEventLatency(mLatency + mInternalLatency);

	// reset our buffer duration, etc. to avoid later calculations
	// +++++ e.moon 11jun99: crashes w/ divide-by-zero when connecting to LoggingConsumer
	ASSERT(mOutput.format.u.raw_audio.frame_rate);

	bigtime_t duration = bigtime_t(1000000) * samplesPerBuffer / bigtime_t(mOutput.format.u.raw_audio.frame_rate);
	SetBufferDuration(duration);

	// Set up the buffer group for our connection, as long as nobody handed us a
	// buffer group (via SetBufferGroup()) prior to this.  That can happen, for example,
	// if the consumer calls SetOutputBuffersFor() on us from within its Connected()
	// method.
	if (!mBufferGroup) AllocateBuffers();
}
Ejemplo n.º 24
0
Archivo: Clock.cpp Proyecto: dakyri/qua
bigtime_t
Clock::Count2USec(bigtime_t t)
{
	return bigtime_t(1000000.0*(t/((double)performanceFrequency)));
}
Ejemplo n.º 25
0
void
GameProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
	bool realTimeEvent)
{
//	FPRINTF(stderr, "ToneProducer::HandleEvent\n");
	switch (event->type)
	{
	case BTimedEventQueue::B_START:
		// don't do anything if we're already running
		if (RunState() != B_STARTED) {
			// Going to start sending buffers so setup the needed bookkeeping
			fFramesSent = 0;
			fStartTime = event->event_time;
			media_timed_event firstBufferEvent(fStartTime,
				BTimedEventQueue::B_HANDLE_BUFFER);

			// Alternatively, we could call HandleEvent() directly with this
			// event, to avoid a trip through the event queue like this:
			//		this->HandleEvent(&firstBufferEvent, 0, false);
			EventQueue()->AddEvent(firstBufferEvent);
		}
		break;

	case BTimedEventQueue::B_STOP:
		// When we handle a stop, we must ensure that downstream consumers don't
		// get any more buffers from us.  This means we have to flush any
		// pending buffer-producing events from the queue.
		EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
			BTimedEventQueue::B_HANDLE_BUFFER);
		break;

	case BTimedEventQueue::B_HANDLE_BUFFER:
		{
			// Ensure we're both started and connected before delivering buffer
			if ((RunState() == BMediaEventLooper::B_STARTED)
				&& (fOutput.destination != media_destination::null)) {
				// Get the next buffer of data
				BBuffer* buffer = FillNextBuffer(event->event_time);
				if (buffer) {
					// Send the buffer downstream if output is enabled
					status_t err = B_ERROR;
					if (fOutputEnabled) {
						err = SendBuffer(buffer, fOutput.source,
							fOutput.destination);
					}
					if (err) {
						// we need to recycle the buffer ourselves if output is
						// disabled or if the call to SendBuffer() fails
						buffer->Recycle();
					}
				}

				// track how much media we've delivered so far
				size_t nFrames = fBufferSize / fFrameSize;
				fFramesSent += nFrames;

				// The buffer is on its way; now schedule the next one to go
				bigtime_t nextEvent = fStartTime + bigtime_t(double(fFramesSent)
					/ double(fOutput.format.u.raw_audio.frame_rate)
					* 1000000.0);
				media_timed_event nextBufferEvent(nextEvent,
					BTimedEventQueue::B_HANDLE_BUFFER);
				EventQueue()->AddEvent(nextBufferEvent);
			}
		}
		break;

	default:
		break;
	}
}
Ejemplo n.º 26
0
void
VideoProducer::Connect(status_t error, const media_source& source,
	const media_destination& destination, const media_format& format,
	char* _name)
{
	FUNCTION("Connect() %ldx%ld\n",
		format.u.raw_video.display.line_width,
		format.u.raw_video.display.line_count);

	if (fConnected) {
		ERROR("Connect() - already connected.\n");
		return;
	}

	if (source != fOutput.source) {
		ERROR("Connect() - wrong source.\n");
		return;
	}
	if (error != B_OK) {
		ERROR("Connect() - consumer error: %s\n", strerror(error));
		return;
	}
	if (!const_cast<media_format*>(&format)->Matches(&fOutput.format)) {
		ERROR("Connect() - format mismatch.\n");
		return;
	}

	fOutput.destination = destination;
	strcpy(_name, fOutput.name);
	fConnectedFormat = format.u.raw_video;
	fBufferDuration = 20000;

	if (fConnectedFormat.field_rate != 0.0f) {
		fPerformanceTimeBase = fPerformanceTimeBase
			+ (bigtime_t)((fFrame - fFrameBase)
				* 1000000LL / fConnectedFormat.field_rate);
		fFrameBase = fFrame;
		fBufferDuration = bigtime_t(1000000LL / fConnectedFormat.field_rate);
	}

	if (fConnectedFormat.display.bytes_per_row == 0) {
		ERROR("Connect() - connected format still has BPR wildcard!\n");
		fConnectedFormat.display.bytes_per_row
			= 4 * fConnectedFormat.display.line_width;
	}

	// Create the buffer group
	if (fUsedBufferGroup == NULL) {
		fBufferGroup = new BBufferGroup(fConnectedFormat.display.bytes_per_row
			* fConnectedFormat.display.line_count, BUFFER_COUNT);
		status_t err = fBufferGroup->InitCheck();
		if (err < B_OK) {
			delete fBufferGroup;
			fBufferGroup = NULL;
			ERROR("Connect() - buffer group error: %s\n", strerror(err));
			return;
		}
		fUsedBufferGroup = fBufferGroup;
	}

	// get the latency
	fBufferLatency = (BUFFER_COUNT - 1) * fBufferDuration;

	int32 bufferCount;
	if (fUsedBufferGroup->CountBuffers(&bufferCount) == B_OK) {
		// recompute the latency
		fBufferLatency = (bufferCount - 1) * fBufferDuration;
	}

	bigtime_t latency = 0;
	media_node_id tsID = 0;
	FindLatencyFor(fOutput.destination, &latency, &tsID);
	SetEventLatency(latency + fBufferLatency);

	fConnected = true;
	fEnabled = true;

	// Tell frame generation thread to recalculate delay value
	release_sem(fFrameSync);
}
Ejemplo n.º 27
0
void MediaReader::Connect(
				status_t error, 
				const media_source & source,
				const media_destination & destination,
				const media_format & format,
				char * io_name)
{
	CALLED();

	if (error != B_OK) {
		PRINT("\t<- error already\n");
		output.destination = media_destination::null;
		GetFormat(&output.format);
		return;
	}
	if (output.source != source) {
		PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
		output.destination = media_destination::null;
		GetFormat(&output.format);
		return;
	}	
	
	// record the agreed upon values
	output.destination = destination;
	output.format = format;
	strncpy(io_name,output.name,B_MEDIA_NAME_LENGTH-1);
	io_name[B_MEDIA_NAME_LENGTH-1] = '\0';

	// determine our downstream latency
	media_node_id id;
	FindLatencyFor(output.destination, &fDownstreamLatency, &id);

	// compute the buffer period (must be done before setbuffergroup)
	fBufferPeriod = bigtime_t(1000 * 8000000 / 1024
	                     * output.format.u.multistream.max_chunk_size
			             / output.format.u.multistream.max_bit_rate);

	PRINT("\tmax chunk size = %ld, max bit rate = %f, buffer period = %lld\n",
			output.format.u.multistream.max_chunk_size,
			output.format.u.multistream.max_bit_rate,fBufferPeriod);

	// setup the buffers if they aren't setup yet
	if (fBufferGroup == 0) {
		status_t status = SetBufferGroup(output.source,0);
		if (status != B_OK) {
			PRINT("\t<- SetBufferGroup failed\n");
			output.destination = media_destination::null;
			GetFormat(&output.format);
			return;
		}
	}

	SetBufferDuration(fBufferPeriod);

	if (GetCurrentFile() != 0) {
		bigtime_t start, end;
		// buffer group buffer size
		uint8 * data = new uint8[output.format.u.multistream.max_chunk_size];
		BBuffer * buffer = 0;
		ssize_t bytesRead = 0;
		{ // timed section
			start = TimeSource()->RealTime();
			// first we try to use a real BBuffer
			buffer = fBufferGroup->RequestBuffer(
					output.format.u.multistream.max_chunk_size,fBufferPeriod);
			if (buffer != 0) {
				FillFileBuffer(buffer);
			} else {
				// didn't get a real BBuffer, try simulation by just a read from the disk
				bytesRead = GetCurrentFile()->Read(
						data, output.format.u.multistream.max_chunk_size);
			}
			end = TimeSource()->RealTime();
		}
		bytesRead = buffer->SizeUsed();
		delete data;
		if (buffer != 0) {
			buffer->Recycle();
		}
		GetCurrentFile()->Seek(-bytesRead,SEEK_CUR); // put it back where we found it
	
		fInternalLatency = end - start;
		
		PRINT("\tinternal latency from disk read = %lld\n", fInternalLatency);
	} else {
		fInternalLatency = 100; // just guess
		PRINT("\tinternal latency guessed = %lld\n", fInternalLatency);
	}
	
	SetEventLatency(fDownstreamLatency + fInternalLatency);
	
	// XXX: do anything else?
}
Ejemplo n.º 28
0
void
AudioProducer::Connect(status_t error, const media_source& source,
	 const media_destination& destination, const media_format& format,
	 char* _name)
{
	TRACE("AudioProducer::Connect(%s)\n", strerror(error));

	// If something earlier failed, Connect() might still be called, but with
	// a non-zero error code.  When that happens we simply unreserve the
	// connection and do nothing else.
	if (error != B_OK) {
		fOutput.destination = media_destination::null;
		fOutput.format = fPreferredFormat;
		return;
	}

	// Okay, the connection has been confirmed.  Record the destination and
	// format that we agreed on, and report our connection name again.
	fOutput.destination = destination;
	fOutput.format = format;
	strncpy(_name, fOutput.name, B_MEDIA_NAME_LENGTH);

	// tell our audio supplier about the format
	if (fSupplier) {
		TRACE("AudioProducer::Connect() fSupplier->SetFormat()\n");
		fSupplier->SetFormat(fOutput.format);
	}

	TRACE("AudioProducer::Connect() FindLatencyFor()\n");

	// Now that we're connected, we can determine our downstream latency.
	// Do so, then make sure we get our events early enough.
	media_node_id id;
	FindLatencyFor(fOutput.destination, &fLatency, &id);

	// Use a dry run to see how long it takes me to fill a buffer of data
	size_t sampleSize = fOutput.format.u.raw_audio.format
		& media_raw_audio_format::B_AUDIO_SIZE_MASK;
	size_t samplesPerBuffer
		= fOutput.format.u.raw_audio.buffer_size / sampleSize;
	fInternalLatency = estimate_internal_latency(fOutput.format);
	if (!fLowLatency)
		fInternalLatency *= 32;
	SetEventLatency(fLatency + fInternalLatency);

	// reset our buffer duration, etc. to avoid later calculations
	bigtime_t duration = bigtime_t(1000000)
		* samplesPerBuffer / bigtime_t(fOutput.format.u.raw_audio.frame_rate
		* fOutput.format.u.raw_audio.channel_count);
	TRACE("AudioProducer::Connect() SetBufferDuration(%lld)\n", duration);
	SetBufferDuration(duration);

	TRACE("AudioProducer::Connect() _AllocateBuffers()\n");

	// Set up the buffer group for our connection, as long as nobody handed
	// us a buffer group (via SetBufferGroup()) prior to this.  That can
	// happen, for example, if the consumer calls SetOutputBuffersFor() on
	// us from within its Connected() method.
	if (fBufferGroup == NULL)
		_AllocateBuffers(fOutput.format);

	TRACE("AudioProducer::Connect() done\n");
}
Ejemplo n.º 29
0
// _FrameGenerator
int32 
VideoProducer::_FrameGenerator()
{
	bool forceSendingBuffer = true;
	bigtime_t lastFrameSentAt = 0;
	bool running = true;
	while (running) {
ldebug("VideoProducer: 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;
		bigtime_t maxRenderTime = 0;
		int32 playingDirection = 0;
		int64 playlistFrame = 0;
		switch (err) {
			case B_OK: {
ldebug("VideoProducer: node manager successfully locked\n");
				// get the times for the current and the next frame
				performanceTime = fManager->TimeForFrame(fFrame);
				nextPerformanceTime = fManager->TimeForFrame(fFrame + 1);
				maxRenderTime = min_c(bigtime_t(33334 * 0.9),
									  max_c(fSupplier->ProcessingLatency(), maxRenderTime));

				waitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase
													+ performanceTime, 0) - maxRenderTime;
				nextWaitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase
													+ nextPerformanceTime, 0) - maxRenderTime;
				// get playing direction and playlist frame for the current frame
				bool newPlayingState;
				playlistFrame = fManager->PlaylistFrameAtFrame(fFrame,
															   playingDirection,
															   newPlayingState);
ldebug("VideoProducer: performance time: %Ld, playlist frame: %Ld\n",
performanceTime, playlistFrame);
				forceSendingBuffer |= newPlayingState;
				fManager->SetCurrentVideoTime(nextPerformanceTime);
				fManager->Unlock();
				break;
			}
			case B_TIMED_OUT:
ldebug("VideoProducer: Couldn't lock the node manager.\n");
				ignoreEvent = true;
				waitUntil = system_time() - 1;
				break;
			default:
				printf("Couldn't lock the node manager. Terminating video producer "
					   "frame generator thread.\n");
				ignoreEvent = true;
				waitUntil = system_time() - 1;
				running = false;
				break;
		}
		// Force sending a frame, if the last one has been sent more than
		// one second ago.
		if (lastFrameSentAt + 1000000 < performanceTime)
			forceSendingBuffer = true;

ldebug("VideoProducer: 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:
ldebug("VideoProducer::_FrameGenerator - going back to sleep.\n");
				break;
			case B_TIMED_OUT:
ldebug("VideoProducer: 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) {
ldebug("VideoProducer: ignore event\n");
					// nothing to do
				// Drop frame if it's at least a frame late.
				} else if (nextWaitUntil < system_time()) {
//printf("VideoProducer: dropped frame (%ld)\n", fFrame);
					if (fManager->LockWithTimeout(10000) == B_OK) {
						fManager->FrameDropped();
						fManager->Unlock();
					}
					// next frame
					fFrame++;
				// Send buffers only, if playing, the node is running and the
				// output has been enabled
				} else if (playingDirection != 0 || forceSendingBuffer) {
ldebug("VideoProducer: produce frame\n");
					BAutolock _(fLock);
					// Fetch a buffer from the buffer group
					BBuffer *buffer = fUsedBufferGroup->RequestBuffer(
						fConnectedFormat.display.bytes_per_row
						* fConnectedFormat.display.line_count, 0LL);
					if (buffer) {
						// 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
						media_format mf;
						mf.type = B_MEDIA_RAW_VIDEO;
						mf.u.raw_video = fConnectedFormat;
ldebug("VideoProducer: frame: %Ld, playlistFrame: %Ld\n", fFrame, playlistFrame);
						bool forceOrWasCached = forceSendingBuffer;
	
//						if (fManager->LockWithTimeout(5000) == B_OK) {
							// we need to lock the manager, or our
							// fSupplier might work on bad data
							err = fSupplier->FillBuffer(playlistFrame,
														buffer->Data(), &mf,
														forceOrWasCached);
//							fManager->Unlock();
//						} else {
//							err = B_ERROR;
//						}
						// clean the buffer if something went wrong
						if (err != B_OK) {
							memset(buffer->Data(), 0, h->size_used);
							err = B_OK;
						}
						// Send the buffer on down to the consumer
						if (!forceOrWasCached) {
							if (SendBuffer(buffer, fOutput.source,
									fOutput.destination) != B_OK) {
								printf("_FrameGenerator: Error sending buffer\n");
								// 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
								fSupplier->DeleteCaches();
							}
						} else
							buffer->Recycle();
						// 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;
							lastFrameSentAt = performanceTime;
						}
					}
else ldebug("no buffer!\n");
					// next frame
					fFrame++;
				} else {
ldebug("VideoProducer: not playing\n");
					// next frame
					fFrame++;
				}
				break;
			default:
ldebug("Couldn't acquire semaphore. Error: %s\n", strerror(err));
				running = false;
				break;
		}
	}
ldebug("VideoProducer: frame generator thread done.\n");
	return B_OK;
}
Ejemplo n.º 30
0
// how should we handle late buffers?  drop them?
// notify the producer?
status_t
SoundPlayNode::SendNewBuffer(const media_timed_event* event,
	bigtime_t lateness, bool realTimeEvent)
{
	CALLED();
	// printf("latency = %12Ld, event = %12Ld, sched = %5Ld, arrive at %12Ld, now %12Ld, current lateness %12Ld\n", EventLatency() + SchedulingLatency(), EventLatency(), SchedulingLatency(), event->event_time, TimeSource()->Now(), lateness);

	// make sure we're both started *and* connected before delivering a buffer
	if (RunState() != BMediaEventLooper::B_STARTED
		|| fOutput.destination == media_destination::null)
		return B_OK;

	// The event->event_time is the time at which the buffer we are preparing
	// here should arrive at it's destination. The MediaEventLooper should have
	// scheduled us early enough (based on EventLatency() and the
	// SchedulingLatency()) to make this possible.
	// lateness is independent of EventLatency()!

	if (lateness > (BufferDuration() / 3) ) {
		printf("SoundPlayNode::SendNewBuffer, event scheduled much too late, "
			"lateness is %Ld\n", lateness);
	}

	// skip buffer creation if output not enabled
	if (fOutputEnabled) {

		// Get the next buffer of data
		BBuffer* buffer = FillNextBuffer(event->event_time);

		if (buffer) {

			// If we are ready way too early, decrase internal latency
/*
			bigtime_t how_early = event->event_time - TimeSource()->Now() - fLatency - fInternalLatency;
			if (how_early > 5000) {

				printf("SoundPlayNode::SendNewBuffer, event scheduled too early, how_early is %Ld\n", how_early);

				if (fTooEarlyCount++ == 5) {
					fInternalLatency -= how_early;
					if (fInternalLatency < 500)
						fInternalLatency = 500;
					printf("SoundPlayNode::SendNewBuffer setting internal latency to %Ld\n", fInternalLatency);
					SetEventLatency(fLatency + fInternalLatency);
					fTooEarlyCount = 0;
				}
			}
*/
			// send the buffer downstream if and only if output is enabled
			if (SendBuffer(buffer, fOutput.source, fOutput.destination)
					!= B_OK) {
				// we need to recycle the buffer
				// if the call to SendBuffer() fails
				printf("SoundPlayNode::SendNewBuffer: Buffer sending "
					"failed\n");
				buffer->Recycle();
			}
		}
	}

	// track how much media we've delivered so far
	size_t nFrames = fOutput.format.u.raw_audio.buffer_size
		/ ((fOutput.format.u.raw_audio.format
			& media_raw_audio_format::B_AUDIO_SIZE_MASK)
		* fOutput.format.u.raw_audio.channel_count);
	fFramesSent += nFrames;

	// The buffer is on its way; now schedule the next one to go
	// nextEvent is the time at which the buffer should arrive at it's
	// destination
	bigtime_t nextEvent = fStartTime + bigtime_t((1000000LL * fFramesSent)
		/ (int32)fOutput.format.u.raw_audio.frame_rate);
	media_timed_event nextBufferEvent(nextEvent, SEND_NEW_BUFFER_EVENT);
	EventQueue()->AddEvent(nextBufferEvent);

	return B_OK;
}