/// Thread entry point, called by wxWidgets. /// @return Always 0. wxThread::ExitCode Controller::Entry() { mCommand = NONE; //assumes mutex already locked here, so we can signal to the main thread that we are ready to accept signals by unlocking it when Wait() is called while (1) { if (NONE == mCommand) { //not been told to do anything new during previous interation mCondition->Wait(); //unlock mutex and wait for a signal, then relock } //Now we're going ControllerThreadEvent event(wxEVT_CONTROLLER_THREAD); event.SetName(mName); event.SetCommand(mCommand); mCommand = NONE; //can now detect if another command is issued while busy(more frequently during abnormal situations) mMutex.Unlock(); if (DIE == event.GetCommand()) { AddPendingEvent(event); break; } //do the work... ProdAuto::Recorder::ReturnCode rc = ProdAuto::Recorder::SUCCESS; wxString msg; CORBA::StringSeq_var strings; //deletes itself ProdAuto::MxfTimecode timecode; //structure so deletes itself //send a recorder command, if any for (int i = 0; i < 2; i++) { //always good to try things twice with CORBA event.SetResult(SUCCESS); //default switch (event.GetCommand()) { case CONNECT: case RECONNECT: { //std::cerr << "thread CONNECT/RECONNECT" << std::endl; ProdAuto::MxfDuration maxPreroll = InvalidMxfDuration; //initialisation prevents compiler warning ProdAuto::MxfDuration maxPostroll = InvalidMxfDuration; //initialisation prevents compiler warning ProdAuto::TrackList_var trackList; bool routerRecorder = false; //initialisation prevents compiler warning event.SetTimecodeStateChanged(); //always trigger action mLastTimecodeReceived.undefined = true; //guarantee that a valid timecode will be assumed to be running timecode mMutex.Lock(); wxString name = mName; mMutex.Unlock(); msg = mComms->SelectRecorder(name, mRecorder); if (msg.IsEmpty()) { //OK so far try { trackList = mRecorder->Tracks(); maxPreroll = mRecorder->MaxPreRoll(); maxPostroll = mRecorder->MaxPostRoll(); event.SetTrackStatusList(mRecorder->TracksStatus()); routerRecorder = wxString(mRecorder->RecordingFormat(), *wxConvCurrent).MakeUpper().Matches(wxT("*ROUTER*")); strings = mRecorder->ProjectNames(); } catch (const CORBA::Exception & e) { //std::cerr << "connect/reconnect exception: " << e._name() << std::endl; msg = wxT("Error communicating with this recorder: ") + wxString(e._name(), *wxConvCurrent) + wxT(". Is it operational and connected to the network? The problem may resolve itself if you try again."); } } if (!msg.IsEmpty()) { //failed to select event.SetResult(COMM_FAILURE); } else { //OK so far mMutex.Lock(); if (CONNECT == event.GetCommand()) { //various checks on the data we've acquired if (!trackList->length() || !event.GetNTracks()) { msg = wxT("This recorder has no tracks."); } else if (maxPreroll.undefined) { msg = wxT("This recorder has no maximum preroll."); } else if (!maxPreroll.edit_rate.numerator || !maxPreroll.edit_rate.denominator) { msg = wxT("This recorder has invalid maximum preroll."); //don't want divide by zero errors } else if (maxPostroll.undefined) { //quite likely as postroll isn't dependent on buffer length mMaxPostroll = maxPreroll; //for the edit rate values mMaxPostroll.undefined = false; mMaxPostroll.samples = DEFAULT_MAX_POSTROLL; } else if (!maxPostroll.edit_rate.numerator || !maxPostroll.edit_rate.denominator) { msg = wxT("This recorder has invalid maximum postroll."); //don't want divide by zero errors } else { mMaxPostroll = maxPostroll; } mTrackList = trackList; mMaxPreroll = maxPreroll; mSourceList.length(mTrackList->length()); mRouterRecorder = routerRecorder; } else { //reconnecting //check nothing's changed after reconnection if (maxPreroll.undefined || maxPreroll.edit_rate.numerator != mMaxPreroll.edit_rate.numerator || maxPreroll.edit_rate.denominator != mMaxPreroll.edit_rate.denominator || maxPreroll.samples != mMaxPreroll.samples) { msg = wxT("the maximum preroll has changed"); } else if ((maxPostroll.undefined && mMaxPostroll.samples != DEFAULT_MAX_POSTROLL) || (!maxPostroll.undefined && (maxPostroll.edit_rate.numerator != mMaxPostroll.edit_rate.numerator || maxPostroll.edit_rate.denominator != mMaxPostroll.edit_rate.denominator || maxPostroll.samples != mMaxPostroll.samples))) { msg = wxT("the maximum postroll has changed"); } else if (trackList->length() != mTrackList->length()) { msg = wxT("the number of tracks has changed"); } else if (mRouterRecorder && !routerRecorder) { msg = wxT("it is no longer a router recorder"); } else if (!mRouterRecorder && routerRecorder) { msg = wxT("it now presents itself as a router recorder"); } else { for (unsigned int i = 0; i < trackList->length(); i++) { if (strcmp(trackList[i].name, mTrackList[i].name)) { msg = wxT("name"); } else if (trackList[i].type != mTrackList[i].type) { msg = wxT("type"); } else if (trackList[i].id != mTrackList[i].id) { msg = wxT("ID"); } else if (trackList[i].has_source != mTrackList[i].has_source) { msg = wxT("\"has_source\" status"); } else if (trackList[i].has_source && strcmp(trackList[i].src.package_name, mTrackList[i].src.package_name)) { msg = wxT("package name"); } else if (trackList[i].has_source && strcmp(trackList[i].src.track_name, mTrackList[i].src.track_name)) { msg = wxT("track name"); } if (msg.Length()) { msg = wxT("track \"") + wxString(mTrackList[i].name, *wxConvCurrent) + wxT("\" has changed ") + msg; break; } } } } mMutex.Unlock(); } if (msg.IsEmpty()) { rc = ProdAuto::Recorder::SUCCESS; } else { rc = ProdAuto::Recorder::FAILURE; event.SetMessage(msg); event.SetTrackStatusList(0); //track status not valid } break; } case SET_TAPE_IDS: { //std::cerr << "thread SET_TAPE_IDS" << std::endl; mMutex.Lock(); CORBA::StringSeq sourceNames = mSourceNames; CORBA::StringSeq tapeIds = mTapeIds; mMutex.Unlock(); try { mRecorder->SetTapeNames(sourceNames, tapeIds); } catch (const CORBA::Exception & e) { //std::cerr << "set tape IDs exception: " << e._name() << std::endl; event.SetResult(COMM_FAILURE); } rc = ProdAuto::Recorder::SUCCESS; //no return code suppled by SetTapeNames() break; } case ADD_PROJECT_NAMES: { mMutex.Lock(); CORBA::StringSeq projectNames = mProjectNames; mMutex.Unlock(); try { mRecorder->AddProjectNames(projectNames); } catch (const CORBA::Exception & e) { //std::cerr << "AddProjectNames exception: " << e._name() << std::endl; event.SetResult(COMM_FAILURE); } rc = ProdAuto::Recorder::SUCCESS; //no return code suppled by AddProject() break; } case RECORD: { //std::cerr << "thread RECORD" << std::endl; mMutex.Lock(); timecode = mStartTimecode; ProdAuto::MxfDuration preroll = mPreroll; CORBA::BooleanSeq rec_enable = mEnableList; std::string project = (const char *) mProject.mb_str(*wxConvCurrent); mMutex.Unlock(); try { rc = mRecorder->Start(timecode, preroll, rec_enable, project.c_str(), false); } catch (const CORBA::Exception & e) { //std::cerr << "start exception: " << e._name() << std::endl; event.SetResult(COMM_FAILURE); } break; } case STOP: { //std::cerr << "thread STOP" << std::endl; mMutex.Lock(); timecode = mStopTimecode; ProdAuto::MxfDuration postroll = mPostroll; char * description = CORBA::string_dup(mDescription.mb_str(*wxConvCurrent)); ProdAuto::LocatorSeq locators = mLocators; mMutex.Unlock(); try { rc = mRecorder->Stop(timecode, postroll, description, locators, strings.out()); } catch (const CORBA::Exception & e) { //std::cerr << "stop exception: " << e._name() << std::endl; event.SetResult(COMM_FAILURE); } event.SetTimecodeStateChanged(); //so that a timecode that was stuck during the recording will be reported as such break; } default: break; } if (COMM_FAILURE != event.GetResult()) { if (ProdAuto::Recorder::SUCCESS == rc) { event.SetStrings(strings); //only relevant for STOP (list of files) and CONNECT (list of project names) if (!timecode.edit_rate.numerator || !timecode.edit_rate.denominator) { timecode.undefined = true; } event.SetTimecode(timecode); //only relevant for START and STOP } else { event.SetResult(FAILURE); } break; //comms successful or no call made: no need to try again } } event.SetTrackList(mTrackList); //get status if (COMM_FAILURE != event.GetResult()) { for (int j = 0; j < 2; j++) { //always good to try things twice with CORBA try { if (CONNECT != event.GetCommand() && RECONNECT != event.GetCommand()) { //haven't already got track status list event.SetTrackStatusList(mRecorder->TracksStatus()); } //timecode situation if (!event.GetNTracks() || event.GetTrackStatusList()[0].timecode.undefined || !event.GetTrackStatusList()[0].timecode.edit_rate.numerator || !event.GetTrackStatusList()[0].timecode.edit_rate.denominator) { //no or invalid timecode if (!mLastTimecodeReceived.undefined) { //newly in this state mLastTimecodeReceived.undefined = true; //to allow future detection of state change event.SetTimecodeStateChanged(); //ABSENT is the default timecode state in the event } } else { //timecode value is OK ProdAuto::MxfTimecode timecode = event.GetTrackStatusList()[0].timecode; if (mLastTimecodeReceived.undefined) { //timecode has just appeared event.SetTimecodeState(UNCONFIRMED); if (!mTimecodeRunning) { //just started mTimecodeRunning = true; //to allow future detection of state change event.SetTimecodeStateChanged(); } } else if (!wxDateTime::Now().IsEqualUpTo(mLastTimecodeRequest, wxTimeSpan::Milliseconds(100))) { //long enough since the last stored value was received to assume that the timecode has changed if (timecode.samples != mLastTimecodeReceived.samples) { //timecode is running event.SetTimecodeState(RUNNING); if (!mTimecodeRunning) { //just started mTimecodeRunning = true; //to allow future detection of state change event.SetTimecodeStateChanged(); } } else { //timecode is stuck event.SetTimecodeState(STUCK); if (mTimecodeRunning) { //just stopped mTimecodeRunning = false; //to allow future detection of state change event.SetTimecodeStateChanged(); } } } else { //repeat previous status event.SetTimecodeState(mTimecodeRunning ? UNCONFIRMED : STUCK); } mLastTimecodeReceived = timecode; mLastTimecodeRequest = wxDateTime::Now(); //now or sometime before! } break; //successful: no need to try again } catch (const CORBA::Exception & e) { //std::cerr << "status exception: " << e._name() << std::endl; event.SetResult(COMM_FAILURE); //indicated by list size being zero } } } //communicate the results AddPendingEvent(event); mMutex.Lock(); } return 0; }
RecordingDevice::ReturnCode RecordingDevice::StopRecording(const Timecode & tc) { bool ok_so_far = true; ReturnCode return_code = OK; if(CORBA::is_nil(mRecorder)) { ok_so_far = false; return_code = NO_DEVICE; } if (ok_so_far) { // Sort out parameters to be passed ProdAuto::MxfTimecode stop_tc_mxf; stop_tc_mxf.undefined = false; stop_tc_mxf.samples = tc.FramesSinceMidnight(); //::Duration post_roll_dur; //post_roll_dur.TotalFrames(post_roll_frames); ProdAuto::MxfDuration post_roll_mxf; post_roll_mxf.undefined = false; post_roll_mxf.samples = post_roll_frames; ::ProdAuto::Recorder::ReturnCode result; CORBA::StringSeq_var files; ::ProdAuto::LocatorSeq locators; try { result = mRecorder->Stop(stop_tc_mxf, post_roll_mxf, "", "", locators, files.out()); } catch (const CORBA::Exception &) { result = ProdAuto::Recorder::FAILURE; } if(ProdAuto::Recorder::SUCCESS == result) { //stop_tc += post_roll_dur; //mOutTime = stop_tc.Text(); // store the stop timecode //::Duration dur_tc; //dur_tc = Timecode(mOutTime.c_str()) - Timecode(mInTime.c_str()); //mDuration = dur_tc.Text(); // store the duration // need to store paths //for(int i = 0; i < SDI_RECORDER_CHANNELS; ++i) //{ //mFilename[i] = path.in()[i].in(); //} mIsRecording = false; mHasRecorded = true; } else { ok_so_far = false; return_code = FAILED; } } return return_code; }