xref: /haiku/src/apps/mediaplayer/media_node_framework/PlaybackManager.cpp (revision b289aaf66bbf6e173aa90fa194fc256965f1b34d)
1 // PlaybackManager.cpp
2 
3 #include <algorithm>
4 #include <stdio.h>
5 
6 #include <Message.h>
7 #include <OS.h>
8 #include <Window.h>
9 
10 #include "EventQueue.h"
11 #include "MessageEvent.h"
12 #include "PlaybackListener.h"
13 
14 #include "PlaybackManager.h"
15 
16 using namespace std;
17 
18 //#define TRACE_NODE_MANAGER
19 #ifdef TRACE_NODE_MANAGER
20 # define TRACE(x...)	printf(x)
21 # define ERROR(x...)	fprintf(stderr, x)
22 #else
23 # define TRACE(x...)
24 # define ERROR(x...)	fprintf(stderr, x)
25 #endif
26 
27 void
28 tdebug(const char* str)
29 {
30 	TRACE("[%lx, %lld] ", find_thread(NULL), system_time());
31 	TRACE(str);
32 }
33 
34 
35 struct PlaybackManager::PlayingState {
36 	int64		start_frame;
37 	int64		end_frame;
38 	int64		frame_count;
39 	int64		first_visible_frame;
40 	int64		last_visible_frame;
41 	int64		max_frame_count;
42 	int32		play_mode;
43 	int32		loop_mode;
44 	bool		looping_enabled;
45 	bool		is_seek_request;
46 	int64		current_frame;			// Playlist frame
47 	int64		range_index;			// playing range index of current_frame
48 	int64		activation_frame;		// absolute video frame
49 
50 	PlayingState()
51 	{
52 	}
53 
54 	PlayingState(const PlayingState& other)
55 		:
56 		start_frame(other.start_frame),
57 		end_frame(other.end_frame),
58 		frame_count(other.frame_count),
59 		first_visible_frame(other.first_visible_frame),
60 		last_visible_frame(other.last_visible_frame),
61 		max_frame_count(other.max_frame_count),
62 		play_mode(other.play_mode),
63 		loop_mode(other.loop_mode),
64 		looping_enabled(other.looping_enabled),
65 		is_seek_request(false),
66 		current_frame(other.current_frame),
67 		range_index(other.range_index),
68 		activation_frame(other.activation_frame)
69 	{
70 	}
71 };
72 
73 
74 struct PlaybackManager::SpeedInfo {
75 	int64		activation_frame;		// absolute video frame
76 	bigtime_t	activation_time;		// performance time
77 	float		speed;					// speed to be used for calculations,
78 										// is 1.0 if not playing
79 	float		set_speed;				// speed set by the user
80 };
81 
82 
83 // #pragma mark - PlaybackManager
84 
85 
86 PlaybackManager::PlaybackManager()
87 	: BLooper("playback manager"),
88 	  fStates(10),
89 	  fSpeeds(10),
90 	  fCurrentAudioTime(0),
91 	  fCurrentVideoTime(0),
92 	  fPerformanceTime(0),
93 	  fFrameRate(1.0),
94 	  fStopPlayingFrame(-1),
95 	  fListeners(),
96 	  fNoAudio(false)
97 {
98 	Run();
99 }
100 
101 
102 PlaybackManager::~PlaybackManager()
103 {
104 	Cleanup();
105 }
106 
107 
108 void
109 PlaybackManager::Init(float frameRate, int32 loopingMode, bool loopingEnabled,
110 	float playbackSpeed, int32 playMode, int32 currentFrame)
111 {
112 	// cleanup first
113 	Cleanup();
114 
115 	// set the new frame rate
116 	fFrameRate = frameRate;
117 	fCurrentAudioTime = 0;
118 	fCurrentVideoTime = 0;
119 	fPerformanceTime = 0;
120 	fStopPlayingFrame = -1;
121 
122 	// set up the initial speed
123 	SpeedInfo* speed = new SpeedInfo;
124 	speed->activation_frame = 0;
125 	speed->activation_time = 0;
126 	speed->speed = playbackSpeed;
127 	speed->set_speed = playbackSpeed;
128 	_PushSpeedInfo(speed);
129 
130 	// set up the initial state
131 	PlayingState* state = new PlayingState;
132 	state->frame_count = Duration();
133 	state->start_frame = 0;
134 	state->end_frame = 0;
135 	state->first_visible_frame = 0;
136 	state->last_visible_frame = 0;
137 	state->max_frame_count = state->frame_count;
138 
139 	state->play_mode = MODE_PLAYING_PAUSED_FORWARD;
140 	state->loop_mode = loopingMode;
141 	state->looping_enabled = loopingEnabled;
142 	state->is_seek_request = false;
143 	state->current_frame = currentFrame;
144 	state->activation_frame = 0;
145 	fStates.AddItem(state);
146 
147 	TRACE("initial state pushed: state count: %ld\n", fStates.CountItems());
148 
149 	// notify the listeners
150 	NotifyPlayModeChanged(PlayMode());
151 	NotifyLoopModeChanged(LoopMode());
152 	NotifyLoopingEnabledChanged(IsLoopingEnabled());
153 	NotifyVideoBoundsChanged(VideoBounds());
154 	NotifyFPSChanged(FramesPerSecond());
155 	NotifySpeedChanged(Speed());
156 	NotifyCurrentFrameChanged(CurrentFrame());
157 	SetPlayMode(playMode);
158 }
159 
160 
161 void
162 PlaybackManager::Cleanup()
163 {
164 	// delete states
165 	for (int32 i = 0; PlayingState* state = _StateAt(i); i++)
166 		delete state;
167 	fStates.MakeEmpty();
168 
169 	// delete speed infos
170 	for (int32 i = 0; SpeedInfo* speed = _SpeedInfoAt(i); i++)
171 		delete speed;
172 	fSpeeds.MakeEmpty();
173 }
174 
175 
176 void
177 PlaybackManager::MessageReceived(BMessage* message)
178 {
179 	switch (message->what) {
180 		case MSG_EVENT:
181 			SetPerformanceTime(TimeForRealTime(system_time()));
182 //TRACE("MSG_EVENT: rt: %lld, pt: %lld\n", system_time(), fPerformanceTime);
183 			break;
184 
185 		case MSG_PLAYBACK_FORCE_UPDATE:
186 		{
187 			int64 frame;
188 			if (message->FindInt64("frame", &frame) < B_OK)
189 				frame = CurrentFrame();
190 			SetCurrentFrame(frame);
191 			break;
192 		}
193 
194 		case MSG_PLAYBACK_SET_RANGE:
195 		{
196 			int64 startFrame = _LastState()->start_frame;
197 			int64 endFrame = _LastState()->end_frame;
198 			message->FindInt64("start frame", &startFrame);
199 			message->FindInt64("end frame", &endFrame);
200 			if (startFrame != _LastState()->start_frame
201 				|| endFrame != _LastState()->end_frame) {
202 				PlayingState* state = new PlayingState(*_LastState());
203 				state->start_frame = startFrame;
204 				state->end_frame = endFrame;
205 				_PushState(state, true);
206 			}
207 			break;
208 		}
209 
210 		case MSG_PLAYBACK_SET_VISIBLE:
211 		{
212 			int64 startFrame = _LastState()->first_visible_frame;
213 			int64 endFrame = _LastState()->last_visible_frame;
214 			message->FindInt64("start frame", &startFrame);
215 			message->FindInt64("end frame", &endFrame);
216 			if (startFrame != _LastState()->first_visible_frame
217 				|| endFrame != _LastState()->last_visible_frame) {
218 				PlayingState* state = new PlayingState(*_LastState());
219 				state->first_visible_frame = startFrame;
220 				state->last_visible_frame = endFrame;
221 				_PushState(state, true);
222 			}
223 			break;
224 		}
225 
226 		case MSG_PLAYBACK_SET_LOOP_MODE:
227 		{
228 			int32 loopMode = _LastState()->loop_mode;
229 			message->FindInt32("mode", &loopMode);
230 			if (loopMode != _LastState()->loop_mode) {
231 				PlayingState* state = new PlayingState(*_LastState());
232 				state->loop_mode = loopMode;
233 				_PushState(state, true);
234 			}
235 			break;
236 		}
237 
238 		// other messages
239 		default:
240 			BLooper::MessageReceived(message);
241 			break;
242 	}
243 }
244 
245 // #pragma mark - playback control
246 
247 
248 void
249 PlaybackManager::StartPlaying(bool atBeginning)
250 {
251 	TRACE("PlaybackManager::StartPlaying()\n");
252 	int32 playMode = PlayMode();
253 	if (playMode == MODE_PLAYING_PAUSED_FORWARD)
254 		SetPlayMode(MODE_PLAYING_FORWARD, !atBeginning);
255 	if (playMode == MODE_PLAYING_PAUSED_BACKWARD)
256 		SetPlayMode(MODE_PLAYING_BACKWARD, !atBeginning);
257 }
258 
259 
260 void
261 PlaybackManager::StopPlaying()
262 {
263 	TRACE("PlaybackManager::StopPlaying()\n");
264 	int32 playMode = PlayMode();
265 	if (playMode == MODE_PLAYING_FORWARD)
266 		SetPlayMode(MODE_PLAYING_PAUSED_FORWARD, true);
267 	if (playMode == MODE_PLAYING_BACKWARD)
268 		SetPlayMode(MODE_PLAYING_PAUSED_BACKWARD, true);
269 }
270 
271 
272 void
273 PlaybackManager::TogglePlaying(bool atBeginning)
274 {
275 	// playmodes (paused <-> playing) are the negative of each other
276 	SetPlayMode(-PlayMode(), !atBeginning);
277 }
278 
279 
280 void
281 PlaybackManager::PausePlaying()
282 {
283 	if (PlayMode() > 0)
284 		TogglePlaying();
285 }
286 
287 
288 bool
289 PlaybackManager::IsPlaying() const
290 {
291 	return (PlayMode() > 0);
292 }
293 
294 
295 int32
296 PlaybackManager::PlayMode() const
297 {
298 	if (!_LastState())
299 		return MODE_PLAYING_PAUSED_FORWARD;
300 	return _LastState()->play_mode;
301 }
302 
303 
304 int32
305 PlaybackManager::LoopMode() const
306 {
307 	if (!_LastState())
308 		return LOOPING_ALL;
309 	return _LastState()->loop_mode;
310 }
311 
312 
313 bool
314 PlaybackManager::IsLoopingEnabled() const
315 {
316 	if (!_LastState())
317 		return true;
318 	return _LastState()->looping_enabled;
319 }
320 
321 
322 int32
323 PlaybackManager::CurrentFrame() const
324 {
325 	return PlaylistFrameAtFrame(FrameForTime(fPerformanceTime));
326 }
327 
328 
329 float
330 PlaybackManager::Speed() const
331 {
332 	if (!_LastState())
333 		return 1.0;
334 	return _LastSpeedInfo()->set_speed;
335 }
336 
337 
338 void
339 PlaybackManager::SetFramesPerSecond(float framesPerSecond)
340 {
341 	if (framesPerSecond != fFrameRate) {
342 		fFrameRate = framesPerSecond;
343 		NotifyFPSChanged(fFrameRate);
344 	}
345 }
346 
347 
348 float
349 PlaybackManager::FramesPerSecond() const
350 {
351 	return fFrameRate;
352 }
353 
354 
355 void
356 PlaybackManager::FrameDropped() const
357 {
358 	NotifyFrameDropped();
359 }
360 
361 
362 void
363 PlaybackManager::DurationChanged()
364 {
365 	if (!_LastState())
366 		return;
367 	int32 frameCount = Duration();
368 	if (frameCount != _LastState()->frame_count) {
369 		PlayingState* state = new PlayingState(*_LastState());
370 		state->frame_count = frameCount;
371 		state->max_frame_count = frameCount;
372 
373 		_PushState(state, true);
374 	}
375 }
376 
377 /*! Creates a new playing state that equals the previous one aside from
378 	its current frame which is set to /frame/.
379 	The new state will be activated as soon as possible. */
380 void
381 PlaybackManager::SetCurrentFrame(int64 frame)
382 {
383 	if (_LastState()->current_frame == frame) {
384 		NotifySeekHandled();
385 		return;
386 	}
387 	PlayingState* newState = new PlayingState(*_LastState());
388 	newState->current_frame = frame;
389 	newState->is_seek_request = true;
390 	_PushState(newState, false);
391 }
392 
393 
394 /*!	Creates a new playing state that equals the previous one aside from
395 	its playing mode which is set to /mode/.
396 	The new state will be activated as soon as possible.
397 	If /continuePlaying/ is true and the new state is a `not stopped'
398 	state, playing continues at the frame the last state reaches when the
399 	new state becomes active (or the next frame in the playing range).
400 	If /continuePlaying/ is false, playing starts at the beginning of the
401 	playing range (taking the playing direction into consideration). */
402 void
403 PlaybackManager::SetPlayMode(int32 mode, bool continuePlaying)
404 {
405 //printf("PlaybackManager::SetPlayMode(%ld, %d)\n", mode, continuePlaying);
406 	PlayingState* newState = new PlayingState(*_LastState());
407 	newState->play_mode = mode;
408 	// Jump to the playing start frame if we should not continue, where we
409 	// stop.
410 	if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
411 		newState->current_frame = _PlayingStartFrameFor(newState);
412 	_PushState(newState, continuePlaying);
413 	NotifyPlayModeChanged(mode);
414 }
415 
416 
417 /*!	Creates a new playing state that equals the previous one aside from
418 	its loop mode which is set to /mode/.
419 	The new state will be activated as soon as possible.
420 	If /continuePlaying/ is true and the new state is a `not stopped'
421 	state, playing continues at the frame the last state reaches when the
422 	new state becomes active (or the next frame in the playing range).
423 	If /continuePlaying/ is false, playing starts at the beginning of the
424 	playing range (taking the playing direction into consideration). */
425 void
426 PlaybackManager::SetLoopMode(int32 mode, bool continuePlaying)
427 {
428 	PlayingState* newState = new PlayingState(*_LastState());
429 	newState->loop_mode = mode;
430 	// Jump to the playing start frame if we should not continue, where we
431 	// stop.
432 	if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
433 		newState->current_frame = _PlayingStartFrameFor(newState);
434 	_PushState(newState, continuePlaying);
435 	NotifyLoopModeChanged(mode);
436 }
437 
438 
439 /*	Creates a new playing state that equals the previous one aside from
440 	its looping enabled flag which is set to /enabled/.
441 	The new state will be activated as soon as possible.
442 	If /continuePlaying/ is true and the new state is a `not stopped'
443 	state, playing continues at the frame the last state reaches when the
444 	new state becomes active (or the next frame in the playing range).
445 	If /continuePlaying/ is false, playing starts at the beginning of the
446 	playing range (taking the playing direction into consideration). */
447 void
448 PlaybackManager::SetLoopingEnabled(bool enabled, bool continuePlaying)
449 {
450 	PlayingState* newState = new PlayingState(*_LastState());
451 	newState->looping_enabled = enabled;
452 	// Jump to the playing start frame if we should not continue, where we
453 	// stop.
454 	if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
455 		newState->current_frame = _PlayingStartFrameFor(newState);
456 	_PushState(newState, continuePlaying);
457 	NotifyLoopingEnabledChanged(enabled);
458 }
459 
460 
461 void
462 PlaybackManager::SetSpeed(float speed)
463 {
464 	SpeedInfo* lastSpeed = _LastSpeedInfo();
465 	if (speed != lastSpeed->set_speed) {
466 		SpeedInfo* info = new SpeedInfo(*lastSpeed);
467 		info->activation_frame = NextFrame();
468 		info->set_speed = speed;
469 		if (_PlayingDirectionFor(_StateAtFrame(info->activation_frame)) != 0)
470 			info->speed = info->set_speed;
471 		else
472 			info->speed = 1.0;
473 		_PushSpeedInfo(info);
474 	}
475 }
476 
477 
478 // #pragma mark -
479 
480 
481 /*!	Returns the first frame at which a new playing state could become active,
482 	that is the first frame for that neither the audio nor the video producer
483 	have fetched data.*/
484 int64
485 PlaybackManager::NextFrame() const
486 {
487 	if (fNoAudio)
488 		return FrameForTime(fCurrentVideoTime - 1) + 1;
489 
490 	return FrameForTime(max((bigtime_t)fCurrentAudioTime,
491 		(bigtime_t)fCurrentVideoTime) - 1) + 1;
492 }
493 
494 
495 //! Returns the Playlist frame for NextFrame().
496 int64
497 PlaybackManager::NextPlaylistFrame() const
498 {
499 	return PlaylistFrameAtFrame(NextFrame());
500 }
501 
502 
503 int64
504 PlaybackManager::FirstPlaybackRangeFrame()
505 {
506 	PlayingState* state = _StateAtFrame(CurrentFrame());
507 	switch (state->loop_mode) {
508 		case LOOPING_RANGE:
509 			return state->start_frame;
510 		case LOOPING_SELECTION:
511 			// TODO: ...
512 			return 0;
513 		case LOOPING_VISIBLE:
514 			return state->first_visible_frame;
515 
516 		case LOOPING_ALL:
517 		default:
518 			return 0;
519 	}
520 }
521 
522 
523 int64
524 PlaybackManager::LastPlaybackRangeFrame()
525 {
526 	PlayingState* state = _StateAtFrame(CurrentFrame());
527 	switch (state->loop_mode) {
528 		case LOOPING_RANGE:
529 			return state->end_frame;
530 		case LOOPING_SELECTION:
531 			// TODO: ...
532 			return state->frame_count - 1;
533 		case LOOPING_VISIBLE:
534 			return state->last_visible_frame;
535 
536 		case LOOPING_ALL:
537 		default:
538 			return state->frame_count - 1;
539 	}
540 }
541 
542 
543 // #pragma mark -
544 
545 
546 int64
547 PlaybackManager::StartFrameAtFrame(int64 frame)
548 {
549 	return _StateAtFrame(frame)->start_frame;
550 }
551 
552 
553 int64
554 PlaybackManager::StartFrameAtTime(bigtime_t time)
555 {
556 	return _StateAtTime(time)->start_frame;
557 }
558 
559 
560 int64
561 PlaybackManager::EndFrameAtFrame(int64 frame)
562 {
563 	return _StateAtTime(frame)->end_frame;
564 }
565 
566 
567 int64
568 PlaybackManager::EndFrameAtTime(bigtime_t time)
569 {
570 	return _StateAtTime(time)->end_frame;
571 }
572 
573 
574 int64
575 PlaybackManager::FrameCountAtFrame(int64 frame)
576 {
577 	return _StateAtTime(frame)->frame_count;
578 }
579 
580 
581 int64
582 PlaybackManager::FrameCountAtTime(bigtime_t time)
583 {
584 	return _StateAtTime(time)->frame_count;
585 }
586 
587 
588 int32
589 PlaybackManager::PlayModeAtFrame(int64 frame)
590 {
591 	return _StateAtTime(frame)->play_mode;
592 }
593 
594 
595 int32
596 PlaybackManager::PlayModeAtTime(bigtime_t time)
597 {
598 	return _StateAtTime(time)->play_mode;
599 }
600 
601 
602 int32
603 PlaybackManager::LoopModeAtFrame(int64 frame)
604 {
605 	return _StateAtTime(frame)->loop_mode;
606 }
607 
608 
609 int32
610 PlaybackManager::LoopModeAtTime(bigtime_t time)
611 {
612 	return _StateAtTime(time)->loop_mode;
613 }
614 
615 
616 /*!	Returns which Playlist frame should be displayed at (performance) video
617 	frame /frame/. Additionally the playing direction (0, 1, -1) is returned,
618 	as well as if a new playing state becomes active with this frame.
619 	A new playing state is installed, if either some data directly concerning
620 	the playing (play mode, loop mode, playing ranges, selection...) or the
621 	Playlist has changed. */
622 int64
623 PlaybackManager::PlaylistFrameAtFrame(int64 frame, int32& playingDirection,
624 	bool& newState) const
625 {
626 //TRACE("PlaybackManager::PlaylistFrameAtFrame(%lld)\n", frame);
627 	PlayingState* state = _StateAtFrame(frame);
628 	newState = (state->activation_frame == frame);
629 	playingDirection = _PlayingDirectionFor(state);
630 //TRACE("playing state: activation frame: %lld, current frame: %lld, dir: %ld\n",
631 //state->activation_frame, state->current_frame, state->play_mode);
632 	// The first part of the index calculation is invariable for a state. We
633 	// could add it to the state data.
634 	int64 result = state->current_frame;
635 	if (playingDirection != 0) {
636 //		int64 index = _RangeFrameForFrame(state, state->current_frame)
637 		int64 index = state->range_index
638 			+ (frame - state->activation_frame) * playingDirection;
639 		result = _FrameForRangeFrame(state, index);
640 	}
641 //TRACE("PlaybackManager::PlaylistFrameAtFrame() done: %lld\n", result);
642 //printf("PlaybackManager::PlaylistFrameAtFrame(%lld): %lld, direction: %ld\n",
643 //	frame, result, playingDirection);
644 	return result;
645 }
646 
647 
648 int64
649 PlaybackManager::PlaylistFrameAtFrame(int64 frame, int32& playingDirection) const
650 {
651 	bool newState;
652 	return PlaylistFrameAtFrame(frame, playingDirection, newState);
653 }
654 
655 
656 int64
657 PlaybackManager::PlaylistFrameAtFrame(int64 frame) const
658 {
659 	bool newState;
660 	int32 playingDirection;
661 	return PlaylistFrameAtFrame(frame, playingDirection, newState);
662 }
663 
664 
665 /*!	Returns the index of the next frame after /startFrame/ at which a
666 	playing state or speed change occurs or /endFrame/, if this happens to be
667 	earlier. */
668 int64
669 PlaybackManager::NextChangeFrame(int64 startFrame, int64 endFrame) const
670 {
671 	int32 startIndex = _IndexForFrame(startFrame);
672 	int32 endIndex = _IndexForFrame(endFrame);
673 	if (startIndex < endIndex)
674 		endFrame = _StateAt(startIndex + 1)->activation_frame;
675 	startIndex = _SpeedInfoIndexForFrame(startFrame);
676 	endIndex = _SpeedInfoIndexForFrame(endFrame);
677 	if (startIndex < endIndex)
678 		endFrame = _SpeedInfoAt(startIndex + 1)->activation_frame;
679 	return endFrame;
680 }
681 
682 
683 /*!	Returns the next time after /startTime/ at which a playing state or
684 	speed change occurs or /endTime/, if this happens to be earlier. */
685 bigtime_t
686 PlaybackManager::NextChangeTime(bigtime_t startTime, bigtime_t endTime) const
687 {
688 	int32 startIndex = _IndexForTime(startTime);
689 	int32 endIndex = _IndexForTime(endTime);
690 	if (startIndex < endIndex)
691 		endTime = TimeForFrame(_StateAt(startIndex + 1)->activation_frame);
692 	startIndex = _SpeedInfoIndexForTime(startTime);
693 	endIndex = _SpeedInfoIndexForTime(endTime);
694 	if (startIndex < endIndex)
695 		endTime = TimeForFrame(_SpeedInfoAt(startIndex + 1)->activation_frame);
696 	return endTime;
697 }
698 
699 
700 /*!	Returns a contiguous Playlist frame interval for a given frame interval.
701 	The returned interval may be smaller than the supplied one. Therefore
702 	the supplied /endFrame/ is adjusted.
703 	The value written to /xEndFrame/ is the first frame that doesn't belong
704 	to the interval. /playingDirection/ may either be -1 for backward,
705 	1 for forward or 0 for not playing. */
706 void
707 PlaybackManager::GetPlaylistFrameInterval(int64 startFrame, int64& endFrame,
708 	int64& xStartFrame, int64& xEndFrame, int32& playingDirection) const
709 {
710 	// limit the interval to one state and speed info
711 	endFrame = NextChangeFrame(startFrame, endFrame);
712 	// init return values
713 	xStartFrame = PlaylistFrameAtFrame(startFrame);
714 	xEndFrame = xStartFrame;
715 	playingDirection = _PlayingDirectionFor(_StateAtFrame(startFrame));
716 	// Step through the interval and check whether the respective Playlist
717 	// frames belong to the Playlist interval.
718 	bool endOfInterval = false;
719 	int64 intervalLength = 0;
720 	while (startFrame < endFrame && !endOfInterval) {
721 		intervalLength++;
722 		startFrame++;
723 		xEndFrame += playingDirection;
724 		endOfInterval = (PlaylistFrameAtFrame(startFrame) != xEndFrame);
725 	}
726 	// order the interval bounds, if playing backwards
727 	if (xStartFrame > xEndFrame) {
728 		swap(xStartFrame, xEndFrame);
729 		xStartFrame++;
730 		xEndFrame++;
731 	}
732 }
733 
734 
735 /*!	The same as GetPlaylistFrameInterval() just for time instead of frame
736 	intervals. Note, that /startTime/ and /endTime/ measure
737 	performance times whereas /xStartTime/ and /xEndTime/ specifies an
738 	Playlist time interval. In general it does not hold
739 	xEndTime - xStartTime == endTime - startTime, even if playing (think
740 	of a playing speed != 1.0). */
741 void
742 PlaybackManager::GetPlaylistTimeInterval(bigtime_t startTime,
743 	bigtime_t& endTime, bigtime_t& xStartTime, bigtime_t& xEndTime,
744 	float& playingSpeed) const
745 {
746 	// Get the frames that bound the given time interval. The end frame might
747 	// be greater than necessary, but that doesn't harm.
748 	int64 startFrame = FrameForTime(startTime);
749 	int64 endFrame = FrameForTime(endTime) + 1;
750 	SpeedInfo* info = _SpeedInfoForFrame(startFrame);
751 	// Get the Playlist frame interval that belongs to the frame interval.
752 	int64 xStartFrame;
753 	int64 xEndFrame;
754 	int32 playingDirection;
755 	GetPlaylistFrameInterval(startFrame, endFrame, xStartFrame, xEndFrame,
756 		playingDirection);
757 	// Calculate the performance time interval end/length.
758 	bigtime_t endTimeForFrame = TimeForFrame(endFrame);
759 	endTime = min(endTime, endTimeForFrame);
760 	bigtime_t intervalLength = endTime - startTime;
761 
762 	// Finally determine the time bounds for the Playlist interval (depending
763 	// on the playing direction).
764 	switch (playingDirection) {
765 		// forward
766 		case 1:
767 		{
768 //			xStartTime = PlaylistTimeForFrame(xStartFrame)
769 //						 + startTime - TimeForFrame(startFrame);
770 //			xEndTime = xStartTime + intervalLength;
771 
772 // TODO: The current method does not handle the times the same way.
773 //       It may happen, that for the same performance time different
774 //       Playlist times (within a frame) are returned when passing it
775 //       one time as a start time and another time as an end time.
776 			xStartTime = PlaylistTimeForFrame(xStartFrame)
777 				+ bigtime_t(double(startTime - TimeForFrame(startFrame))
778 							* info->speed);
779 			xEndTime = xStartTime
780 					   + bigtime_t((double)intervalLength * info->speed);
781 			break;
782 		}
783 		// backward
784 		case -1:
785 		{
786 //			xEndTime = PlaylistTimeForFrame(xEndFrame)
787 //					   - startTime + TimeForFrame(startFrame);
788 //			xStartTime = xEndTime - intervalLength;
789 
790 			xEndTime = PlaylistTimeForFrame(xEndFrame)
791 				- bigtime_t(double(startTime - TimeForFrame(startFrame))
792 							* info->speed);
793 			xStartTime = xEndTime
794 					   - bigtime_t((double)intervalLength * info->speed);
795 			break;
796 		}
797 		// not playing
798 		case 0:
799 		default:
800 			xStartTime = PlaylistTimeForFrame(xStartFrame);
801 			xEndTime = xStartTime;
802 			break;
803 	}
804 	playingSpeed = (float)playingDirection * info->speed;
805 }
806 
807 
808 /*!	Returns the frame that is being performed at the supplied time.
809 	It holds TimeForFrame(frame) <= time < TimeForFrame(frame + 1). */
810 int64
811 PlaybackManager::FrameForTime(bigtime_t time) const
812 {
813 //TRACE("PlaybackManager::FrameForTime(%lld)\n", time);
814 //	return (int64)((double)time * (double)fFrameRate / 1000000.0);
815 	// In order to avoid problems caused by rounding errors, we check
816 	// if for the resulting frame holds
817 	// TimeForFrame(frame) <= time < TimeForFrame(frame + 1).
818 //	int64 frame = (int64)((double)time * (double)fFrameRate / 1000000.0);
819 	SpeedInfo* info = _SpeedInfoForTime(time);
820 if (!info) {
821 	fprintf(stderr, "PlaybackManager::FrameForTime() - no SpeedInfo!\n");
822 	return 0;
823 }
824 	int64 frame = (int64)(((double)time - info->activation_time)
825 		* (double)fFrameRate * info->speed / 1000000.0)
826 		+ info->activation_frame;
827 	if (TimeForFrame(frame) > time)
828 		frame--;
829 	else if (TimeForFrame(frame + 1) <= time)
830 		frame++;
831 //TRACE("PlaybackManager::FrameForTime() done: %lld\n", frame);
832 //printf("PlaybackManager::FrameForTime(%lld): %lld\n", time, frame);
833 	return frame;
834 }
835 
836 
837 /*!	Returns the time at which the supplied frame should be performed (started
838 	to be performed). */
839 bigtime_t
840 PlaybackManager::TimeForFrame(int64 frame) const
841 {
842 //	return (bigtime_t)((double)frame * 1000000.0 / (double)fFrameRate);
843 	SpeedInfo* info = _SpeedInfoForFrame(frame);
844 if (!info) {
845 	fprintf(stderr, "PlaybackManager::TimeForFrame() - no SpeedInfo!\n");
846 	return 0;
847 }
848 //	return (bigtime_t)((double)(frame - info->activation_frame) * 1000000.0
849 //					   / ((double)fFrameRate * info->speed))
850 // 		   + info->activation_time;
851 bigtime_t result = (bigtime_t)((double)(frame - info->activation_frame) * 1000000.0
852 / ((double)fFrameRate * info->speed)) + info->activation_time;
853 //fprintf(stderr, "PlaybackManager::TimeForFrame(%lld): %lld\n", frame, result);
854 return result;
855 }
856 
857 
858 /*!	Returns the Playlist frame for an Playlist time.
859 	It holds PlaylistTimeForFrame(frame) <= time <
860 	PlaylistTimeForFrame(frame + 1). */
861 int64
862 PlaybackManager::PlaylistFrameForTime(bigtime_t time) const
863 {
864 	// In order to avoid problems caused by rounding errors, we check
865 	// if for the resulting frame holds
866 	// PlaylistTimeForFrame(frame) <= time < PlaylistTimeForFrame(frame + 1).
867 	int64 frame = (int64)((double)time * (double)fFrameRate / 1000000.0);
868 	if (TimeForFrame(frame) > time)
869 		frame--;
870 	else if (TimeForFrame(frame + 1) <= time)
871 		frame++;
872 	return frame;
873 }
874 
875 
876 //! Returns the Playlist start time for an Playlist frame.
877 bigtime_t
878 PlaybackManager::PlaylistTimeForFrame(int64 frame) const
879 {
880 	return (bigtime_t)((double)frame * 1000000.0 / (double)fFrameRate);
881 }
882 
883 
884 //! To be called when done with all activities concerning audio before /time/.
885 void
886 PlaybackManager::SetCurrentAudioTime(bigtime_t time)
887 {
888 TRACE("PlaybackManager::SetCurrentAudioTime(%lld)\n", time);
889 	bigtime_t lastFrameTime = _TimeForLastFrame();
890 	fCurrentAudioTime = time;
891 //	_UpdateStates();
892 	bigtime_t newLastFrameTime = _TimeForLastFrame();
893 	if (lastFrameTime != newLastFrameTime) {
894 		bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
895 		EventQueue::Default().AddEvent(new MessageEvent(eventTime, this));
896 		_CheckStopPlaying();
897 	}
898 }
899 
900 
901 //! To be called when done with all activities concerning video before /frame/.
902 void
903 PlaybackManager::SetCurrentVideoFrame(int64 frame)
904 {
905 	SetCurrentVideoTime(TimeForFrame(frame));
906 }
907 
908 
909 //! To be called when done with all activities concerning video before /time/.
910 void
911 PlaybackManager::SetCurrentVideoTime(bigtime_t time)
912 {
913 TRACE("PlaybackManager::SetCurrentVideoTime(%lld)\n", time);
914 	bigtime_t lastFrameTime = _TimeForLastFrame();
915 	fCurrentVideoTime = time;
916 //	_UpdateStates();
917 	bigtime_t newLastFrameTime = _TimeForLastFrame();
918 	if (lastFrameTime != newLastFrameTime) {
919 		bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
920 		EventQueue::Default().AddEvent(new MessageEvent(eventTime, this));
921 		_CheckStopPlaying();
922 	}
923 }
924 
925 
926 //! To be called as soon as video frame /frame/ is being performed.
927 void
928 PlaybackManager::SetPerformanceFrame(int64 frame)
929 {
930 	SetPerformanceFrame(TimeForFrame(frame));
931 }
932 
933 
934 /*!	Similar to SetPerformanceTime() just with a time instead of a frame
935 	argument. */
936 void
937 PlaybackManager::SetPerformanceTime(bigtime_t time)
938 {
939 	int32 oldCurrentFrame = CurrentFrame();
940 	fPerformanceTime = time;
941 	_UpdateStates();
942 	_UpdateSpeedInfos();
943 	int32 currentFrame = CurrentFrame();
944 
945 //printf("PlaybackManager::SetPerformanceTime(%lld): %ld -> %ld\n",
946 //	time, oldCurrentFrame, currentFrame);
947 
948 	if (currentFrame != oldCurrentFrame)
949 		NotifyCurrentFrameChanged(currentFrame);
950 }
951 
952 
953 // #pragma mark - Listeners
954 
955 
956 void
957 PlaybackManager::AddListener(PlaybackListener* listener)
958 {
959 	if (!listener)
960 		return;
961 
962 	if (!Lock())
963 		return;
964 
965 	if (!fListeners.HasItem(listener) && fListeners.AddItem(listener)) {
966 		// bring listener up2date, if we have been initialized
967 		if (_LastState()) {
968 			listener->PlayModeChanged(PlayMode());
969 			listener->LoopModeChanged(LoopMode());
970 			listener->LoopingEnabledChanged(IsLoopingEnabled());
971 			listener->VideoBoundsChanged(VideoBounds());
972 			listener->FramesPerSecondChanged(FramesPerSecond());
973 			listener->SpeedChanged(Speed());
974 			listener->CurrentFrameChanged(CurrentFrame());
975 		}
976 	}
977 
978 	Unlock();
979 }
980 
981 
982 void
983 PlaybackManager::RemoveListener(PlaybackListener* listener)
984 {
985 	if (listener && Lock()) {
986 		fListeners.RemoveItem(listener);
987 		Unlock();
988 	}
989 }
990 
991 
992 void
993 PlaybackManager::NotifyPlayModeChanged(int32 mode) const
994 {
995 	for (int32 i = 0;
996 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
997 		 i++) {
998 		listener->PlayModeChanged(mode);
999 	}
1000 }
1001 
1002 
1003 void
1004 PlaybackManager::NotifyLoopModeChanged(int32 mode) const
1005 {
1006 	for (int32 i = 0;
1007 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1008 		 i++) {
1009 		listener->LoopModeChanged(mode);
1010 	}
1011 }
1012 
1013 
1014 void
1015 PlaybackManager::NotifyLoopingEnabledChanged(bool enabled) const
1016 {
1017 	for (int32 i = 0;
1018 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1019 		 i++) {
1020 		listener->LoopingEnabledChanged(enabled);
1021 	}
1022 }
1023 
1024 
1025 void
1026 PlaybackManager::NotifyVideoBoundsChanged(BRect bounds) const
1027 {
1028 	for (int32 i = 0;
1029 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1030 		 i++) {
1031 		listener->VideoBoundsChanged(bounds);
1032 	}
1033 }
1034 
1035 
1036 void
1037 PlaybackManager::NotifyFPSChanged(float fps) const
1038 {
1039 	for (int32 i = 0;
1040 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1041 		 i++) {
1042 		listener->FramesPerSecondChanged(fps);
1043 	}
1044 }
1045 
1046 
1047 void
1048 PlaybackManager::NotifyCurrentFrameChanged(int32 frame) const
1049 {
1050 	for (int32 i = 0;
1051 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1052 		 i++) {
1053 		listener->CurrentFrameChanged(frame);
1054 	}
1055 }
1056 
1057 
1058 void
1059 PlaybackManager::NotifySpeedChanged(float speed) const
1060 {
1061 	for (int32 i = 0;
1062 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1063 		 i++) {
1064 		listener->SpeedChanged(speed);
1065 	}
1066 }
1067 
1068 
1069 void
1070 PlaybackManager::NotifyFrameDropped() const
1071 {
1072 	for (int32 i = 0;
1073 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1074 		 i++) {
1075 		listener->FrameDropped();
1076 	}
1077 }
1078 
1079 
1080 void
1081 PlaybackManager::NotifyStopFrameReached() const
1082 {
1083 	// not currently implemented in PlaybackListener interface
1084 }
1085 
1086 
1087 void
1088 PlaybackManager::NotifySeekHandled() const
1089 {
1090 	// not currently implemented in PlaybackListener interface
1091 }
1092 
1093 
1094 void
1095 PlaybackManager::PrintState(PlayingState* state)
1096 {
1097 	TRACE("state: activation frame: %lld, current frame: %lld, "
1098 		   "start frame: %lld, end frame: %lld, frame count: %lld, "
1099 		   "first visible: %lld, last visible: %lld, selection (...), "
1100 		   "play mode: %ld, loop mode: %ld\n",
1101 			state->activation_frame,
1102 			state->current_frame,
1103 			state->start_frame,
1104 			state->end_frame,
1105 			state->frame_count,
1106 			state->first_visible_frame,
1107 			state->last_visible_frame,
1108 //			state->selection,
1109 			state->play_mode,
1110 			state->loop_mode);
1111 }
1112 
1113 
1114 void
1115 PlaybackManager::PrintStateAtFrame(int64 frame)
1116 {
1117 	TRACE("frame %lld: ", frame);
1118 	PrintState(_StateAtFrame(frame));
1119 }
1120 
1121 
1122 /*!	Appends the supplied state to the list of states. If the state would
1123 	become active at the same time as _LastState() the latter is removed
1124 	and deleted. However, the activation time for the new state is adjusted,
1125 	so that it is >= that of the last state and >= the current audio and
1126 	video time. If the additional parameter /adjustCurrentFrame/ is true,
1127 	the new state's current frame is tried to be set to the frame that is
1128 	reached at the time the state will become active. In every case
1129 	it is ensured that the current frame lies within the playing range
1130 	(if playing). */
1131 void
1132 PlaybackManager::_PushState(PlayingState* state, bool adjustCurrentFrame)
1133 {
1134 //	if (!_LastState())
1135 //		debugger("PlaybackManager::_PushState() used before Init()\n");
1136 
1137 TRACE("PlaybackManager::_PushState()\n");
1138 	if (state == NULL)
1139 		return;
1140 
1141 	// unset fStopPlayingFrame
1142 	int64 oldStopPlayingFrame = fStopPlayingFrame;
1143 	fStopPlayingFrame = -1;
1144 	// get last state
1145 	PlayingState* lastState = _LastState();
1146 	int64 activationFrame = max(max(state->activation_frame,
1147 									lastState->activation_frame),
1148 								NextFrame());
1149 TRACE("  state activation frame: %lld, last state activation frame: %lld, "
1150 "NextFrame(): %lld\n", state->activation_frame, lastState->activation_frame,
1151 NextFrame());
1152 
1153 	int64 currentFrame = 0;
1154 	// remember the current frame, if necessary
1155 	if (adjustCurrentFrame)
1156 		currentFrame = PlaylistFrameAtFrame(activationFrame);
1157 	// check whether it is active
1158 	// (NOTE: We may want to keep the last state, if it is not active,
1159 	//  but then the new state should become active after the last one.
1160 	//  Thus we had to replace `NextFrame()' with `activationFrame'.)
1161 	if (lastState->activation_frame >= NextFrame()) {
1162 		// it isn't -- remove it
1163 		fStates.RemoveItem(fStates.CountItems() - 1);
1164 TRACE("deleting last \n");
1165 PrintState(lastState);
1166 		delete lastState;
1167 	} else {
1168 		// it is -- keep it
1169 	}
1170 	// adjust the new state's current frame and activation frame
1171 	if (adjustCurrentFrame)
1172 		state->current_frame = currentFrame;
1173 	int32 playingDirection = _PlayingDirectionFor(state);
1174 	if (playingDirection != 0) {
1175 		state->current_frame
1176 			= _NextFrameInRange(state, state->current_frame);
1177 	} else {
1178 		// If not playing, we check at least, if the current frame lies
1179 		// within the interval [0, max_frame_count).
1180 		if (state->current_frame >= state->max_frame_count)
1181 			state->current_frame = state->max_frame_count - 1;
1182 		if (state->current_frame < 0)
1183 			state->current_frame = 0;
1184 	}
1185 	state->range_index = _RangeFrameForFrame(state, state->current_frame);
1186 	state->activation_frame = activationFrame;
1187 	fStates.AddItem(state);
1188 PrintState(state);
1189 TRACE("_PushState: state count: %ld\n", fStates.CountItems());
1190 	// push a new speed info
1191 	SpeedInfo* speedInfo = new SpeedInfo(*_LastSpeedInfo());
1192 	if (playingDirection == 0)
1193 		speedInfo->speed = 1.0;
1194 	else
1195 		speedInfo->speed = speedInfo->set_speed;
1196 	speedInfo->activation_frame = state->activation_frame;
1197 	_PushSpeedInfo(speedInfo);
1198 	// If the new state is a playing state and looping is turned off,
1199 	// determine when playing shall stop.
1200 	if (playingDirection != 0 && !state->looping_enabled) {
1201 		int64 startFrame, endFrame, frameCount;
1202 		_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1203 		if (playingDirection == -1)
1204 			swap(startFrame, endFrame);
1205 		// If we shall stop at the frame we start, set the current frame
1206 		// to the beginning of the range.
1207 		// We have to take care, since this state may equal the one
1208 		// before (or probably differs in just one (unimportant)
1209 		// parameter). This happens for instance, if the user changes the
1210 		// data or start/end frame... while playing. In this case setting
1211 		// the current frame to the start frame is unwanted. Therefore
1212 		// we check whether the previous state was intended to stop
1213 		// at the activation frame of this state.
1214 		if (oldStopPlayingFrame != state->activation_frame
1215 			&& state->current_frame == endFrame && frameCount > 1) {
1216 			state->current_frame = startFrame;
1217 			state->range_index
1218 				= _RangeFrameForFrame(state, state->current_frame);
1219 		}
1220 		if (playingDirection == 1) {	// forward
1221 			fStopPlayingFrame = state->activation_frame
1222 								+ frameCount - state->range_index - 1;
1223 		} else {						// backwards
1224 			fStopPlayingFrame = state->activation_frame
1225 								+ state->range_index;
1226 		}
1227 		_CheckStopPlaying();
1228 	}
1229 TRACE("PlaybackManager::_PushState() done\n");
1230 }
1231 
1232 
1233 /*!	Removes and deletes all states that are obsolete, that is which stopped
1234 	being active at or before the current audio and video time. */
1235 void
1236 PlaybackManager::_UpdateStates()
1237 {
1238 //	int32 firstActive = min(_IndexForTime(fCurrentAudioTime),
1239 //							_IndexForTime(fCurrentVideoTime));
1240 	// Performance time should always be the least one.
1241 	int32 firstActive = _IndexForTime(fPerformanceTime);
1242 //TRACE("firstActive: %ld, numStates: %ld\n", firstActive, fStates.CountItems());
1243 	for (int32 i = 0; i < firstActive; i++)
1244 		delete _StateAt(i);
1245 	if (firstActive > 0)
1246 {
1247 		fStates.RemoveItems(0, firstActive);
1248 TRACE("_UpdateStates: states removed: %ld, state count: %ld\n",
1249 firstActive, fStates.CountItems());
1250 }
1251 	PlayingState* currentState = _StateAt(firstActive);
1252 	if (currentState != NULL && currentState->is_seek_request) {
1253 		currentState->is_seek_request = false;
1254 		NotifySeekHandled();
1255 	}
1256 }
1257 
1258 
1259 /*!	Returns the index of the state, that is active at frame /frame/.
1260 	The index of the first frame (0) is returned, if /frame/ is even less
1261 	than the frame at which this state becomes active. */
1262 int32
1263 PlaybackManager::_IndexForFrame(int64 frame) const
1264 {
1265 	int32 index = 0;
1266 	PlayingState* state;
1267 	while (((state = _StateAt(index + 1))) && state->activation_frame <= frame)
1268 		index++;
1269 	return index;
1270 }
1271 
1272 /*!	Returns the index of the state, that is active at time /time/.
1273 	The index of the first frame (0) is returned, if /time/ is even less
1274 	than the time at which this state becomes active. */
1275 int32
1276 PlaybackManager::_IndexForTime(bigtime_t time) const
1277 {
1278 	return _IndexForFrame(FrameForTime(time));
1279 }
1280 
1281 
1282 //! Returns the state that reflects the most recent changes.
1283 PlaybackManager::PlayingState*
1284 PlaybackManager::_LastState() const
1285 {
1286 	return _StateAt(fStates.CountItems() - 1);
1287 }
1288 
1289 
1290 PlaybackManager::PlayingState*
1291 PlaybackManager::_StateAt(int32 index) const
1292 {
1293 	return (PlayingState*)fStates.ItemAt(index);
1294 }
1295 
1296 
1297 PlaybackManager::PlayingState*
1298 PlaybackManager::_StateAtFrame(int64 frame) const
1299 {
1300 	return _StateAt(_IndexForFrame(frame));
1301 }
1302 
1303 
1304 PlaybackManager::PlayingState*
1305 PlaybackManager::_StateAtTime(bigtime_t time) const
1306 {
1307 	return _StateAt(_IndexForTime(time));
1308 }
1309 
1310 
1311 int32
1312 PlaybackManager::_PlayingDirectionFor(int32 playingMode)
1313 {
1314 	int32 direction = 0;
1315 	switch (playingMode) {
1316 		case MODE_PLAYING_FORWARD:
1317 			direction = 1;
1318 			break;
1319 		case MODE_PLAYING_BACKWARD:
1320 			direction = -1;
1321 			break;
1322 		case MODE_PLAYING_PAUSED_FORWARD:
1323 		case MODE_PLAYING_PAUSED_BACKWARD:
1324 			break;
1325 	}
1326 	return direction;
1327 }
1328 
1329 
1330 int32
1331 PlaybackManager::_PlayingDirectionFor(PlayingState* state)
1332 {
1333 	return _PlayingDirectionFor(state->play_mode);
1334 }
1335 
1336 
1337 /*!	Returns the Playlist frame range that bounds the playing range of a given
1338 	state.
1339 	\a startFrame is the lower and \a endFrame the upper bound of the range,
1340 	and \a frameCount the number of frames in the range; it is guaranteed to
1341 	be >= 1, even for an empty selection. */
1342 void
1343 PlaybackManager::_GetPlayingBoundsFor(PlayingState* state, int64& startFrame,
1344 	int64& endFrame, int64& frameCount)
1345 {
1346 	switch (state->loop_mode) {
1347 		case LOOPING_ALL:
1348 			startFrame = 0;
1349 			endFrame = max(startFrame, state->frame_count - 1);
1350 			frameCount = endFrame - startFrame + 1;
1351 			break;
1352 		case LOOPING_RANGE:
1353 			startFrame = state->start_frame;
1354 			endFrame = state->end_frame;
1355 			frameCount = endFrame - startFrame + 1;
1356 			break;
1357 //		case LOOPING_SELECTION:
1358 //			if (!state->selection.IsEmpty()) {
1359 //				startFrame = state->selection.FirstIndex();
1360 //				endFrame = state->selection.LastIndex();
1361 //				frameCount = state->selection.CountIndices();
1362 //TRACE("  LOOPING_SELECTION: %lld - %lld (%lld)\n", startFrame, endFrame,
1363 //frameCount);
1364 //			} else {
1365 //				startFrame = state->current_frame;
1366 //				endFrame = state->current_frame;
1367 //				frameCount = 1;
1368 //			}
1369 //			break;
1370 		case LOOPING_VISIBLE:
1371 			startFrame = state->first_visible_frame;
1372 			endFrame = state->last_visible_frame;
1373 			frameCount = endFrame - startFrame + 1;
1374 			break;
1375 	}
1376 }
1377 
1378 
1379 /*!	Returns the frame at which playing would start for a given playing
1380 	state. If the playing mode for the supplied state specifies a stopped
1381 	or undefined mode, the result is the state's current frame. */
1382 int64
1383 PlaybackManager::_PlayingStartFrameFor(PlayingState* state)
1384 {
1385 	int64 startFrame, endFrame, frameCount, frame = 0;
1386 	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1387 	switch (_PlayingDirectionFor(state)) {
1388 		case -1:
1389 			frame = endFrame;
1390 			break;
1391 		case 1:
1392 			frame = startFrame;
1393 			break;
1394 		default:
1395 			frame = state->current_frame;
1396 			break;
1397 	}
1398 	return frame;
1399 }
1400 
1401 
1402 /*!	Returns the frame at which playing would end for a given playing
1403 	state. If the playing mode for the supplied state specifies a stopped
1404 	or undefined mode, the result is the state's current frame. */
1405 int64
1406 PlaybackManager::_PlayingEndFrameFor(PlayingState* state)
1407 {
1408 	int64 startFrame, endFrame, frameCount, frame = 0;
1409 	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1410 	switch (_PlayingDirectionFor(state)) {
1411 		case -1:
1412 			frame = startFrame;
1413 			break;
1414 		case 1:
1415 			frame = endFrame;
1416 			break;
1417 		default:
1418 			frame = state->current_frame;
1419 			break;
1420 	}
1421 	return frame;
1422 }
1423 
1424 
1425 /*!	Returns the index that the supplied frame has within a playing range.
1426 	If the state specifies a not-playing mode, 0 is returned. The supplied
1427 	frame has to lie within the bounds of the playing range, but it doesn't
1428 	need to be contained, e.g. if the range is not contiguous. In this case
1429 	the index of the next frame within the range (in playing direction) is
1430 	returned. */
1431 int64
1432 PlaybackManager::_RangeFrameForFrame(PlayingState* state, int64 frame)
1433 {
1434 TRACE("PlaybackManager::_RangeFrameForFrame(%lld)\n", frame);
1435 	int64 startFrame, endFrame, frameCount;
1436 	int64 index = 0;
1437 	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1438 TRACE("  start frame: %lld, end frame: %lld, frame count: %lld\n",
1439 startFrame, endFrame, frameCount);
1440 	switch (state->loop_mode) {
1441 		case LOOPING_ALL:
1442 		case LOOPING_RANGE:
1443 		case LOOPING_VISIBLE:
1444 			index = frame - startFrame;
1445 			break;
1446 	}
1447 TRACE("PlaybackManager::_RangeFrameForFrame() done: %lld\n", index);
1448 	return index;
1449 }
1450 
1451 
1452 /*!	Returns the Playlist frame for a playing range index. /index/ doesn't need
1453 	to be in the playing range -- it is mapped into. */
1454 int64
1455 PlaybackManager::_FrameForRangeFrame(PlayingState* state, int64 index)
1456 {
1457 TRACE("PlaybackManager::_FrameForRangeFrame(%lld)\n", index);
1458 	int64 startFrame, endFrame, frameCount;
1459 	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1460 TRACE("  frame range: %lld - %lld, count: %lld\n", startFrame, endFrame,
1461 frameCount);
1462 	// map the index into the index interval of the playing range
1463 	if (frameCount > 1)
1464 		index = (index % frameCount + frameCount) % frameCount;
1465 
1466 	// get the frame for the index
1467 	int32 frame = startFrame;
1468 	switch (state->loop_mode) {
1469 		case LOOPING_ALL:
1470 		case LOOPING_RANGE:
1471 		case LOOPING_VISIBLE:
1472 			frame = startFrame + index;
1473 			break;
1474 	}
1475 TRACE("PlaybackManager::_FrameForRangeFrame() done: %ld\n", frame);
1476 	return frame;
1477 }
1478 
1479 
1480 /*!	Given an arbitrary Playlist frame this function returns the next frame within
1481 	the playing range for the supplied playing state. */
1482 int64
1483 PlaybackManager::_NextFrameInRange(PlayingState* state, int64 frame)
1484 {
1485 	int64 newFrame = frame;
1486 	int64 startFrame, endFrame, frameCount;
1487 	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1488 	if (frame < startFrame || frame > endFrame)
1489 		newFrame = _PlayingStartFrameFor(state);
1490 	else {
1491 		int64 index = _RangeFrameForFrame(state, frame);
1492 		newFrame = _FrameForRangeFrame(state, index);
1493 		if (newFrame > frame && _PlayingDirectionFor(state) == -1)
1494 			newFrame = _FrameForRangeFrame(state, index - 1);
1495 	}
1496 	return newFrame;
1497 }
1498 
1499 
1500 void
1501 PlaybackManager::_PushSpeedInfo(SpeedInfo* info)
1502 {
1503 	if (info) {
1504 		// check the last state
1505 		if (SpeedInfo* lastSpeed = _LastSpeedInfo()) {
1506 			// set the activation time
1507 			info->activation_time = TimeForFrame(info->activation_frame);
1508 			// Remove the last state, if it won't be activated.
1509 			if (lastSpeed->activation_frame == info->activation_frame) {
1510 //fprintf(stderr, "  replacing last speed info\n");
1511 				fSpeeds.RemoveItem(lastSpeed);
1512 				delete lastSpeed;
1513 			}
1514 		}
1515 		fSpeeds.AddItem(info);
1516 //fprintf(stderr, "  speed info pushed: activation frame: %lld, "
1517 //"activation time: %lld, speed: %f\n", info->activation_frame,
1518 //info->activation_time, info->speed);
1519 		// ...
1520 	}
1521 }
1522 
1523 
1524 PlaybackManager::SpeedInfo*
1525 PlaybackManager::_LastSpeedInfo() const
1526 {
1527 	return (SpeedInfo*)fSpeeds.ItemAt(fSpeeds.CountItems() - 1);
1528 }
1529 
1530 
1531 PlaybackManager::SpeedInfo*
1532 PlaybackManager::_SpeedInfoAt(int32 index) const
1533 {
1534 	return (SpeedInfo*)fSpeeds.ItemAt(index);
1535 }
1536 
1537 
1538 int32
1539 PlaybackManager::_SpeedInfoIndexForFrame(int64 frame) const
1540 {
1541 	int32 index = 0;
1542 	SpeedInfo* info;
1543 	while (((info = _SpeedInfoAt(index + 1)))
1544 		   && info->activation_frame <= frame) {
1545 		index++;
1546 	}
1547 //fprintf(stderr, "PlaybackManager::_SpeedInfoIndexForFrame(%lld): %ld\n",
1548 //frame, index);
1549 	return index;
1550 }
1551 
1552 
1553 int32
1554 PlaybackManager::_SpeedInfoIndexForTime(bigtime_t time) const
1555 {
1556 	int32 index = 0;
1557 	SpeedInfo* info;
1558 	while (((info = _SpeedInfoAt(index + 1)))
1559 		   && info->activation_time <= time) {
1560 		index++;
1561 	}
1562 //fprintf(stderr, "PlaybackManager::_SpeedInfoIndexForTime(%lld): %ld\n",
1563 //time, index);
1564 	return index;
1565 }
1566 
1567 
1568 PlaybackManager::SpeedInfo*
1569 PlaybackManager::_SpeedInfoForFrame(int64 frame) const
1570 {
1571 	return _SpeedInfoAt(_SpeedInfoIndexForFrame(frame));
1572 }
1573 
1574 
1575 PlaybackManager::SpeedInfo*
1576 PlaybackManager::_SpeedInfoForTime(bigtime_t time) const
1577 {
1578 	return _SpeedInfoAt(_SpeedInfoIndexForTime(time));
1579 }
1580 
1581 
1582 void
1583 PlaybackManager::_UpdateSpeedInfos()
1584 {
1585 	int32 firstActive = _SpeedInfoIndexForTime(fPerformanceTime);
1586 	for (int32 i = 0; i < firstActive; i++)
1587 		delete _SpeedInfoAt(i);
1588 	if (firstActive > 0)
1589 		fSpeeds.RemoveItems(0, firstActive);
1590 //fprintf(stderr, "  speed infos 0 - %ld removed\n", firstActive);
1591 }
1592 
1593 
1594 /*!	Returns the end time for the last video frame that the video and the
1595 	audio producer both have completely processed. */
1596 bigtime_t
1597 PlaybackManager::_TimeForLastFrame() const
1598 {
1599 	if (fNoAudio)
1600 		return TimeForFrame(FrameForTime(fCurrentVideoTime));
1601 
1602 	return TimeForFrame(FrameForTime(min((bigtime_t)fCurrentAudioTime,
1603 										 (bigtime_t)fCurrentVideoTime)));
1604 }
1605 
1606 
1607 /*!	Returns the start time for the first video frame for which
1608 	neither the video nor the audio producer have fetched data. */
1609 bigtime_t
1610 PlaybackManager::_TimeForNextFrame() const
1611 {
1612 	return TimeForFrame(NextFrame());
1613 }
1614 
1615 
1616 void
1617 PlaybackManager::_CheckStopPlaying()
1618 {
1619 //printf("_CheckStopPlaying() - %lld, next: %lld\n", fStopPlayingFrame, NextFrame());
1620 	if (fStopPlayingFrame > 0 && fStopPlayingFrame <= NextFrame()) {
1621 		StopPlaying();
1622 		NotifyStopFrameReached();
1623 	}
1624 }
1625 
1626