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