xref: /haiku/src/apps/mediaplayer/Controller.cpp (revision 1acbe440b8dd798953bec31d18ee589aa3f71b73)
1 /*
2  * Controller.cpp - Media Player for the Haiku Operating System
3  *
4  * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * version 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  *
19  */
20 #include <stdio.h>
21 #include <string.h>
22 #include <Debug.h>
23 #include <Entry.h>
24 #include <Window.h>
25 #include <Bitmap.h>
26 #include <Autolock.h>
27 #include <MediaFile.h>
28 #include <MediaTrack.h>
29 
30 #include "Controller.h"
31 #include "ControllerView.h"
32 #include "VideoView.h"
33 #include "SoundOutput.h"
34 
35 
36 #define AUDIO_PLAY_PRIORITY		110
37 #define VIDEO_PLAY_PRIORITY		20
38 #define AUDIO_DECODE_PRIORITY	13
39 #define VIDEO_DECODE_PRIORITY	13
40 
41 void
42 HandleError(const char *text, status_t err)
43 {
44 	if (err != B_OK) {
45 		printf("%s. error 0x%08x (%s)\n",text, (int)err, strerror(err));
46 		fflush(NULL);
47 		exit(1);
48 	}
49 }
50 
51 
52 Controller::Controller()
53  :	fVideoView(NULL)
54  ,	fControllerView(NULL)
55  ,	fName()
56  ,	fPaused(false)
57  ,	fStopped(true)
58  ,	fMediaFile(0)
59  ,	fAudioTrack(0)
60  ,	fVideoTrack(0)
61  ,	fAudioTrackLock("audio track lock")
62  ,	fVideoTrackLock("video track lock")
63  ,	fAudioTrackList(new BList)
64  ,	fVideoTrackList(new BList)
65  ,	fPosition(0)
66  ,	fAudioDecodeSem(-1)
67  ,	fVideoDecodeSem(-1)
68  ,	fAudioPlaySem(-1)
69  ,	fVideoPlaySem(-1)
70  ,	fAudioDecodeThread(-1)
71  ,	fVideoDecodeThread(-1)
72  ,	fAudioPlayThread(-1)
73  ,	fVideoPlayThread(-1)
74  ,	fSoundOutput(NULL)
75  ,	fSeekAudio(false)
76  ,	fSeekVideo(false)
77  ,	fSeekPosition(0)
78  ,	fDuration(0)
79  ,	fAudioBufferCount(MAX_AUDIO_BUFFERS)
80  ,	fAudioBufferReadIndex(0)
81  ,	fAudioBufferWriteIndex(0)
82  ,	fVideoBufferCount(MAX_VIDEO_BUFFERS)
83  ,	fVideoBufferReadIndex(0)
84  ,	fVideoBufferWriteIndex(0)
85  ,	fTimeSourceLock("time source lock")
86  ,	fTimeSourceSysTime(0)
87  ,	fTimeSourcePerfTime(0)
88  ,	fAutoplay(true)
89  ,	fCurrentBitmap(NULL)
90 {
91 	for (int i = 0; i < MAX_AUDIO_BUFFERS; i++) {
92 		fAudioBuffer[i].bitmap = NULL;
93 		fAudioBuffer[i].buffer = NULL;
94 		fAudioBuffer[i].sizeMax = 0;
95 	}
96 	for (int i = 0; i < MAX_VIDEO_BUFFERS; i++) {
97 		fVideoBuffer[i].bitmap = NULL;
98 		fVideoBuffer[i].buffer = NULL;
99 		fVideoBuffer[i].sizeMax = 0;
100 	}
101 
102 	fStopped = fAutoplay ? false : true;
103 
104 }
105 
106 
107 Controller::~Controller()
108 {
109 	StopThreads();
110 
111 	if (fMediaFile)
112 		fMediaFile->ReleaseAllTracks();
113 	delete fMediaFile;
114 	delete fAudioTrackList;
115 	delete fVideoTrackList;
116 }
117 
118 
119 void
120 Controller::SetVideoView(VideoView *view)
121 {
122 	fVideoView = view;
123 	fVideoView->SetController(this);
124 }
125 
126 
127 void
128 Controller::SetControllerView(ControllerView *view)
129 {
130 	fControllerView = view;
131 }
132 
133 
134 status_t
135 Controller::SetTo(const entry_ref &ref)
136 {
137 	StopThreads();
138 
139 	UpdatePosition(0);
140 
141 	BAutolock al(fAudioTrackLock);
142 	BAutolock vl(fVideoTrackLock);
143 
144 	fAudioTrackList->MakeEmpty();
145 	fVideoTrackList->MakeEmpty();
146 	if (fMediaFile)
147 		fMediaFile->ReleaseAllTracks();
148 	delete fMediaFile;
149 	fAudioTrack = 0;
150 	fVideoTrack = 0;
151 	fMediaFile = 0;
152 	fPosition = 0;
153 	fSeekAudio = false;
154 	fSeekVideo = false;
155 	fPaused = false;
156 	fStopped = fAutoplay ? false : true;
157 	fDuration = 0;
158 	fName = ref.name;
159 
160 	status_t err;
161 
162 	BMediaFile *mf = new BMediaFile(&ref);
163 	err = mf->InitCheck();
164 	if (err != B_OK) {
165 		printf("Controller::SetTo: initcheck failed\n");
166 		delete mf;
167 		return err;
168 	}
169 
170 	int trackcount = mf->CountTracks();
171 	if (trackcount <= 0) {
172 		printf("Controller::SetTo: trackcount %d\n", trackcount);
173 		delete mf;
174 		return B_MEDIA_NO_HANDLER;
175 	}
176 
177 	for (int i = 0; i < trackcount; i++) {
178 		BMediaTrack *t = mf->TrackAt(i);
179 		media_format f;
180 		err = t->EncodedFormat(&f);
181 		if (err != B_OK) {
182 			printf("Controller::SetTo: EncodedFormat failed for track index %d, error 0x%08lx (%s)\n",
183 				i, err, strerror(err));
184 			mf->ReleaseTrack(t);
185 			continue;
186 		}
187 		if (f.IsAudio()) {
188 			fAudioTrackList->AddItem(t);
189 		} else if (f.IsVideo()) {
190 			fVideoTrackList->AddItem(t);
191 		} else {
192 			printf("Controller::SetTo: track index %d has unknown type\n", i);
193 			mf->ReleaseTrack(t);
194 		}
195 	}
196 
197 	if (AudioTrackCount() == 0 && VideoTrackCount() == 0) {
198 		printf("Controller::SetTo: no audio or video tracks found\n");
199 		delete mf;
200 		return B_MEDIA_NO_HANDLER;
201 	}
202 
203 	printf("Controller::SetTo: %d audio track, %d video track\n", AudioTrackCount(), VideoTrackCount());
204 
205 	fMediaFile = mf;
206 
207 	SelectAudioTrack(0);
208 	SelectVideoTrack(0);
209 
210 	StartThreads();
211 
212 	return B_OK;
213 }
214 
215 
216 void
217 Controller::GetSize(int *width, int *height)
218 {
219 /*
220 	media_format fmt;
221 			buffer->mediaFormat.type = B_MEDIA_RAW_VIDEO;
222 			buffer->mediaFormat.u.raw_video = media_raw_video_format::wildcard;
223 			buffer->mediaFormat.u.raw_video.display.format = IsOverlayActive() ? B_YCbCr422 : B_RGB32;
224 	if (fVideoTrack || B_OK != fVideoTrack->DecodedFormat(&fmt)) {
225 */
226 		*height = 480;
227 		*width = 640;
228 /*
229 	} else {
230 		*height = fmt.u.raw_video.display.line_count;
231 		*width = fmt.u.raw_video.display.line_width;
232 	}
233 */
234 }
235 
236 
237 int
238 Controller::VideoTrackCount()
239 {
240 	return fVideoTrackList->CountItems();
241 }
242 
243 
244 int
245 Controller::AudioTrackCount()
246 {
247 	return fAudioTrackList->CountItems();
248 }
249 
250 
251 status_t
252 Controller::SelectAudioTrack(int n)
253 {
254 	BAutolock al(fAudioTrackLock);
255 
256 	BMediaTrack *t = (BMediaTrack *)fAudioTrackList->ItemAt(n);
257 	if (!t)
258 		return B_ERROR;
259 	fAudioTrack = t;
260 
261 	bigtime_t a = fAudioTrack ? fAudioTrack->Duration() : 0;
262 	bigtime_t v = fVideoTrack ? fVideoTrack->Duration() : 0;
263 	fDuration = max_c(a, v);
264 
265 	return B_OK;
266 }
267 
268 
269 status_t
270 Controller::SelectVideoTrack(int n)
271 {
272 	BAutolock vl(fVideoTrackLock);
273 
274 	BMediaTrack *t = (BMediaTrack *)fVideoTrackList->ItemAt(n);
275 	if (!t)
276 		return B_ERROR;
277 	fVideoTrack = t;
278 
279 	bigtime_t a = fAudioTrack ? fAudioTrack->Duration() : 0;
280 	bigtime_t v = fVideoTrack ? fVideoTrack->Duration() : 0;
281 	fDuration = max_c(a, v);
282 
283 	return B_OK;
284 }
285 
286 
287 bigtime_t
288 Controller::Duration()
289 {
290 	return fDuration;
291 }
292 
293 
294 bigtime_t
295 Controller::Position()
296 {
297 	return fPosition;
298 }
299 
300 
301 status_t
302 Controller::Seek(bigtime_t pos)
303 {
304 	return B_OK;
305 }
306 
307 
308 void
309 Controller::VolumeUp()
310 {
311 }
312 
313 
314 void
315 Controller::VolumeDown()
316 {
317 }
318 
319 
320 void
321 Controller::SetVolume(float value)
322 {
323 	printf("Controller::SetVolume %.4f\n", value);
324 	if (fSoundOutput) // hack...
325 		fSoundOutput->SetVolume(value);
326 }
327 
328 
329 void
330 Controller::SetPosition(float value)
331 {
332 	printf("Controller::SetPosition %.4f\n", value);
333 	fSeekPosition = bigtime_t(value * Duration());
334 	fSeekAudio = true;
335 	fSeekVideo = true;
336 }
337 
338 
339 void
340 Controller::UpdateVolume(float value)
341 {
342 	fControllerView->SetVolume(value);
343 }
344 
345 
346 void
347 Controller::UpdatePosition(float value)
348 {
349 	fControllerView->SetPosition(value);
350 }
351 
352 
353 bool
354 Controller::IsOverlayActive()
355 {
356 //	return true;
357 	return false;
358 }
359 
360 
361 void
362 Controller::LockBitmap()
363 {
364 }
365 
366 
367 void
368 Controller::UnlockBitmap()
369 {
370 }
371 
372 
373 BBitmap *
374 Controller::Bitmap()
375 {
376 	return fCurrentBitmap;
377 }
378 
379 
380 void
381 Controller::Stop()
382 {
383 	printf("Controller::Stop\n");
384 
385 	if (fStopped)
386 		return;
387 
388 	fPaused = false;
389 	fStopped = true;
390 
391 	// böse...
392 	snooze(30000);
393 	fSeekAudio = true;
394 	fSeekVideo = true;
395 	fSeekPosition = 0;
396 	snooze(30000);
397 	UpdatePosition(0);
398 }
399 
400 
401 void
402 Controller::Play()
403 {
404 	printf("Controller::Play\n");
405 
406 	if (!fPaused && !fStopped)
407 		return;
408 
409 	fStopped =
410 	fPaused = false;
411 }
412 
413 
414 void
415 Controller::Pause()
416 {
417 	printf("Controller::Pause\n");
418 
419 	if (fStopped || fPaused)
420 		return;
421 
422 	fPaused = true;
423 }
424 
425 
426 bool
427 Controller::IsPaused()
428 {
429 	return fPaused;
430 }
431 
432 
433 bool
434 Controller::IsStopped()
435 {
436 	return fStopped;
437 }
438 
439 
440 void
441 Controller::AudioDecodeThread()
442 {
443 	BMediaTrack *lastTrack = 0;
444 	size_t bufferSize = 0;
445 	size_t frameSize = 0;
446 	bool decodeError = false;
447 	status_t status;
448 
449 printf("audio decode start\n");
450 	if (fAudioTrack == 0) {
451 		return;
452 	}
453 
454 
455 	while (acquire_sem(fAudioDecodeSem) == B_OK) {
456 //printf("audio decoding..\n");
457 		buffer_info *buffer = &fAudioBuffer[fAudioBufferWriteIndex];
458 		fAudioBufferWriteIndex = (fAudioBufferWriteIndex + 1) % fAudioBufferCount;
459 		BAutolock lock(fAudioTrackLock);
460 		if (lastTrack != fAudioTrack) {
461 //printf("audio track changed\n");
462 			lastTrack = fAudioTrack;
463 			buffer->formatChanged = true;
464 			buffer->mediaFormat.type = B_MEDIA_RAW_AUDIO;
465 			buffer->mediaFormat.u.raw_audio = media_multi_audio_format::wildcard;
466 			#ifdef __HAIKU__
467 			  buffer->mediaFormat.u.raw_audio.format = media_multi_audio_format::B_AUDIO_FLOAT;
468 			#endif
469 			status = fAudioTrack->DecodedFormat(&buffer->mediaFormat);
470 			if (status != B_OK) {
471 				printf("audio decoded format status %08lx %s\n", status, strerror(status));
472 				return;
473 			}
474 			bufferSize = buffer->mediaFormat.u.raw_audio.buffer_size;
475 			frameSize = (buffer->mediaFormat.u.raw_audio.format & 0xf) * buffer->mediaFormat.u.raw_audio.channel_count;
476 		} else {
477 			buffer->formatChanged = false;
478 		}
479 		if (buffer->sizeMax < bufferSize) {
480 			delete buffer->buffer;
481 			buffer->buffer = new char [bufferSize];
482 			buffer->sizeMax = bufferSize;;
483 		}
484 
485 		if (fSeekAudio) {
486 			bigtime_t pos = fSeekPosition;
487 			fAudioTrack->SeekToTime(&pos);
488 			fSeekAudio = false;
489 			decodeError = false;
490 		}
491 
492 		int64 frames;
493 		media_header mh;
494 		if (!decodeError) {
495 			decodeError = B_OK != fAudioTrack->ReadFrames(buffer->buffer, &frames, &mh);
496 		}
497 		if (!decodeError) {
498 			buffer->sizeUsed = frames * frameSize;
499 			buffer->startTime = mh.start_time;
500 		} else {
501 			buffer->sizeUsed = 0;
502 			buffer->startTime = 0;
503 		}
504 
505 		release_sem(fAudioPlaySem);
506 	}
507 }
508 
509 
510 void
511 Controller::AudioPlayThread()
512 {
513 	SoundOutput *output = NULL;
514 	bigtime_t bufferDuration = 0;
515 	status_t status;
516 
517 
518 printf("audio play start\n");
519 	if (fAudioTrack == 0) {
520 		fTimeSourceSysTime = 0;
521 		fTimeSourcePerfTime = 0;
522 		return;
523 	}
524 
525 	while (acquire_sem(fAudioPlaySem) == B_OK) {
526 //printf("audio playing..\n");
527 		buffer_info *buffer = &fAudioBuffer[fAudioBufferReadIndex];
528 		fAudioBufferReadIndex = (fAudioBufferReadIndex + 1) % fAudioBufferCount;
529 		wait:
530 		if (fPaused || fStopped) {
531 //printf("waiting..\n");
532 			status = acquire_sem_etc(fThreadWaitSem, 1, B_RELATIVE_TIMEOUT, 50000);
533 			if (status != B_TIMED_OUT)
534 				break;
535 			goto wait;
536 		}
537 		if (buffer->formatChanged) {
538 //printf("format changed..\n");
539 			delete output;
540 			output = new SoundOutput(fName.String(), buffer->mediaFormat.u.raw_audio);
541 			bufferDuration = bigtime_t(1000000.0 * (buffer->mediaFormat.u.raw_audio.buffer_size / (buffer->mediaFormat.u.raw_audio.channel_count * (buffer->mediaFormat.u.raw_audio.format & 0xf))) / buffer->mediaFormat.u.raw_audio.frame_rate);
542 			printf("audio format changed, new buffer duration %Ld\n", bufferDuration);
543 			fSoundOutput = output; // hack...
544 		}
545 		if (output) {
546 			if (buffer->sizeUsed)
547 				output->Play(buffer->buffer, buffer->sizeUsed);
548 			BAutolock lock(fTimeSourceLock);
549 			fTimeSourceSysTime = system_time() + output->Latency() - bufferDuration;
550 			fTimeSourcePerfTime = buffer->startTime;
551 //			printf("timesource: sys: %Ld perf: %Ld\n", fTimeSourceSysTime, fTimeSourcePerfTime);
552 			UpdatePosition(buffer->startTime / (float)Duration());
553 		} else {
554 			debugger("kein SoundOutput");
555 		}
556 		release_sem(fAudioDecodeSem);
557 	}
558 	delete output;
559 	fSoundOutput = NULL; // hack...
560 }
561 
562 
563 void
564 Controller::VideoDecodeThread()
565 {
566 	BMediaTrack *lastTrack = 0;
567 	size_t bufferSize = 0;
568 	size_t bytePerRow = 0;
569 	int lineWidth = 0;
570 	int lineCount = 0;
571 	bool decodeError = false;
572 	status_t status;
573 
574 printf("video decode start\n");
575 	if (fVideoTrack == 0) {
576 		return;
577 	}
578 
579 	while (acquire_sem(fVideoDecodeSem) == B_OK) {
580 		buffer_info *buffer = &fVideoBuffer[fVideoBufferWriteIndex];
581 		fVideoBufferWriteIndex = (fVideoBufferWriteIndex + 1) % fVideoBufferCount;
582 		BAutolock lock(fVideoTrackLock);
583 		if (lastTrack != fVideoTrack) {
584 //printf("Video track changed\n");
585 			lastTrack = fVideoTrack;
586 			buffer->formatChanged = true;
587 			buffer->mediaFormat.type = B_MEDIA_RAW_VIDEO;
588 			buffer->mediaFormat.u.raw_video = media_raw_video_format::wildcard;
589 			buffer->mediaFormat.u.raw_video.display.format = IsOverlayActive() ? B_YCbCr422 : B_RGB32;
590 			status = fVideoTrack->DecodedFormat(&buffer->mediaFormat);
591 			if (status != B_OK) {
592 				printf("video decoded format status %08lx %s\n", status, strerror(status));
593 				return;
594 			}
595 			bytePerRow = buffer->mediaFormat.u.raw_video.display.bytes_per_row;
596 			lineWidth = buffer->mediaFormat.u.raw_video.display.line_width;
597 			lineCount = buffer->mediaFormat.u.raw_video.display.line_count;
598 			bufferSize = lineCount * bytePerRow;
599 		} else {
600 			buffer->formatChanged = false;
601 		}
602 		if (buffer->sizeMax != bufferSize) {
603 			BRect r(0, 0, lineWidth - 1, lineCount - 1);
604 			delete buffer->bitmap;
605 			printf("allocating bitmap %d %d %ld\n", lineWidth, lineCount, bytePerRow);
606 			if (IsOverlayActive())
607 				buffer->bitmap = new BBitmap(r, B_BITMAP_WILL_OVERLAY | (fVideoBufferWriteIndex == 1) ? B_BITMAP_RESERVE_OVERLAY_CHANNEL : 0, IsOverlayActive() ? B_YCbCr422 : B_RGB32, bytePerRow);
608 			else
609 				buffer->bitmap = new BBitmap(r, 0, B_RGB32, bytePerRow);
610 			status = buffer->bitmap->InitCheck();
611 			if (status != B_OK) {
612 				printf("video decoded format status %08lx %s\n", status, strerror(status));
613 				return;
614 			}
615 			buffer->buffer = (char *)buffer->bitmap->Bits();
616 			buffer->sizeMax = bufferSize;
617 		}
618 
619 		if (fSeekVideo) {
620 			bigtime_t pos = fSeekPosition;
621 			fVideoTrack->SeekToTime(&pos);
622 			fSeekVideo = false;
623 			decodeError = false;
624 		}
625 
626 		int64 frames;
627 		media_header mh;
628 		if (!decodeError) {
629 //			printf("reading video frame...\n");
630 			decodeError = B_OK != fVideoTrack->ReadFrames(buffer->buffer, &frames, &mh);
631 		}
632 		if (!decodeError) {
633 			buffer->sizeUsed = buffer->sizeMax;
634 			buffer->startTime = mh.start_time;
635 		} else {
636 			buffer->sizeUsed = 0;
637 			buffer->startTime = 0;
638 		}
639 
640 		release_sem(fVideoPlaySem);
641 	}
642 }
643 
644 
645 void
646 Controller::VideoPlayThread()
647 {
648 	status_t status;
649 printf("video decode start\n");
650 	if (fVideoTrack == 0) {
651 		return;
652 	}
653 
654 	while (acquire_sem(fVideoPlaySem) == B_OK) {
655 
656 		buffer_info *buffer = &fVideoBuffer[fVideoBufferReadIndex];
657 		fVideoBufferReadIndex = (fVideoBufferReadIndex + 1) % fVideoBufferCount;
658 		wait:
659 		if (fPaused || fStopped) {
660 //printf("waiting..\n");
661 			status = acquire_sem_etc(fThreadWaitSem, 1, B_RELATIVE_TIMEOUT, 50000);
662 			if (status != B_TIMED_OUT)
663 				break;
664 			goto wait;
665 		}
666 
667 #if 0
668 		bigtime_t waituntil;
669 		bigtime_t waitdelta;
670 		char test[100];
671 		{
672 			BAutolock lock(fTimeSourceLock);
673 
674 			waituntil = fTimeSourceSysTime - fTimeSourcePerfTime + buffer->startTime;
675 			waitdelta = waituntil - system_time();
676 			sprintf(test, "sys %.6f perf %.6f, vid %.6f, waituntil %.6f, waitdelta %.6f",
677 			 fTimeSourceSysTime / 1000000.0f,
678 			 fTimeSourcePerfTime / 1000000.0f,
679 			 buffer->startTime / 1000000.0f,
680 			 waituntil / 1000000.0f,
681 			 waitdelta / 1000000.0f);
682 		}
683 		if (fVideoView->LockLooperWithTimeout(5000) == B_OK) {
684 			fVideoView->Window()->SetTitle(test);
685 			fVideoView->UnlockLooper();
686 		}
687 #else
688 		bigtime_t waituntil;
689 		waituntil = fTimeSourceSysTime - fTimeSourcePerfTime + buffer->startTime;
690 #endif
691 
692 		status = acquire_sem_etc(fThreadWaitSem, 1, B_ABSOLUTE_TIMEOUT, waituntil);
693 		if (status != B_TIMED_OUT)
694 			break;
695 
696 		fCurrentBitmap = buffer->bitmap;
697 		fVideoView->DrawFrame();
698 
699 	//	snooze(60000);
700 		release_sem(fVideoDecodeSem);
701 	}
702 
703 //		status_t status = acquire_sem_etc(fThreadWaitSem, 1, B_ABSOLUTE_TIMEOUT, buffer->startTime);
704 //		if (status != B_TIMED_OUT)
705 //			return;
706 }
707 
708 
709 void
710 Controller::StartThreads()
711 {
712 	if (fAudioDecodeSem > 0)
713 		return;
714 
715 	fAudioBufferReadIndex = 0;
716 	fAudioBufferWriteIndex = 0;
717 	fVideoBufferReadIndex = 0;
718 	fVideoBufferWriteIndex = 0;
719 	fAudioDecodeSem = create_sem(fAudioBufferCount, "audio decode sem");
720 	fVideoDecodeSem = create_sem(fVideoBufferCount - 2, "video decode sem");
721 	fAudioPlaySem = create_sem(0, "audio play sem");
722 	fVideoPlaySem = create_sem(0, "video play sem");
723 	fThreadWaitSem = create_sem(0, "thread wait sem");
724 	fAudioDecodeThread = spawn_thread(audio_decode_thread, "audio decode", AUDIO_DECODE_PRIORITY, this);
725 	fVideoDecodeThread = spawn_thread(video_decode_thread, "video decode", VIDEO_DECODE_PRIORITY, this);
726 	fAudioPlayThread = spawn_thread(audio_play_thread, "audio play", AUDIO_PLAY_PRIORITY, this);
727 	fVideoPlayThread = spawn_thread(video_play_thread, "video play", VIDEO_PLAY_PRIORITY, this);
728 	resume_thread(fAudioDecodeThread);
729 	resume_thread(fVideoDecodeThread);
730 	resume_thread(fAudioPlayThread);
731 	resume_thread(fVideoPlayThread);
732 }
733 
734 
735 void
736 Controller::StopThreads()
737 {
738 	if (fAudioDecodeSem < 0)
739 		return;
740 
741 	delete_sem(fAudioDecodeSem);
742 	delete_sem(fVideoDecodeSem);
743 	delete_sem(fAudioPlaySem);
744 	delete_sem(fVideoPlaySem);
745 	delete_sem(fThreadWaitSem);
746 
747 	status_t dummy;
748 	wait_for_thread(fAudioDecodeThread, &dummy);
749 	wait_for_thread(fVideoDecodeThread, &dummy);
750 	wait_for_thread(fAudioPlayThread, &dummy);
751 	wait_for_thread(fVideoPlayThread, &dummy);
752 	fAudioDecodeThread = -1;
753 	fVideoDecodeThread = -1;
754 	fAudioPlayThread = -1;
755 	fVideoPlayThread = -1;
756 	fThreadWaitSem = -1;
757 	fAudioDecodeSem = -1;
758 	fVideoDecodeSem = -1;
759 	fAudioPlaySem = -1;
760 	fVideoPlaySem = -1;
761 }
762 
763 int32
764 Controller::audio_decode_thread(void *self)
765 {
766 	static_cast<Controller *>(self)->AudioDecodeThread();
767 	return 0;
768 }
769 
770 
771 int32
772 Controller::video_decode_thread(void *self)
773 {
774 	static_cast<Controller *>(self)->VideoDecodeThread();
775 	return 0;
776 }
777 
778 
779 int32
780 Controller::audio_play_thread(void *self)
781 {
782 	static_cast<Controller *>(self)->AudioPlayThread();
783 	return 0;
784 }
785 
786 
787 int32
788 Controller::video_play_thread(void *self)
789 {
790 	static_cast<Controller *>(self)->VideoPlayThread();
791 	return 0;
792 }
793