123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490 |
- #include "PlaybackBase2.h"
- PlaybackImpl::PlaybackImpl()
- {
- playback=0;
- playback_parameters=0;
- }
- PlaybackImpl::~PlaybackImpl()
- {
- // TODO: decide if we need playback->Release() or not
- if (playback_parameters)
- playback_parameters->Release();
- }
- void PlaybackImpl::Connect(PlaybackBase2 *playback, ifc_playback_parameters *playback_parameters)
- {
- this->playback = playback;
- // TODO: decide if we need playback->Retain() or not
- this->playback_parameters = playback_parameters;
- if (playback_parameters)
- playback_parameters->Retain();
- }
- /* ---------- */
- PlaybackBase2::PlaybackBase2()
- {
- out=0;
- implementation=0;
- filelocker=0;
- paused=false;
- last_position=0;
- output_pointers=0;
- exact_length=false;
- memset(¶meters, 0, sizeof(parameters));
- }
- PlaybackBase2::~PlaybackBase2()
- {
- /* out should have hopefully been close already. just in case */
- if (out)
- out->Release();
- out=0;
- if (filelocker)
- filelocker->Release();
- delete implementation;
- free(output_pointers);
- }
- ns_error_t PlaybackBase2::Initialize(api_service *service_manager, PlaybackImpl *implementation, nx_uri_t filename, ifc_player *player)
- {
- service_manager->GetService(&filelocker);
- this->implementation = implementation;
- ns_error_t ret = PlaybackBase::Initialize(filename, player);
- if (ret != NErr_Success)
- return ret;
- implementation->Connect(this, secondary_parameters);
- this->ifc_playback::Retain(); /* the thread needs to hold a reference to this object so that it doesn't disappear out from under us */
- NXThreadCreate(&playback_thread, PlayerThreadFunction, this);
- return NErr_Success;
- }
- nx_thread_return_t PlaybackBase2::PlayerThreadFunction(nx_thread_parameter_t param)
- {
- PlaybackBase2 *playback = (PlaybackBase2 *)param;
- NXThreadCurrentSetPriority(NX_THREAD_PRIORITY_PLAYBACK);
- nx_thread_return_t ret = playback->DecodeLoop();
- playback->ifc_playback::Release(); /* give up the reference that was acquired before spawning the thread */
- return ret;
- }
- int PlaybackBase2::Init()
- {
- if (filelocker)
- filelocker->WaitForReadInterruptable(filename, this);
- ns_error_t ret = implementation->Open(filename);
- if (ret != NErr_Success)
- return ret;
- ifc_metadata *metadata;
- if (implementation->GetMetadata(&metadata) == NErr_Success)
- {
- player->SetMetadata(metadata);
- metadata->Release();
- }
- else
- player->SetMetadata(0);
- player->SetSeekable(implementation->IsSeekable()?1:0);
- double length;
- ret = implementation->GetLength(&length, &exact_length);
- if (ret == NErr_Success)
- player->SetLength(length);
- return NErr_Success;
- }
- nx_thread_return_t PlaybackBase2::DecodeLoop()
- {
- player->OnLoaded(filename);
- int ret = Init();
- if (ret != NErr_Success)
- {
- implementation->Close();
- if (filelocker)
- filelocker->UnlockFile(filename);
- player->OnError(ret);
- return 0;
- }
- player->OnReady();
- /* wait for Play (or Stop to abort) */
- for (;;)
- {
- ns_error_t ret = Wake(WAKE_PLAY|WAKE_STOP|WAKE_INTERRUPT);
- if (ret == WAKE_PLAY)
- {
- break;
- }
- else if (ret == WAKE_STOP)
- {
- player->OnStopped();
- goto cleanup;
- }
- else if (ret == WAKE_INTERRUPT)
- {
- ns_error_t ret = Internal_Interrupt();
- if (ret != NErr_Success)
- {
- implementation->Close();
- player->OnError(ret);
- goto cleanup;
- }
- }
- }
- /* at this point, we know that PLAY is on */
- for (;;)
- {
- int ret = Check(WAKE_STOP|WAKE_PAUSE|WAKE_INTERRUPT);
- if (ret == WAKE_PAUSE)
- {
- if (out)
- out->Pause(1);
- paused=true;
- continue; /* continue in case there's another wake reason */
- }
- else if (ret== WAKE_UNPAUSE)
- {
- if (out)
- out->Pause(0);
- paused=false;
- continue; /* continue in case there's another wake reason */
- }
- else if (ret == WAKE_STOP)
- {
- if (out)
- {
- out->Stop();
- out->Release();
- out=0;
- }
- player->OnStopped();
- goto cleanup;
- }
- else if (ret == WAKE_INTERRUPT)
- {
- ns_error_t ret = Internal_Interrupt();
- if (ret != NErr_Success)
- {
- implementation->Close();
- player->OnError(ret);
- goto cleanup;
- }
- continue;
- }
- Agave_Seek *seek = PlaybackBase::GetSeek();
- if (seek)
- {
- ns_error_t seek_error;
- double new_position;
- ns_error_t ret = implementation->Seek(seek, &seek_error, &new_position);
- if (ret != NErr_Success)
- {
- player->OnError(ret);
- goto cleanup;
- }
- if (out)
- out->Flush(new_position);
- player->OnSeekComplete(seek_error, new_position);
- PlaybackBase::FreeSeek(seek);
- }
- ret = implementation->DecodeStep();
- if (ret == NErr_EndOfFile)
- {
- if (out)
- out->Done();
- PlaybackBase::OnStopPlaying();
- player->OnEndOfFile();
- ret = WaitForClose();
- if (out)
- out->Release();
- out=0;
- if (ret != NErr_True)
- goto cleanup;
- }
- else if (ret != NErr_Success)
- {
- if (out)
- {
- out->Done();
- out->Release();
- out=0;
- }
- if (ret != NErr_False)
- player->OnError(NErr_Error); // TODO: find better error code
- goto cleanup;
- }
- else
- {
- if (!exact_length)
- {
- double length;
- ret = implementation->GetLength(&length, &exact_length);
- if (ret == NErr_Success)
- player->SetLength(length);
- }
- }
- }
- cleanup:
- implementation->Close();
- if (filelocker)
- filelocker->UnlockFile(filename);
- return 0;
- }
- ns_error_t PlaybackBase2::WaitForClose()
- {
- if (!out)
- {
- player->OnClosed();
- return NErr_False;
- }
- else for (;;)
- {
- int ret = Wait(10, WAKE_PLAY|WAKE_KILL|WAKE_STOP);
- if (ret == WAKE_KILL)
- {
- player->OnClosed();
- return NErr_False;
- }
- else if (ret == WAKE_PLAY)
- {
- return NErr_True;
- }
- else if (ret == WAKE_STOP)
- {
- player->OnStopped();
- return NErr_False;
- }
- else
- {
- if (out->Playing() == NErr_True)
- player->SetPosition(last_position - out->Latency());
- else
- {
- player->SetPosition(last_position);
- player->OnClosed();
- return NErr_False;
- }
- }
- }
- }
- ns_error_t PlaybackBase2::OpenOutput(const ifc_audioout::Parameters *_parameters)
- {
- // if out is already set, it means that there was a change in parameters, so we'll start a new stream
- if (out)
- {
- // check to see that the parameters actually changed
- if (!memcmp(¶meters, _parameters, sizeof(parameters)))
- return NErr_Success;
- out->Done();
- out=0;
- }
- parameters = *_parameters;
- free(output_pointers);
- output_pointers = (const uint8_t **)malloc(parameters.audio.number_of_channels * sizeof(const uint8_t *));
- if (!output_pointers)
- return NErr_OutOfMemory;
- ns_error_t ret = output_service->AudioOpen(¶meters, player, secondary_parameters, &out);
- if (ret != NErr_Success)
- {
- player->OnError(ret);
- return ret;
- }
- if (paused)
- out->Pause(1);
- else
- out->Pause(0);
- return NErr_True;
- }
- int PlaybackBase2::OutputNonInterleaved(const void *decode_buffer, size_t decoded, double start_position)
- {
- int ret;
- size_t frames_written=0;
- const uint8_t **buffer = (const uint8_t **)decode_buffer;
- for (size_t c=0;c<parameters.audio.number_of_channels;c++)
- {
- output_pointers[c] = buffer[c];
- }
- while (decoded)
- {
- size_t to_write = out->CanWrite();
- if (to_write)
- {
- if (decoded < to_write)
- to_write = decoded;
- ret = out->Output(output_pointers, to_write);
- if (ret != NErr_Success)
- {
- out->Release();
- out=0;
- return ret;
- }
- decoded -= to_write;
- for (size_t c=0;c<parameters.audio.number_of_channels;c++)
- {
- output_pointers[c] += to_write/parameters.audio.number_of_channels;
- }
- frames_written += to_write/parameters.audio.number_of_channels;
- player->SetPosition(start_position + (double)frames_written/parameters.audio.sample_rate - out->Latency());
- }
- else
- {
- ns_error_t ret = OutputWait();
- if (ret != NErr_Success)
- return ret;
- }
- }
- return NErr_True;
- }
- int PlaybackBase2::Output(const void *decode_buffer, size_t decoded, double start_position)
- {
- int ret;
- size_t frames_written=0;
- const uint8_t *decode8 = (const uint8_t *)decode_buffer;
- size_t buffer_position=0;
- while (decoded)
- {
- size_t to_write = out->CanWrite();
- if (to_write)
- {
- if (decoded < to_write)
- to_write = decoded;
- ret = out->Output(&decode8[buffer_position], to_write);
- if (ret != NErr_Success)
- {
- out->Release();
- out=0;
- return ret;
- }
- decoded -= to_write;
- buffer_position += to_write;
- frames_written += to_write/parameters.audio.number_of_channels;
- player->SetPosition(start_position + (double)frames_written/parameters.audio.sample_rate - out->Latency());
- }
- else
- {
- ns_error_t ret = OutputWait();
- if (ret != NErr_Success)
- return ret;
- }
- }
- return NErr_True;
- }
- ns_error_t PlaybackBase2::OutputWait()
- {
- if (paused)
- {
- /* if we're paused, we need to sit and wait until we're eiter unpaused or stopped */
- for (;;)
- {
- int ret = Wake(WAKE_STOP|WAKE_PAUSE|WAKE_INTERRUPT);
- if (ret == WAKE_STOP)
- {
- out->Stop();
- out->Release();
- out=0;
- player->OnStopped();
- return NErr_False;
- }
- else if (ret == WAKE_UNPAUSE)
- {
- out->Pause(0);
- paused=false;
- break;
- }
- else if (ret == WAKE_PAUSE)
- {
- out->Pause(1);
- paused=true;
- }
- else if (PlaybackBase::PendingSeek())
- {
- return NErr_True;
- }
- else if (ret == WAKE_INTERRUPT)
- {
- ns_error_t ret = Internal_Interrupt();
- if (ret != NErr_Success)
- return ret;
- }
- }
- }
- else
- {
- int ret = Wait(10, WAKE_STOP);
- if (ret == WAKE_STOP)
- {
- out->Stop();
- out->Release();
- out=0;
- player->OnStopped();
- return NErr_False;
- }
- }
- return NErr_Success;
- }
- ns_error_t PlaybackBase2::Internal_Interrupt()
- {
- Agave_Seek resume_information;
- implementation->Interrupt(&resume_information);
- ns_error_t ret = filelocker->UnlockFile(filename);
- if (ret != NErr_Success)
- {
- implementation->Close();
- return ret;
- }
- PlaybackBase::OnInterrupted();
- if (filelocker)
- filelocker->WaitForReadInterruptable(filename, this);
- ret = implementation->Resume(&resume_information);
- if (ret != NErr_Success)
- return ret;
- ifc_metadata *metadata;
- if (implementation->GetMetadata(&metadata) == NErr_Success)
- {
- player->SetMetadata(metadata);
- metadata->Release();
- }
- return NErr_Success;
- }
|