1/*
2 * Controller.cpp - Media Player for the Haiku Operating System
3 *
4 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
5 * Copyright (C) 2007-2008 Stephan A��mus <superstippi@gmx.de>
6 * Copyright (C) 2007-2009 Fredrik Mod��en <[FirstName]@[LastName].se>
7 *
8 * Released under the terms of the MIT license.
9 */
10
11
12#include "Controller.h"
13
14#include <new>
15#include <stdio.h>
16#include <string.h>
17
18#include <Autolock.h>
19#include <Bitmap.h>
20#include <Catalog.h>
21#include <Debug.h>
22#include <Path.h>
23#include <Window.h> // for debugging only
24
25#include "AutoDeleter.h"
26#include "ControllerView.h"
27#include "MainApp.h"
28#include "PlaybackState.h"
29#include "Settings.h"
30#include "VideoView.h"
31
32// suppliers
33#include "AudioTrackSupplier.h"
34#include "MediaTrackAudioSupplier.h"
35#include "MediaTrackVideoSupplier.h"
36#include "ProxyAudioSupplier.h"
37#include "ProxyVideoSupplier.h"
38#include "SubTitles.h"
39#include "TrackSupplier.h"
40#include "VideoTrackSupplier.h"
41
42
43#undef B_TRANSLATION_CONTEXT
44#define B_TRANSLATION_CONTEXT "MediaPlayer-Controller"
45#define MIN_WIDTH 250
46
47using std::nothrow;
48
49
50class TrackSupplierReleaser {
51public:
52	TrackSupplierReleaser(PlaylistItemRef& owner)
53		:
54		fOwner(owner),
55		fRelease(true)
56		{}
57
58	virtual ~TrackSupplierReleaser()
59	{
60		if (fRelease)
61			fOwner.Get()->ReleaseTrackSupplier();
62	}
63
64	void Detach()
65	{
66		fRelease = false;
67	}
68
69private:
70	PlaylistItemRef&	fOwner;
71	bool				fRelease;
72};
73
74
75void
76HandleError(const char *text, status_t err)
77{
78	if (err != B_OK) {
79		printf("%s. error 0x%08x (%s)\n",text, (int)err, strerror(err));
80		fflush(NULL);
81		exit(1);
82	}
83}
84
85
86// #pragma mark - Controller::Listener
87
88
89Controller::Listener::Listener() {}
90Controller::Listener::~Listener() {}
91void Controller::Listener::FileFinished() {}
92void Controller::Listener::FileChanged(PlaylistItem* item, status_t result) {}
93void Controller::Listener::VideoTrackChanged(int32) {}
94void Controller::Listener::AudioTrackChanged(int32) {}
95void Controller::Listener::SubTitleTrackChanged(int32) {}
96void Controller::Listener::VideoStatsChanged() {}
97void Controller::Listener::AudioStatsChanged() {}
98void Controller::Listener::PlaybackStateChanged(uint32) {}
99void Controller::Listener::PositionChanged(float) {}
100void Controller::Listener::SeekHandled(int64 seekFrame) {}
101void Controller::Listener::VolumeChanged(float) {}
102void Controller::Listener::MutedChanged(bool) {}
103
104
105// #pragma mark - Controller
106
107
108enum {
109	MSG_SET_TO = 'stto'
110};
111
112
113Controller::Controller()
114	:
115	NodeManager(),
116	fVideoView(NULL),
117	fVolume(1.0),
118	fActiveVolume(1.0),
119	fMuted(false),
120
121	fItem(NULL),
122
123	fVideoSupplier(new ProxyVideoSupplier()),
124	fAudioSupplier(new ProxyAudioSupplier(this)),
125	fVideoTrackSupplier(NULL),
126	fAudioTrackSupplier(NULL),
127	fSubTitles(NULL),
128	fSubTitlesIndex(-1),
129
130	fCurrentFrame(0),
131	fDuration(0),
132	fVideoFrameRate(25.0),
133
134	fPendingSeekRequests(0),
135	fSeekFrame(-1),
136	fRequestedSeekFrame(-1),
137
138	fGlobalSettingsListener(this),
139
140	fListeners(4)
141{
142	Settings::Default()->AddListener(&fGlobalSettingsListener);
143	_AdoptGlobalSettings();
144
145	fAutoplay = fAutoplaySetting;
146}
147
148
149Controller::~Controller()
150{
151	Settings::Default()->RemoveListener(&fGlobalSettingsListener);
152	SetTo(NULL);
153}
154
155
156// #pragma mark - NodeManager interface
157
158
159void
160Controller::MessageReceived(BMessage* message)
161{
162	switch (message->what) {
163		case MSG_OBJECT_CHANGED:
164			// received from fGlobalSettingsListener
165			// TODO: find out which object, if we ever watch more than
166			// the global settings instance...
167			_AdoptGlobalSettings();
168			break;
169
170		case MSG_SET_TO:
171		{
172			PlaylistItem* item;
173			if (message->FindPointer("item", (void**)&item) == B_OK) {
174				PlaylistItemRef itemRef(item, true);
175					// The reference was passed with the message.
176				SetTo(itemRef);
177			} else
178				_NotifyFileChanged(NULL, B_BAD_VALUE);
179
180			break;
181		}
182
183		default:
184			NodeManager::MessageReceived(message);
185	}
186}
187
188
189int64
190Controller::Duration()
191{
192	return _FrameDuration();
193}
194
195
196VideoTarget*
197Controller::CreateVideoTarget()
198{
199	return fVideoView;
200}
201
202
203VideoSupplier*
204Controller::CreateVideoSupplier()
205{
206	return fVideoSupplier;
207}
208
209
210AudioSupplier*
211Controller::CreateAudioSupplier()
212{
213	return fAudioSupplier;
214}
215
216
217// #pragma mark -
218
219
220status_t
221Controller::SetToAsync(const PlaylistItemRef& item)
222{
223	PlaylistItemRef additionalReference(item);
224
225	BMessage message(MSG_SET_TO);
226	status_t ret = message.AddPointer("item", item.Get());
227	if (ret != B_OK)
228		return ret;
229
230	ret = PostMessage(&message);
231	if (ret != B_OK)
232		return ret;
233
234	// The additional reference is now passed along with the message...
235	additionalReference.Detach();
236
237	return B_OK;
238}
239
240
241status_t
242Controller::SetTo(const PlaylistItemRef& item)
243{
244	BAutolock _(this);
245
246	if (fItem == item) {
247		if (InitCheck() == B_OK) {
248			if (fAutoplay) {
249				SetPosition(0.0);
250				StartPlaying(true);
251			}
252		}
253		return B_OK;
254	}
255
256	fAudioSupplier->SetSupplier(NULL, fVideoFrameRate);
257	fVideoSupplier->SetSupplier(NULL);
258
259	if (fItem != NULL)
260		TrackSupplierReleaser oldSupplierReleaser(fItem);
261
262	fItem = item;
263
264	// Do not delete the supplier chain until after we called
265	// NodeManager::Init() to setup a new media node chain
266	ObjectDeleter<VideoTrackSupplier> videoSupplierDeleter(
267		fVideoTrackSupplier);
268	ObjectDeleter<AudioTrackSupplier> audioSupplierDeleter(
269		fAudioTrackSupplier);
270
271	fVideoTrackSupplier = NULL;
272	fAudioTrackSupplier = NULL;
273	fSubTitles = NULL;
274	fSubTitlesIndex = -1;
275
276	fCurrentFrame = 0;
277	fDuration = 0;
278	fVideoFrameRate = 25.0;
279
280	fPendingSeekRequests = 0;
281	fSeekFrame = -1;
282	fRequestedSeekFrame = -1;
283
284	if (!fItem.IsSet())
285		return B_BAD_VALUE;
286
287	TrackSupplier* trackSupplier = fItem->GetTrackSupplier();
288	if (trackSupplier == NULL) {
289		_NotifyFileChanged(item.Get(), B_NO_MEMORY);
290		return B_NO_MEMORY;
291	}
292	TrackSupplierReleaser trackSupplierReleaser(fItem);
293
294	status_t err = trackSupplier->InitCheck();
295	if (err != B_OK) {
296		printf("Controller::SetTo: InitCheck failed\n");
297		_NotifyFileChanged(item.Get(), err);
298		return err;
299	}
300
301	if (trackSupplier->CountAudioTracks() == 0
302		&& trackSupplier->CountVideoTracks() == 0) {
303		_NotifyFileChanged(item.Get(), B_MEDIA_NO_HANDLER);
304		return B_MEDIA_NO_HANDLER;
305	}
306
307	SelectAudioTrack(0);
308	SelectVideoTrack(0);
309
310	if (fAudioTrackSupplier == NULL && fVideoTrackSupplier == NULL) {
311		printf("Controller::SetTo: no audio or video tracks found or "
312			"no decoders\n");
313		_NotifyFileChanged(item.Get(), B_MEDIA_NO_HANDLER);
314		return B_MEDIA_NO_HANDLER;
315	}
316
317	trackSupplierReleaser.Detach();
318
319	// prevent blocking the creation of new overlay buffers
320	if (fVideoView)
321		fVideoView->DisableOverlay();
322
323	// get video properties (if there is video at all)
324	bool useOverlays = fVideoView ? fVideoView->UseOverlays() : true;
325
326	int width;
327	int height;
328	GetSize(&width, &height);
329	color_space preferredVideoFormat = B_NO_COLOR_SPACE;
330	if (fVideoTrackSupplier != NULL) {
331		const media_format& format = fVideoTrackSupplier->Format();
332		preferredVideoFormat = format.u.raw_video.display.format;
333	}
334
335	uint32 enabledNodes;
336	if (!fVideoTrackSupplier)
337		enabledNodes = AUDIO_ONLY;
338	else if (!fAudioTrackSupplier)
339		enabledNodes = VIDEO_ONLY;
340	else
341		enabledNodes = AUDIO_AND_VIDEO;
342
343	float audioFrameRate = 44100.0f;
344	uint32 audioChannels = 2;
345	if (fAudioTrackSupplier != NULL) {
346		const media_format& audioTrackFormat = fAudioTrackSupplier->Format();
347		audioFrameRate = audioTrackFormat.u.raw_audio.frame_rate;
348		audioChannels = audioTrackFormat.u.raw_audio.channel_count;
349	}
350
351	if (InitCheck() != B_OK) {
352		Init(BRect(0, 0, width - 1, height - 1), fVideoFrameRate,
353			preferredVideoFormat, audioFrameRate, audioChannels, LOOPING_ALL,
354			false, 1.0, enabledNodes, useOverlays);
355	} else {
356		FormatChanged(BRect(0, 0, width - 1, height - 1), fVideoFrameRate,
357			preferredVideoFormat, audioFrameRate, audioChannels, enabledNodes,
358			useOverlays);
359	}
360
361	_NotifyFileChanged(item.Get(), B_OK);
362
363	if (fAutoplay)
364		StartPlaying(true);
365
366	return B_OK;
367}
368
369
370void
371Controller::PlayerActivated(bool active)
372{
373	if (LockWithTimeout(5000) != B_OK)
374		return;
375
376	if (active && gMainApp->PlayerCount() > 1) {
377		if (fActiveVolume != fVolume)
378			SetVolume(fActiveVolume);
379	} else {
380		fActiveVolume = fVolume;
381		if (gMainApp->PlayerCount() > 1)
382			switch (fBackgroundMovieVolumeMode) {
383				case mpSettings::BG_MOVIES_MUTED:
384					SetVolume(0.0);
385					break;
386				case mpSettings::BG_MOVIES_HALF_VLUME:
387					SetVolume(fVolume * 0.25);
388					break;
389				case mpSettings::BG_MOVIES_FULL_VOLUME:
390				default:
391					break;
392			}
393	}
394
395	Unlock();
396}
397
398
399void
400Controller::GetSize(int *width, int *height, int* _widthAspect,
401	int* _heightAspect)
402{
403	BAutolock _(this);
404
405	if (fVideoTrackSupplier) {
406		media_format format = fVideoTrackSupplier->Format();
407		*height = format.u.raw_video.display.line_count;
408		*width = format.u.raw_video.display.line_width;
409		int widthAspect = 0;
410		int heightAspect = 0;
411		// Ignore format aspect when both values are 1. If they have been
412		// intentionally at 1:1 then no harm is done for quadratic videos,
413		// only if the video is indeed encoded anamorphotic, but supposed
414		// to be displayed quadratic... extremely unlikely.
415		if (format.u.raw_video.pixel_width_aspect
416			!= format.u.raw_video.pixel_height_aspect
417			&& format.u.raw_video.pixel_width_aspect != 1) {
418			widthAspect = format.u.raw_video.pixel_width_aspect;
419			heightAspect = format.u.raw_video.pixel_height_aspect;
420		}
421		if (_widthAspect != NULL)
422			*_widthAspect = widthAspect;
423		if (_heightAspect != NULL)
424			*_heightAspect = heightAspect;
425	} else {
426		*height = 0;
427		*width = 0;
428		if (_widthAspect != NULL)
429			*_widthAspect = 1;
430		if (_heightAspect != NULL)
431			*_heightAspect = 1;
432	}
433}
434
435
436int
437Controller::AudioTrackCount()
438{
439	BAutolock _(this);
440
441	if (fItem != NULL && fItem->HasTrackSupplier())
442		return fItem->GetTrackSupplier()->CountAudioTracks();
443	return 0;
444}
445
446
447int
448Controller::VideoTrackCount()
449{
450	BAutolock _(this);
451
452	if (fItem != NULL && fItem->HasTrackSupplier())
453		return fItem->GetTrackSupplier()->CountVideoTracks();
454	return 0;
455}
456
457
458int
459Controller::SubTitleTrackCount()
460{
461	BAutolock _(this);
462
463	if (fItem != NULL && fItem->HasTrackSupplier())
464		return fItem->GetTrackSupplier()->CountSubTitleTracks();
465	return 0;
466}
467
468
469status_t
470Controller::SelectAudioTrack(int n)
471{
472	BAutolock _(this);
473	if (fItem == NULL || !fItem->HasTrackSupplier())
474		return B_NO_INIT;
475
476	ObjectDeleter<AudioTrackSupplier> deleter(fAudioTrackSupplier);
477	fAudioTrackSupplier
478		= fItem->GetTrackSupplier()->CreateAudioTrackForIndex(n);
479	if (fAudioTrackSupplier == NULL)
480		return B_BAD_INDEX;
481
482	bigtime_t a = fAudioTrackSupplier->Duration();
483	bigtime_t v = fVideoTrackSupplier != NULL
484		? fVideoTrackSupplier->Duration() : 0;
485	fDuration = max_c(a, v);
486	DurationChanged();
487	// TODO: notify duration changed!
488
489	fAudioSupplier->SetSupplier(fAudioTrackSupplier, fVideoFrameRate);
490
491	_NotifyAudioTrackChanged(n);
492	return B_OK;
493}
494
495
496int
497Controller::CurrentAudioTrack()
498{
499	BAutolock _(this);
500
501	if (fAudioTrackSupplier == NULL)
502		return -1;
503
504	return fAudioTrackSupplier->TrackIndex();
505}
506
507
508int
509Controller::AudioTrackChannelCount()
510{
511	media_format format;
512	if (GetEncodedAudioFormat(&format) == B_OK)
513		return format.u.encoded_audio.output.channel_count;
514
515	return 2;
516}
517
518
519status_t
520Controller::SelectVideoTrack(int n)
521{
522	BAutolock _(this);
523
524	if (fItem == NULL || !fItem->HasTrackSupplier())
525		return B_NO_INIT;
526
527	ObjectDeleter<VideoTrackSupplier> deleter(fVideoTrackSupplier);
528	fVideoTrackSupplier
529		= fItem->GetTrackSupplier()->CreateVideoTrackForIndex(n);
530	if (fVideoTrackSupplier == NULL)
531		return B_BAD_INDEX;
532
533	bigtime_t a = fAudioTrackSupplier ? fAudioTrackSupplier->Duration() : 0;
534	bigtime_t v = fVideoTrackSupplier->Duration();
535	fDuration = max_c(a, v);
536	fVideoFrameRate = fVideoTrackSupplier->Format().u.raw_video.field_rate;
537	if (fVideoFrameRate <= 0.0) {
538		printf("Controller::SelectVideoTrack(%d) - invalid video frame rate: %.1f\n",
539			n, fVideoFrameRate);
540		fVideoFrameRate = 25.0;
541	}
542
543	DurationChanged();
544	// TODO: notify duration changed!
545
546	fVideoSupplier->SetSupplier(fVideoTrackSupplier);
547
548	_NotifyVideoTrackChanged(n);
549	return B_OK;
550}
551
552
553int
554Controller::CurrentVideoTrack()
555{
556	BAutolock _(this);
557
558	if (fVideoTrackSupplier == NULL)
559		return -1;
560
561	return fVideoTrackSupplier->TrackIndex();
562}
563
564
565status_t
566Controller::SelectSubTitleTrack(int n)
567{
568	BAutolock _(this);
569
570	if (fItem == NULL || !fItem->HasTrackSupplier())
571		return B_NO_INIT;
572
573	fSubTitlesIndex = n;
574	fSubTitles =
575		fItem->GetTrackSupplier()->SubTitleTrackForIndex(n);
576
577	const SubTitle* subTitle = NULL;
578	if (fSubTitles != NULL)
579		subTitle = fSubTitles->SubTitleAt(_TimePosition());
580	if (subTitle != NULL)
581		fVideoView->SetSubTitle(subTitle->text.String());
582	else
583		fVideoView->SetSubTitle(NULL);
584
585	_NotifySubTitleTrackChanged(n);
586	return B_OK;
587}
588
589
590int
591Controller::CurrentSubTitleTrack()
592{
593	BAutolock _(this);
594
595	if (fSubTitles == NULL)
596		return -1;
597
598	return fSubTitlesIndex;
599}
600
601
602const char*
603Controller::SubTitleTrackName(int n)
604{
605	BAutolock _(this);
606
607	if (fItem == NULL || !fItem->HasTrackSupplier())
608		return NULL;
609
610	const SubTitles* subTitles
611		= fItem->GetTrackSupplier()->SubTitleTrackForIndex(n);
612	if (subTitles == NULL)
613		return NULL;
614
615	return subTitles->Name();
616}
617
618
619// #pragma mark -
620
621
622void
623Controller::Stop()
624{
625	//printf("Controller::Stop\n");
626
627	BAutolock _(this);
628
629	StopPlaying();
630	SetPosition(0.0);
631
632	fAutoplay = fAutoplaySetting;
633}
634
635
636void
637Controller::Play()
638{
639	//printf("Controller::Play\n");
640
641	BAutolock _(this);
642
643	StartPlaying();
644	fAutoplay = true;
645}
646
647
648void
649Controller::Pause()
650{
651//	printf("Controller::Pause\n");
652
653	BAutolock _(this);
654
655	PausePlaying();
656
657	fAutoplay = fAutoplaySetting;
658}
659
660
661void
662Controller::TogglePlaying()
663{
664//	printf("Controller::TogglePlaying\n");
665
666	BAutolock _(this);
667
668	if (InitCheck() == B_OK) {
669		NodeManager::TogglePlaying();
670
671		fAutoplay = IsPlaying() || fAutoplaySetting;
672	}
673}
674
675
676uint32
677Controller::PlaybackState()
678{
679	BAutolock _(this);
680
681	return _PlaybackState(PlaybackManager::PlayMode());
682}
683
684
685bigtime_t
686Controller::TimeDuration()
687{
688	BAutolock _(this);
689
690	return fDuration;
691}
692
693
694bigtime_t
695Controller::TimePosition()
696{
697	BAutolock _(this);
698
699	return _TimePosition();
700}
701
702
703status_t
704Controller::SaveState(bool reset)
705{
706	if (!fItem.IsSet())
707		return B_OK;
708	if (reset)
709		fCurrentFrame = 0;
710	status_t status = fItem.Get()->SetLastVolume(fVolume);
711	if (status == B_OK)
712		status = fItem.Get()->SetLastFrame(fCurrentFrame);
713	else
714		fItem.Get()->SetLastFrame(fCurrentFrame);
715	return status;
716}
717
718
719void
720Controller::RestoreState()
721{
722	PlaylistItem *item =fItem.Get();
723	if (item == NULL)
724		return;
725
726	int lastFrame = item->LastFrame();
727	float lastVolume = item->LastVolume();
728
729	// Don't Pause()/Play() if we have nothing to do.
730	if (lastFrame <= 0 && lastVolume < 0)
731		return;
732
733	Pause();
734
735	if (lastFrame > 0) {
736		bool resume = fResume == mpSettings::RESUME_ALWAYS;
737		if (fResume == mpSettings::RESUME_ASK) {
738			BString label;
739			int32 time = (int32)((float)lastFrame * TimeDuration()
740					/ (1000000 * _FrameDuration()));
741			label.SetToFormat(B_TRANSLATE("Do you want to resume %s at %dm%ds?"),
742					item->Name().String(), time / 60, time % 60);
743			BAlert *alert = new BAlert(B_TRANSLATE("Resume?"), label,
744					B_TRANSLATE("Resume"), B_TRANSLATE("Reset"));
745			resume = alert->Go() == 0;
746		}
747
748		if (resume)
749			SetFramePosition(lastFrame);
750	}
751
752	if (lastVolume >= 0)
753		SetVolume(lastVolume);
754
755	Play();
756}
757
758
759void
760Controller::SetVolume(float value)
761{
762//	printf("Controller::SetVolume %.4f\n", value);
763	BAutolock _(this);
764
765	value = max_c(0.0, min_c(2.0, value));
766
767	if (fVolume != value) {
768		if (fMuted)
769			ToggleMute();
770
771		fVolume = value;
772		fAudioSupplier->SetVolume(fVolume);
773
774		_NotifyVolumeChanged(fVolume);
775	}
776}
777
778
779void
780Controller::VolumeUp()
781{
782	// TODO: linear <-> exponential
783	SetVolume(Volume() + 0.05);
784}
785
786
787void
788Controller::VolumeDown()
789{
790	// TODO: linear <-> exponential
791	SetVolume(Volume() - 0.05);
792}
793
794
795void
796Controller::ToggleMute()
797{
798	BAutolock _(this);
799
800	fMuted = !fMuted;
801
802	if (fMuted)
803		fAudioSupplier->SetVolume(0.0);
804	else
805		fAudioSupplier->SetVolume(fVolume);
806
807	_NotifyMutedChanged(fMuted);
808}
809
810
811float
812Controller::Volume()
813{
814	BAutolock _(this);
815
816	return fVolume;
817}
818
819
820int64
821Controller::SetPosition(float value)
822{
823	BAutolock _(this);
824
825	return SetFramePosition(_FrameDuration() * value);
826}
827
828
829int64
830Controller::SetFramePosition(int64 value)
831{
832	BAutolock _(this);
833
834	fPendingSeekRequests++;
835	fRequestedSeekFrame = max_c(0, min_c(_FrameDuration(), value));
836	fSeekFrame = fRequestedSeekFrame;
837
838	int64 currentFrame = CurrentFrame();
839
840	// Snap to a video keyframe, since that will be the fastest
841	// to display and seeking will feel more snappy. Note that we
842	// don't store this change in fSeekFrame, since we still want
843	// to report the originally requested seek frame in TimePosition()
844	// until we could reach that frame.
845	if (Duration() > 240 && fVideoTrackSupplier != NULL
846		&& abs(value - currentFrame) > 5) {
847		fVideoTrackSupplier->FindKeyFrameForFrame(&fSeekFrame);
848	}
849
850//printf("SetFramePosition(%lld) -> %lld (current: %lld, duration: %lld) "
851//"(video: %p)\n", value, fSeekFrame, currentFrame, _FrameDuration(),
852//fVideoTrackSupplier);
853	if (fSeekFrame != currentFrame) {
854		int64 seekFrame = fSeekFrame;
855		SetCurrentFrame(fSeekFrame);
856			// May trigger the notification and reset fSeekFrame,
857			// if next current frame == seek frame.
858		return seekFrame;
859	} else
860		NotifySeekHandled(fRequestedSeekFrame);
861	return currentFrame;
862}
863
864
865int64
866Controller::SetTimePosition(bigtime_t value)
867{
868	BAutolock _(this);
869
870	return SetPosition((float)value / TimeDuration());
871}
872
873
874// #pragma mark -
875
876
877bool
878Controller::HasFile()
879{
880	// you need to hold the data lock
881	return fItem != NULL && fItem->HasTrackSupplier();
882}
883
884
885status_t
886Controller::GetFileFormatInfo(media_file_format* fileFormat)
887{
888	// you need to hold the data lock
889	if (fItem == NULL || !fItem->HasTrackSupplier())
890		return B_NO_INIT;
891	return fItem->GetTrackSupplier()->GetFileFormatInfo(fileFormat);
892}
893
894
895status_t
896Controller::GetCopyright(BString* copyright)
897{
898	// you need to hold the data lock
899	if (fItem == NULL || !fItem->HasTrackSupplier())
900		return B_NO_INIT;
901	return fItem->GetTrackSupplier()->GetCopyright(copyright);
902}
903
904
905status_t
906Controller::GetLocation(BString* location)
907{
908	// you need to hold the data lock
909	if (!fItem.IsSet())
910		return B_NO_INIT;
911	*location = fItem->LocationURI();
912	return B_OK;
913}
914
915
916status_t
917Controller::GetName(BString* name)
918{
919	// you need to hold the data lock
920	if (!fItem.IsSet())
921		return B_NO_INIT;
922	*name = fItem->Name();
923	return B_OK;
924}
925
926
927status_t
928Controller::GetEncodedVideoFormat(media_format* format)
929{
930	// you need to hold the data lock
931	if (fVideoTrackSupplier)
932		return fVideoTrackSupplier->GetEncodedFormat(format);
933	return B_NO_INIT;
934}
935
936
937status_t
938Controller::GetVideoCodecInfo(media_codec_info* info)
939{
940	// you need to hold the data lock
941	if (fVideoTrackSupplier)
942		return fVideoTrackSupplier->GetCodecInfo(info);
943	return B_NO_INIT;
944}
945
946
947status_t
948Controller::GetEncodedAudioFormat(media_format* format)
949{
950	// you need to hold the data lock
951	if (fAudioTrackSupplier)
952		return fAudioTrackSupplier->GetEncodedFormat(format);
953	return B_NO_INIT;
954}
955
956
957status_t
958Controller::GetAudioCodecInfo(media_codec_info* info)
959{
960	// you need to hold the data lock
961	if (fAudioTrackSupplier)
962		return fAudioTrackSupplier->GetCodecInfo(info);
963	return B_NO_INIT;
964}
965
966
967status_t
968Controller::GetMetaData(BMessage* metaData)
969{
970	// you need to hold the data lock
971	if (fItem == NULL || !fItem->HasTrackSupplier())
972		return B_NO_INIT;
973	return fItem->GetTrackSupplier()->GetMetaData(metaData);
974}
975
976
977status_t
978Controller::GetVideoMetaData(int32 index, BMessage* metaData)
979{
980	// you need to hold the data lock
981	if (fItem == NULL || !fItem->HasTrackSupplier())
982		return B_NO_INIT;
983	return fItem->GetTrackSupplier()->GetVideoMetaData(index, metaData);
984}
985
986
987status_t
988Controller::GetAudioMetaData(int32 index, BMessage* metaData)
989{
990	// you need to hold the data lock
991	if (fItem == NULL || !fItem->HasTrackSupplier())
992		return B_NO_INIT;
993	return fItem->GetTrackSupplier()->GetAudioMetaData(index, metaData);
994}
995
996
997// #pragma mark -
998
999
1000void
1001Controller::SetVideoView(VideoView *view)
1002{
1003	BAutolock _(this);
1004
1005	fVideoView = view;
1006}
1007
1008
1009bool
1010Controller::IsOverlayActive()
1011{
1012	if (fVideoView)
1013		return fVideoView->IsOverlayActive();
1014
1015	return false;
1016}
1017
1018
1019// #pragma mark -
1020
1021
1022bool
1023Controller::AddListener(Listener* listener)
1024{
1025	BAutolock _(this);
1026
1027	if (listener && !fListeners.HasItem(listener))
1028		return fListeners.AddItem(listener);
1029	return false;
1030}
1031
1032
1033void
1034Controller::RemoveListener(Listener* listener)
1035{
1036	BAutolock _(this);
1037
1038	fListeners.RemoveItem(listener);
1039}
1040
1041
1042// #pragma mark - Private
1043
1044
1045void
1046Controller::_AdoptGlobalSettings()
1047{
1048	mpSettings settings;
1049	Settings::Default()->Get(settings);
1050
1051	fAutoplaySetting = settings.autostart;
1052	// not yet used:
1053	fLoopMovies = settings.loopMovie;
1054	fLoopSounds = settings.loopSound;
1055	fBackgroundMovieVolumeMode = settings.backgroundMovieVolumeMode;
1056	fResume = settings.resume;
1057}
1058
1059
1060uint32
1061Controller::_PlaybackState(int32 playingMode) const
1062{
1063	uint32 state = 0;
1064	switch (playingMode) {
1065		case MODE_PLAYING_PAUSED_FORWARD:
1066		case MODE_PLAYING_PAUSED_BACKWARD:
1067			state = PLAYBACK_STATE_PAUSED;
1068			break;
1069		case MODE_PLAYING_FORWARD:
1070		case MODE_PLAYING_BACKWARD:
1071			state = PLAYBACK_STATE_PLAYING;
1072			break;
1073
1074		default:
1075			state = PLAYBACK_STATE_STOPPED;
1076			break;
1077	}
1078	return state;
1079}
1080
1081
1082bigtime_t
1083Controller::_TimePosition() const
1084{
1085	if (fDuration == 0)
1086		return 0;
1087
1088	// Check if we are still waiting to reach the seekframe,
1089	// pass the last pending seek frame back to the caller, so
1090	// that the view of the current frame/time from the outside
1091	// does not depend on the internal latency to reach requested
1092	// frames asynchronously.
1093	int64 frame;
1094	if (fPendingSeekRequests > 0)
1095		frame = fRequestedSeekFrame;
1096	else
1097		frame = fCurrentFrame;
1098
1099	return frame * fDuration / _FrameDuration();
1100}
1101
1102
1103int64
1104Controller::_FrameDuration() const
1105{
1106	// This should really be total frames (video frames at that)
1107	// TODO: It is not so nice that the MediaPlayer still measures
1108	// in video frames if only playing audio. Here for example, it will
1109	// return a duration of 0 if the audio clip happens to be shorter than
1110	// one video frame at 25 fps.
1111	return (int64)((double)fDuration * fVideoFrameRate / 1000000.0);
1112}
1113
1114
1115// #pragma mark - Notifications
1116
1117
1118void
1119Controller::_NotifyFileChanged(PlaylistItem* item, status_t result) const
1120{
1121	BList listeners(fListeners);
1122	int32 count = listeners.CountItems();
1123	for (int32 i = 0; i < count; i++) {
1124		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1125		listener->FileChanged(item, result);
1126	}
1127}
1128
1129
1130void
1131Controller::_NotifyFileFinished() const
1132{
1133	BList listeners(fListeners);
1134	int32 count = listeners.CountItems();
1135	for (int32 i = 0; i < count; i++) {
1136		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1137		listener->FileFinished();
1138	}
1139}
1140
1141
1142void
1143Controller::_NotifyVideoTrackChanged(int32 index) const
1144{
1145	BList listeners(fListeners);
1146	int32 count = listeners.CountItems();
1147	for (int32 i = 0; i < count; i++) {
1148		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1149		listener->VideoTrackChanged(index);
1150	}
1151}
1152
1153
1154void
1155Controller::_NotifyAudioTrackChanged(int32 index) const
1156{
1157	BList listeners(fListeners);
1158	int32 count = listeners.CountItems();
1159	for (int32 i = 0; i < count; i++) {
1160		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1161		listener->AudioTrackChanged(index);
1162	}
1163}
1164
1165
1166void
1167Controller::_NotifySubTitleTrackChanged(int32 index) const
1168{
1169	BList listeners(fListeners);
1170	int32 count = listeners.CountItems();
1171	for (int32 i = 0; i < count; i++) {
1172		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1173		listener->SubTitleTrackChanged(index);
1174	}
1175}
1176
1177
1178void
1179Controller::_NotifyVideoStatsChanged() const
1180{
1181	BList listeners(fListeners);
1182	int32 count = listeners.CountItems();
1183	for (int32 i = 0; i < count; i++) {
1184		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1185		listener->VideoStatsChanged();
1186	}
1187}
1188
1189
1190void
1191Controller::_NotifyAudioStatsChanged() const
1192{
1193	BList listeners(fListeners);
1194	int32 count = listeners.CountItems();
1195	for (int32 i = 0; i < count; i++) {
1196		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1197		listener->AudioStatsChanged();
1198	}
1199}
1200
1201
1202void
1203Controller::_NotifyPlaybackStateChanged(uint32 state) const
1204{
1205	BList listeners(fListeners);
1206	int32 count = listeners.CountItems();
1207	for (int32 i = 0; i < count; i++) {
1208		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1209		listener->PlaybackStateChanged(state);
1210	}
1211}
1212
1213
1214void
1215Controller::_NotifyPositionChanged(float position) const
1216{
1217	BList listeners(fListeners);
1218	int32 count = listeners.CountItems();
1219	for (int32 i = 0; i < count; i++) {
1220		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1221		listener->PositionChanged(position);
1222	}
1223}
1224
1225
1226void
1227Controller::_NotifySeekHandled(int64 seekFrame) const
1228{
1229	BList listeners(fListeners);
1230	int32 count = listeners.CountItems();
1231	for (int32 i = 0; i < count; i++) {
1232		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1233		listener->SeekHandled(seekFrame);
1234	}
1235}
1236
1237
1238void
1239Controller::_NotifyVolumeChanged(float volume) const
1240{
1241	BList listeners(fListeners);
1242	int32 count = listeners.CountItems();
1243	for (int32 i = 0; i < count; i++) {
1244		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1245		listener->VolumeChanged(volume);
1246	}
1247}
1248
1249
1250void
1251Controller::_NotifyMutedChanged(bool muted) const
1252{
1253	BList listeners(fListeners);
1254	int32 count = listeners.CountItems();
1255	for (int32 i = 0; i < count; i++) {
1256		Listener* listener = (Listener*)listeners.ItemAtFast(i);
1257		listener->MutedChanged(muted);
1258	}
1259}
1260
1261
1262void
1263Controller::NotifyPlayModeChanged(int32 mode) const
1264{
1265	uint32 state = _PlaybackState(mode);
1266	if (fVideoView)
1267		fVideoView->SetPlaying(state == PLAYBACK_STATE_PLAYING);
1268	_NotifyPlaybackStateChanged(state);
1269}
1270
1271
1272void
1273Controller::NotifyLoopModeChanged(int32 mode) const
1274{
1275}
1276
1277
1278void
1279Controller::NotifyLoopingEnabledChanged(bool enabled) const
1280{
1281}
1282
1283
1284void
1285Controller::NotifyVideoBoundsChanged(BRect bounds) const
1286{
1287}
1288
1289
1290void
1291Controller::NotifyFPSChanged(float fps) const
1292{
1293}
1294
1295
1296void
1297Controller::NotifyCurrentFrameChanged(int64 frame) const
1298{
1299	fCurrentFrame = frame;
1300	bigtime_t timePosition = _TimePosition();
1301	_NotifyPositionChanged((float)timePosition / fDuration);
1302
1303	if (fSubTitles != NULL) {
1304		const SubTitle* subTitle = fSubTitles->SubTitleAt(timePosition);
1305		if (subTitle != NULL)
1306			fVideoView->SetSubTitle(subTitle->text.String());
1307		else
1308			fVideoView->SetSubTitle(NULL);
1309	}
1310}
1311
1312
1313void
1314Controller::NotifySpeedChanged(float speed) const
1315{
1316}
1317
1318
1319void
1320Controller::NotifyFrameDropped() const
1321{
1322//	printf("Controller::NotifyFrameDropped()\n");
1323}
1324
1325
1326void
1327Controller::NotifyStopFrameReached() const
1328{
1329	// Currently, this means we reached the end of the current
1330	// file and should play the next file
1331	_NotifyFileFinished();
1332}
1333
1334
1335void
1336Controller::NotifySeekHandled(int64 seekedFrame) const
1337{
1338	if (fPendingSeekRequests == 0)
1339		return;
1340
1341	fPendingSeekRequests--;
1342	if (fPendingSeekRequests == 0) {
1343		fSeekFrame = -1;
1344		fRequestedSeekFrame = -1;
1345	}
1346
1347	_NotifySeekHandled(seekedFrame);
1348}
1349
1350