#include "rm_player.h" #include #include #include #include #include #include "../rm_include.h" #include "../rm_app.h" #include "../data/rm_video_item_2ch.h" #include "../ui/rm_button.h" #include "../ui/rm_slider.h" //#include "../ui/rm_frame_slider.h" #include "../ui/rm_play_slider.h" #include "rm_key_event.h" #include "fav/fav_common.h" // 연속해서 press + release 문제 발생시 수정 #define FIX_PRESS_AND_RELEASE 1 #if (RM_MODEL_360 || SUPPORT_WIDE_MODE) #include "../fav/OpenGLVideo.h" #include "../fav/OpenGLRendererBase.h" #endif #if (MODEL_BBVIEWER) #include "../ui/rm_eq_widget.h" #endif // MODEL_BBVIEWER #if (CAPTURE_DEBUG) QString g_errorMessage; #endif // 플레이 마지막에 AVDemux 가 종료된 시점에서 seek 하면 문제가 발생함. // 실제 300~400ms 사이에서 발생함.. #define LAST_PLAY_SEEK_TEST 0 #define USE_LAST_PLAY_SEEK 1 // 플레이 종료 시점에서 전방으로 이동시 발생하는 문제 제거 #define LAST_PLAY_SEEK_LIMIT 500 // 100 으로 처리하면 step 이동시 종료되어 다음 파일로 넘어감 //const int kSyncInterval = 500; //const qreal kVolumeInterval = 0.1; //const int kMuteInterval = 10;//100; //const qint64 gPausePosition = 1; // ??? // RMPlayer* RMPlayer::_instance = NULL; #if (MODEL_BBVIEWER) float g_PlaySpeedList[PLAY_SPEED_COUNT] = {0.3f,0.5f,1.0f,2.0f,3.0f}; //QList g_PlaySpeedNames = QList() << "0.5" << "1.0" << "2.0" << "4.0"; #elif (RM_MODEL == RM_MODEL_TYPE_KEIYO1 || \ RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \ RM_MODEL == RM_MODEL_TYPE_FC_DR232W) float g_PlaySpeedList[PLAY_SPEED_COUNT] = {0.5f, 0.8f, 1.0f,2.0f}; int DEFAULT_SPEED_INDEX = 2; QList g_PlaySpeedNames = QList() << "0.5 x" << "0.8 x" << "1 x" << "2.0 x"; #elif (RM_MODEL == RM_MODEL_TYPE_AN6000) float g_PlaySpeedList[PLAY_SPEED_COUNT] = {1.0f, 2.0f,4.0f,16.0f}; QList g_PlaySpeedNames = QList() << "1x" << "2x" << "4x" << "16x"; int DEFAULT_SPEED_INDEX = 0; #elif (RM_MODEL == RM_MODEL_TYPE_TB4000) float g_PlaySpeedList[PLAY_SPEED_COUNT] = {0.5f, 1.0f, 2.0f,4.0f,8.0f}; QList g_PlaySpeedNames = QList() << "0.5 x" << "1 x" << "2 x" << "4 x" << "8 x"; int DEFAULT_SPEED_INDEX = 1; #elif (RM_MODEL_EMT_KR) float g_PlaySpeedList[PLAY_SPEED_COUNT] = {0.25f,0.5f, 1.0f, 2.0f,4.0f}; QList g_PlaySpeedNames = QList() << FM_WSTR(L"¼ x") << FM_WSTR(L"½ x") << "1 x" << "2 x" << "4 x"; int DEFAULT_SPEED_INDEX = 2; #else // @OTHER float g_PlaySpeedList[PLAY_SPEED_COUNT] = {0.5f, 1.0f, 2.0f,8.0f}; QList g_PlaySpeedNames = QList() << "0.5 x" << "1 x" << "2 x" << "8 x"; int DEFAULT_SPEED_INDEX = 1; #endif //const int g_PlaySpeed1XIndex = 1; //#if (USE_LIBRARY_MODE) // 360 DLL //RMPlayer* RMPlayer::libraryInstance = NULL; //#endif // RMPlayer::RMPlayer(QObject *parent) : RMPlayerZoom(parent) { _sync_time = false; // seek sync timer <- sub _sync_restart_timer = NULL; // " <- sub _stepping = false; // seek <- sub // _sliderReleaseTimer = NULL; // seek <- sub _refreshSeeking = false; // seek <- sub _captureFileList = NULL; // capture <- sub _captureTimer = NULL; // capture <- sub _captureWatcher = NULL; // " _pauseWhileSeek = false; #if (USE_RM_KEYBOARD_EVENT) _seekAcceleration = 1; _seekPressedTimer = NULL; _seekReleasedTimer = NULL; _seekUpdateTimer = NULL; #endif #if (RM_TESTING) _testFilterF = NULL; #endif // 슬라이더 업데이트 //qInfo() << _playerF << __FUNCTION__; connect(_playerF, SIGNAL(started()), SLOT(updateSlider())); } //-------------------------------------------------------------------------- void RMPlayer::onUserStop() { #if (PLAY_SEEK_FILE_MOVE) // 사용자 STOP 요청시 step 모드 종료됨 endStepMode(); #endif // 다음 파일로 넘어갈때는 호출하면 안됨 ??? #if (PLAY_CONTINUE_EVENT) stop(); emit playEvent(PLAY_DID_USER_STOP,NULL); #else emit playEvent(PLAY_DID_USER_STOP,NULL); stop(); #endif // User Stop 시에는 잔상 남지 않도록 Clear /* int count = 0; while(count++ < 100 && (_playerF->isPlaying() || (_playerR->isLoaded() && _playerR->isPlaying()))) { QThread::msleep(10); } FAV::VideoFrame frame; if(_videoOutputF != NULL) { _videoOutputF->receive(frame); } if(_videoOutputR != NULL) { _videoOutputR->receive(frame); } */ } void RMPlayer::stop() { RMPlayerBase::stop(); stopSeekUpdateTimer(); } #if (PLAYER_ONLY_LIBRARY_MODE) void RMPlayer::clear() { if(_playerF != NULL) { _playerF->stop(); delete _playerF; _playerF = NULL; } } void RMPlayer::create() { if(_playerF == NULL) { _createVideoPlayers(); } } #endif // PLAYER_ONLY_LIBRARY_MODE void RMPlayer::updatePausedScreen() { _pausePosition = MAX(_pausePosition,INITIAL_VIDEO_POSITION); // 비동기화 seek 가 발생하면 업데이트가 일어남 if(_playerF->isPaused()) { seek(_pausePosition,true); } } void RMPlayer::updatePausedScreenFrontOnly() { if(_playerF == NULL) { return; } _pausePosition = MAX(_pausePosition,INITIAL_VIDEO_POSITION); // 비동기화 seek 가 발생하면 업데이트가 일어남 if(_playerF->isPaused()) { seekFrontOnly(_pausePosition,true); } } #if (!(SINGLE_CH_VIEWER || TOGGLE_PLAYER) || PENTA_CHANNEL) void RMPlayer::updatePausedScreenRearOnly() { if(_playerR == NULL) { return; } _pausePosition = MAX(_pausePosition,INITIAL_VIDEO_POSITION); // 비동기화 seek 가 발생하면 업데이트가 일어남 if(_playerR->isPaused()) { seekRearOnly(_pausePosition,true); } } #endif //-------------------------------------------------------------------------- // Slider 컨트롤 /* void RMPlayer::setSliderFrame(RMFrameSlider* frame) { //(QSlider*) _slider = frame->playSlider->slider; // PRESSED -> MOVED -> RELEASED 순서대로 호출 되도록 이벤트 처리해야함 // 노브가 아닌 지점 CLICK 시에는 Slider는 (Slider)PRESSED 이벤트 만 호출됨 (RMSlider 내부에서 구현) // PRESSED 이벤트 발생시 KNOB 가 false 일 경우 released 이벤트 발생하지 않으니 별도의 처리 하지 말아야함 // connect(_slider, SIGNAL(sliderPressedWithKnob(bool)),SLOT(onSliderPress(bool))); // 이벤트 두번씩 발생함 // connect(_slider, SIGNAL(sliderMoved(int)),SLOT(onSliderMove(int))); // 슬라이더 컨트롤 // connect(_slider, SIGNAL(mouseReleased()),SLOT(onSliderRelease())); // 슬라이더 업데이트 connect(_playerF, SIGNAL(started()), SLOT(updateSlider())); // _volumeSlider = frame->volumeSlider; // connect(_volumeSlider, SIGNAL(sliderPressed()), SLOT(onSetVolume())); // connect(_volumeSlider, SIGNAL(valueChanged(int)), SLOT(onSetVolume())); // onSetVolume(); // connect(frame->speedSlider, SIGNAL(mouseReleased()),SLOT(onSpeedSliderReleased())); // connect(frame->speedSlider, SIGNAL(sliderMoved(int)),SLOT(onSpeedSliderMoved(int))); // 참조용 _muteButton = frame->muteButton; } */ #if (MODEL_BBVIEWER) void RMPlayer::setEQFrame(RMEQWidget* eq) { connect(eq->speedSlider, SIGNAL(mouseReleased()),SLOT(onSpeedSliderReleased())); connect(eq->speedSlider, SIGNAL(sliderMoved(int)),SLOT(onSpeedSliderMoved(int))); } #endif // ------------------------------------------------------------------------------------------------ // 캡쳐 기능 QString RMPlayer::currentTimeString(double* lat, double* lon) { qreal ratio = (qreal)_playerF->position() / (qreal)_playerF->duration(); QDateTime dateTime = _currentItem->dateTimeInPosition(ratio, lat, lon); if(dateTime.isValid()) { // range->setProperty("Value",FM_WSTR(L"20XX년 XX월 XX일 XX시 XX분")); QString dateFormatString = "yyyy" + FM_WSTR(L"년") + " MM" + FM_WSTR(L"월") + " dd" + FM_WSTR(L"일"); QString date = dateTime.toString(dateFormatString); QString timeFormatString = "HH" + FM_WSTR(L"시") + " mm" + FM_WSTR(L"분"); QString time = dateTime.toString(timeFormatString); return date + " " + time; } return ""; } // 0. 캡쳐 요청 bool RMPlayer::requestVideoCapture() { bool readyForCapture = (_playerF->isPaused() == true); // 현재 정지된 상태인지 확인 if(_captureWatcher != NULL || prepareForCapture() == false) // 준비 (정지, 전체화면 취소, 확대 취소) { return false; } #if (CAPTURE_DEBUG) g_errorMessage = "0-1.Prepare for capture.\n"; #endif // 혹시 capture 가 성공하지 못했을 경우 2초후 확인 clearCaptureWatcher(); _captureWatcher = new QTimer(this); _captureWatcher->setSingleShot(true); _captureWatcher->setInterval(2000); connect(_captureWatcher,SIGNAL(timeout()),SLOT(onFailToCapture())); _captureWatcher->start(); QApplication::setOverrideCursor(Qt::WaitCursor); // 커서 변경 if(readyForCapture == true) // 현재 준비 완료 상태이면 0.1초후 캡쳐 시작 { #if (CAPTURE_DEBUG) g_errorMessage += "0-2.Ready for capture.\n"; qInfo() << "0-2.Ready for capture."; #endif QTimer::singleShot(100, this, SLOT(requestVideoCaptureProcess())); } else // 준비가 완료되지 않은 상태이면 준비확인 { #if (CAPTURE_DEBUG) g_errorMessage += "0-2.Not ready for capture\n";; qInfo() << "0-2.Not ready for capture"; #endif clearCaptureTimer(); _captureTimer = new QTimer(this); _captureTimer->setSingleShot(false); _captureTimer->setInterval(500); connect(_captureTimer,SIGNAL(timeout()),SLOT(onReadyForCapture())); _captureTimer->start(); } return true; } // 1. 캡쳐 준비 (다른 용도로도 사용될 수 있음) bool RMPlayer::prepareForCapture() { if(_playerF != NULL) { if(_playerF->isLoaded() == false || _playerF->position() < INITIAL_VIDEO_POSITION) { return false; } if(_playerF->isPaused() == false) { #if (CAPTURE_DEBUG) g_errorMessage += "0-0.Try to pause\n";; qInfo() << "0-0.Try to pause"; #endif playOrPause(false); } } emit cancelFullScreen(); return true; } // 0-1 캡쳐가 준비되지 않은 상태이면 전/후방 정지까지 대기 void RMPlayer::onReadyForCapture() { #if (CAPTURE_DEBUG) g_errorMessage += "0-1.Check ready for capture.\n"; qInfo() << "0-1.Check ready for capture."; #endif #if (FORCE_SINGLE_PLAYER || SINGLE_CH_VIEWER) // 통합해야함.. if(_playerF->isPaused() == true) #else if(_playerF->isPaused() == true && (_playerR == NULL || (_playerR->isLoaded() == false || _playerR->isPaused() == true))) // 존재하지 않거나, 종료되었을 경우 #endif { #if (CAPTURE_DEBUG) g_errorMessage += "0-1.Ready for capture all paused done.\n"; qInfo() << "0-1.Ready for capture all paused done."; #endif clearCaptureTimer(); QTimer::singleShot(100, this, SLOT(requestVideoCaptureProcess())); } } // 0-2 캡쳐 준비가 완료되면 삭제 void RMPlayer::clearCaptureTimer() { if(_captureTimer != NULL) { _captureTimer->stop(); delete _captureTimer; _captureTimer = NULL; } } // 0-99 2초 이후에도 캡쳐가 완료되지 않았을 경우 void RMPlayer::onFailToCapture() { #if (CAPTURE_DEBUG) QString message; #if !(FORCE_SINGLE_PLAYER) message.sprintf("capture error.\nplayerF:%d(%d),playerR:%d(%d)\n",(int)_playerF->isPaused(),(int)_playerF->position(),(int)_playerR->isPaused(),(int)_playerR->position()); #else message.sprintf("capture error.\nplayerF:%d(%d)\n",(int)_playerF->isPaused(),(int)_playerF->position()); #endif message += g_errorMessage; QMessageBox msgBox(QMessageBox::Warning, "", message, QMessageBox::Yes, RMUIManager::window); msgBox.setWindowFlags(Qt::WindowTitleHint | Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::CustomizeWindowHint); msgBox.exec(); #endif clearCaptureWatcher(); QApplication::restoreOverrideCursor(); // 커서 복구 } // 2. 캡쳐 프로세스 시작 void RMPlayer::requestVideoCaptureProcess() { #if (CAPTURE_DEBUG) g_errorMessage += "2-1.Capture process started.\n"; qInfo() << "2-1.Capture process started."; #endif if(_captureFileList != NULL) { delete _captureFileList; _captureFileList = NULL; } _captureFileList = new QList(); // 파일명 리스트 생성 _captureDir = RMApp::appPath(RMApp::CAPTURE); FAV::VideoCapture* captureF = _playerF->videoCapture(); // 캡쳐 instance //#if (CAPTURE_DEBUG) // qInfo() << "2-1. Capture async:" << captureF->isAsync() << " autosave:" << captureF->autoSave(); //#endif if(captureF != NULL) { captureF->setAsync(false); captureF->setCaptureDir(_captureDir); // 폴더 지정 captureF->setSaveFormat("jpg"); // 포멧 지정 // Slider 컨트롤에 표시한 시간과 다르게 나올 수 있어 // _playerF->position() 은 사용하지 않으며 최종 전송된 _lastPosition 사용 // QString captureTitle = _currentItem->titleCapture(titleSeconds); // _lastPosition captureTitle += "_CH1_"; captureF->setCaptureName(captureTitle); connect(captureF,SIGNAL(saved(const QString&)),SLOT(onCaptureSavedFront(const QString&)),Qt::UniqueConnection); #if (CAPTURE_DEBUG) connect(captureF,SIGNAL(imageCaptured(const QImage&)),SLOT(onImageCaptured(const QImage&)),Qt::UniqueConnection); connect(captureF,SIGNAL(failed()),SLOT(onCaptureFailed()),Qt::UniqueConnection); #endif captureF->capture(); // (전방)캡쳐 시작 // updatePausedScreen(); // 한번더 확인 #if (CAPTURE_DEBUG) g_errorMessage += "2-2.Start front capture.\n"; qInfo() << "2-2.Start front capture."; #endif } #if (CAPTURE_DEBUG) else { g_errorMessage += "2-2.Error front capture.\n"; qInfo() << "2-2.Error front capture."; } #endif } // 3. 전방 캡쳐 완료 void RMPlayer::onCaptureSavedFront(const QString& path) { if(_playerF != NULL) { FAV::VideoCapture* captureF = _playerF->videoCapture(); // 캡쳐 instance disconnect(captureF,SIGNAL(saved(const QString&)),this,SLOT(onCaptureSavedFront(const QString&))); } #if (CAPTURE_DEBUG) g_errorMessage += "3-1.Front capture done:" + path + "\n"; qInfo() << "3-1.Front capture done:" << path; #endif if(_captureFileList != NULL) { _captureFileList->append(path); } #if (!(FORCE_SINGLE_PLAYER || SINGLE_CH_VIEWER || TOGGLE_PLAYER) || PENTA_CHANNEL) //if(_playerF->videoStreamCount() == 1) // 1CH 파일일 경우 ??? (TELEBIT mp4 1CH) if(_playerR == NULL || _playerR->isLoaded() == false) // 1CH 파일일 경우 ??? (TELEBIT mp4 1CH) #endif { if(_captureFileList != NULL) { QApplication::restoreOverrideCursor(); // 커서 복구 clearCaptureWatcher(); // watcher 삭제 //qInfo() << __LINE__ << __FUNCTION__; emit videoCaptureDone(_captureFileList); // 완료 시그널 } } #if (!(FORCE_SINGLE_PLAYER || SINGLE_CH_VIEWER || TOGGLE_PLAYER) || PENTA_CHANNEL) else // 후방 캡쳐 시작 { FAV::VideoCapture* captureR = _playerR->videoCapture(); if(captureR != NULL) { captureR->setAsync(false); captureR->setCaptureDir(_captureDir); // 폴더, 파일, 포멧, 이름 지정 captureR->setSaveFormat("jpg"); QString captureTitle = _currentItem->titleCapture(_playerF->position()); captureTitle += "_CH2_"; captureR->setCaptureName(captureTitle); // QString name = captureR->captureName(); // if(name.contains("_CH2_",Qt::CaseInsensitive) == false) // { // name += "_CH2_"; // captureR->setCaptureName(name); // } connect(captureR,SIGNAL(saved(const QString&)),SLOT(onCaptureSavedRear(const QString&)),Qt::UniqueConnection); #if (CAPTURE_DEBUG) connect(captureR,SIGNAL(imageCaptured(const QImage&)),SLOT(onImageCaptured(const QImage&)),Qt::UniqueConnection); connect(captureR,SIGNAL(failed()),SLOT(onCaptureFailed()),Qt::UniqueConnection); #endif captureR->capture(); // 후방 캡쳐 시작 //updatePausedScreenRearOnly(); // 한번더 확인 #if (CAPTURE_DEBUG) g_errorMessage += "3-2.Start rear capture.\n"; qInfo() << "3-2.Start rear capture."; #endif } #if (CAPTURE_DEBUG) else { g_errorMessage += "3-2.Error rear capture.\n"; qInfo() << "3-2.Error rear capture."; } #endif } #endif } // 4. 후방 캡쳐 완료 #if !(FORCE_SINGLE_PLAYER || SINGLE_CH_VIEWER) void RMPlayer::onCaptureSavedRear(const QString& path) { // 연결 해제 if(_playerR != NULL) { FAV::VideoCapture* captureR = _playerR->videoCapture(); disconnect(captureR,SIGNAL(saved(const QString&)),this,SLOT(onCaptureSavedRear(const QString&))); } if(_captureFileList != NULL) { #if (CAPTURE_DEBUG) g_errorMessage += "4.Rear capture done. All OK!:" + path + "\n"; qInfo() << "4.Rear capture done. All OK!:" << path; #endif _captureFileList->append(path); // 파일명 추가 QApplication::restoreOverrideCursor(); // 커서 복구 clearCaptureWatcher(); // Watcher 제거 //qInfo() << __LINE__ << __FUNCTION__; emit videoCaptureDone(_captureFileList); // 시그널 } } #endif // 5. 캡쳐 Watcher 삭제 void RMPlayer::clearCaptureWatcher() { if(_captureWatcher != NULL) { _captureWatcher->stop(); delete _captureWatcher; _captureWatcher = NULL; } } #if (CAPTURE_DEBUG) void RMPlayer::onImageCaptured(const QImage& image) { Q_UNUSED(image); g_errorMessage += "999.image captured.\n"; qInfo() << "999.image captured."; } void RMPlayer::onCaptureFailed() { g_errorMessage += "999.image capture failed.\n"; qInfo() << "999.image capture failed."; } #endif // 캡쳐 DONE // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ // SEEK qint64 RMPlayer::ratioToSliderValue(qreal ratio) { //return _playerF->mediaStopPosition() * ratio; return _slider->maximum() * ratio; } void RMPlayer::on_sync_timer_restarted() { _sync_time = true; } #if (PENTA_CHANNEL) #define CHANNEL_PAUSE_MODE 1 void RMPlayer::onUpdateCHMode() { changeCHMode(RMApp::instance()->chMode); } void RMPlayer::changeCHMode(RMApp::ChannelMode mode) { RMApp::instance()->chMode = mode; // 채널 변경 시작 emit playEvent(PLAY_WILL_CHANGE_CH,_currentItem); // 이전 채널 확인 FAV::AVPlayer* oldF = _playerF; FAV::AVPlayer* oldR = _playerR; // 커넥션 해제 _clearPlayerConnection(); // NULL 처리시 문제가 발생하므로 _playerF = NULL;//_players[RMApp::ChannelFront]; _playerR = NULL;//_players[RMApp::ChannelRear]; // 이미 playOrPause 에서 play 된 상태로 호출됨 for(int i=0;i<5;i++) { #if (CHANNEL_PAUSE_MODE) //qInfo() << "PLAYER:" << i << _players[i]->isLoaded() << __FUNCTION__; if(_players[i]->isLoaded()) { if(!_players[i]->isPaused()) { //qInfo() << "PAUSE:" << i << __FUNCTION__; _players[i]->pause(true); } } #else // CHANNEL_PAUSE_MODE _players[i]->setSkipVideo(true); #endif // CHANNEL_PAUSE_MODE } switch(RMApp::instance()->chMode) { case RMApp::ChannelModeFR: _playerF = _players[RMApp::ChannelFront]; _playerR = _players[RMApp::ChannelRear]; break; case RMApp::ChannelModeLR: _playerF = _players[RMApp::ChannelLeft]; _playerR = _players[RMApp::ChannelRight]; break; case RMApp::ChannelModeFront: _playerF = _players[RMApp::ChannelFront]; break; case RMApp::ChannelModeRear: _playerF = _players[RMApp::ChannelRear]; break; case RMApp::ChannelModeLeft: _playerF = _players[RMApp::ChannelLeft]; break; case RMApp::ChannelModeRight: _playerF = _players[RMApp::ChannelRight]; break; case RMApp::ChannelModeSub: _playerF = _players[RMApp::ChannelSub]; break; } if(_playerF != NULL) { #if (CHANNEL_PAUSE_MODE) //qInfo() << "PLAYER LOADED:" << _playerF->isLoaded() << "PAUSED:" << _playerF->isPaused() << __FUNCTION__; // 현재 정지상태일 경우 if(!_pausedState && _playerF->isPaused()) { _playerF->pause(false); //qInfo() << "PLAY F2:" << _playerF << __FUNCTION__; } if(oldF != NULL && oldF != _playerF) { _playerF->setPosition(oldF->position()); } #else // CHANNEL_PAUSE_MODE _playerF->setSkipVideo(false); #endif // CHANNEL_PAUSE_MODE } if(_playerR != NULL) { #if (CHANNEL_PAUSE_MODE) if(!_pausedState && _playerR->isPaused()) { _playerR->pause(false); //qInfo() << "PLAY R2:" << _playerR << __FUNCTION__; } if(oldR != NULL && oldR != _playerR) { _playerR->setPosition(oldR->position()); } #else // CHANNEL_PAUSE_MODE _playerR->setSkipVideo(false); #endif // CHANNEL_PAUSE_MODE } // 사운드 처리 if(!_isMuted()) { if(oldF != NULL && _playerF != oldF) { oldF->audio()->setMute(true); //_players[RMApp::ChannelFront]->setAudioStream(-1); _playerF->audio()->setMute(false); _playerF->audio()->setVolume(oldF->audio()->volume()); oldF->audio()->setVolume(0); } // 후방은 무조건 끄기 if(_playerR != NULL && !_playerR->audio()->isMute()) { _playerR->audio()->setMute(true); _playerR->audio()->setVolume(0); } } // 비디오 장치 연결 _configureVideoRenderers(); //qInfo() << "PLAYERS:" << _players[0] << _players[1] << _players[2] << _players[3] << _players[4] << __FUNCTION__; // qInfo() << "F:" << _playerF << "R:" << _playerR << __FUNCTION__; // 커넥션 처리 _updatePlayerConnection(); if(_playerF != NULL) { _playerF->masterClock()->updateExternalClock(*_clock); } if(_playerR != NULL) { _playerR->masterClock()->updateExternalClock(*_clock); } // 채널변경 종료 emit playEvent(PLAY_DID_CHANGE_CH,_currentItem); // 필터 업데이트 _updateEQFilter(true); // FLIP 업데이트 updateFlip(false); if(_pausedState) { updatePausedScreen(); } } #endif void RMPlayer::clear_seek_timer() { // seek 종료시 일정시간 후 동기화 시작한다 if(_sync_restart_timer != NULL) { _sync_restart_timer->stop(); delete _sync_restart_timer; _sync_restart_timer = NULL; } _sync_time = false; } void RMPlayer::seek(qint64 pos,bool refresh) { //qInfo() << pos << __FUNCTION__ << __LINE__; #if (LIMIT_SEEK_END) if(pos > _playerF->duration() - 2000) { //qInfo() << pos << _playerF->duration() << __FUNCTION__ << __LINE__; pos = qMax((qint64)(_playerF->duration() - 2000),(qint64)0); } #endif // #if (SINGLE_CH_VIEWER || TOGGLE_PLAYER) if(_playerF->isPlaying() == false) #else if(_playerF->isPlaying() == false || (_playerR->isLoaded() == true && _playerR->isPlaying() == false)) #endif { //qInfo() << __FUNCTION__ << __LINE__; return; } #if (USE_LAST_PLAY_SEEK && !(PLAY_SEEK_FILE_MOVE)) // 최종 종료단계에 들어간 경우 처리하지 않는다. // 최종 종료단계로 이동할 경우 처리한다. (하지 않으면 이동하지 않는다.) if(_playerF->position()+ LAST_PLAY_SEEK_LIMIT > _playerF->duration()) { return; } #endif //#if (RM_MODEL != RM_MODEL_TYPE_KEIYO1) clear_seek_timer(); // 동기화 중지 _refreshSeeking = refresh; // 화면갱신을 위한.. #if (PENTA_CHANNEL) for(int i=0;i<5;i++) { if(_players[i]->isLoaded()) { _players[i]->setSeekType(VIDEO_SEEK_TYPE); _players[i]->seek(pos); } } #else // PENTA_CHANNEL _playerF->setSeekType(VIDEO_SEEK_TYPE); _playerF->seek(pos); #if !(SINGLE_CH_VIEWER || TOGGLE_PLAYER) if(_playerR->isLoaded()) { // 이거 왜하는지 모르겠음 if(_pausedState == false) { _playerR->pause(false); } _playerR->setSeekType(VIDEO_SEEK_TYPE); _playerR->seek(pos); } #endif // #if (SINGLE_CH_VIEWER) #if (TRI_CHANNEL) if(_playerI->isLoaded()) { // 이거 왜하는지 모르겠음 if(_pausedState == false) { _playerI->pause(false); } _playerI->setSeekType(VIDEO_SEEK_TYPE); _playerI->seek(pos); } #endif //#endif // RM_MODEL_TYPE_KEIYO1 //#if (MODEL_BBVIEWER) // // 처리하지 않으면 위치 클릭전 위치로 이동함. // _clock->updateExternalClock( pos ); // // 처리하지 않으면 seek 가 play seek 되면서 delay 발생함 // _playerF->masterClock()->updateExternalClock(*_clock); //#if !(FORCE_SINGLE_PLAYER) // _playerR->masterClock()->updateExternalClock(*_clock); //#endif //#endif // MODEL_BBVIEWER #endif // #else // PENTA_CHANNEL } void RMPlayer::onSliderMove(int value) { //LOG_TEST; _refreshSeeking = false; // 초기화 해야함 if(_playerF == NULL) { return; } if (_playerF->isLoaded()) { // Release 이후에 Slider Move 가 한번더 호출되어 _sliderMoving == true 처리하면 안됨 // 그러나 _sliderMoving 처리하지 않으면 다시 seek 발생하고 슬라이드 이동 재귀 호출됨 _sliderMoving = true; #if (PLAY_SYNC_FIX2) qint64 pos = value; #else qint64 pos = MAX(INITIAL_VIDEO_POSITION,value); #endif // 완전 뮤트 muteIfRequired(-1); if(_playerF->isPaused() == true) { // 정지된 상태에서만 클럭 업데이트 #if !(PLAY_SYNC_FIX2) _clock->updateExternalClock( pos ); #endif // PLAY_SYNC_FIX2 //qInfo() << "*** SLIDE MOVE" << pos << __FUNCTION__; _playerF->setPosition( pos ); #if !(FORCE_SINGLE_PLAYER || SINGLE_CH_VIEWER || TOGGLE_PLAYER) if(_playerR->isLoaded()) { _playerR->setPosition( pos ); } //qInfo() << __FUNCTION__ << "setPosition:" << pos; #endif } else { seek(pos,false); } _pausePosition = pos; // ??? // 정지 상태일 경우 지도 및 그래프는 업데이트 한다. -> 실제 정지 상태에서 onPositionChanged 가 발생함 //emit positionChanged(pos,_playerF->duration()); } } void RMPlayer::onSliderRelease() { //FLOG_OT << __FUNCTION__ << __LINE__; // mouse relese 이후에 move 가 한번더 발생하여 // _sliderMoving 이 다시 true 가 되어 정지됨 _sliderMoving = false; // SEEK 도중 PAUSE 처리 복구 // && _playerF->isPaused() 는 완전히 처리완료 되지 않은 경우가 있어 확인하지 않는다 if(_pauseWhileSeek ) { //qInfo() << "_pauseWhileSeek:" << _pauseWhileSeek<< __FUNCTION__; _pause(false,true); _pauseWhileSeek = false; } onUnMuteIfRequired(); sync_clock(); } void RMPlayer::onSliderPress(bool bKnob) { if(_playerF == NULL) { return; } //FLOG_OT << __FUNCTION__ << __LINE__; endStepMode(); RMSlider* slider = qobject_cast(QObject::sender()); if(slider == NULL) { return; } // 처리 도중에는 자동 파일 이동 방지 _sliderMoving = true; // 적용안됨??? bKnob = true; int sliderPos = slider->value(); // KNOB 가 아닐 경우 이후 이벤트 발생하지 않기 때문에 pause 등 처리할 필요 없음 #if (PLAY_SYNC_FIX2) // isPaused 는 player 내부에서 SEEK 중 일 경우 사용자 paused 상태에서도 풀릴 수 있음 if(bKnob == true && _pausedState == false) { #else if(bKnob == true && _playerF->isPaused() == false) { #endif _pauseWhileSeek = true; _pause(true,true); } // 클릭한 지점으로 이동 onSliderMove(sliderPos); // 클릭한 경우 released 가 호출되지 않기 때문에 _sliderMoving 바로 해제 if(bKnob == false) { _sliderMoving = false; // MUTE 도 바로 해제 onUnMuteIfRequired(); } } void RMPlayer::startSeekUpdateTimer() { // 기존 타이머 제거 stopSeekUpdateTimer(); _seekPressedTimer = new QTimer(this); _seekPressedTimer->setInterval(300); connect(_seekPressedTimer,SIGNAL(timeout()),SLOT(onSeekPressed())); _seekPressedTimer->start(); // pressed -> 중 업데이트 _seekUpdateTimer = new QTimer(this); _seekUpdateTimer->setInterval(500); connect(_seekUpdateTimer,SIGNAL(timeout()),SLOT(onSeekUpdate())); _seekUpdateTimer->start(); } void RMPlayer::stopSeekUpdateTimer() { if(_seekPressedTimer != NULL) { _seekPressedTimer->stop(); delete _seekPressedTimer; _seekPressedTimer = NULL; } if(_seekReleasedTimer != NULL) { _seekReleasedTimer->stop(); delete _seekReleasedTimer; _seekReleasedTimer = NULL; } if(_seekUpdateTimer != NULL) { _seekUpdateTimer->stop(); delete _seekUpdateTimer; _seekUpdateTimer = NULL; } _seekAcceleration = 1; } void RMPlayer::onSeekPressed() { if(_firstStep == true) // && _stepMode == STEP_MODE_B1 { return; } if(_seekPressedTimer != NULL) { _seekPressedTimer->setInterval(150); } int step = (_stepMode != STEP_MODE_B1) ? 1 : -1; qint64 pos = _stepPosition(); if(_isLastStep() && step > 0) { // 다음파일이 없을 경우 이후 동작하지 않는다 _playItem == NULL 로 처리해서 stopSeekUpdateTimer(); if(RMVideoFileList::instance()->isNextPlayItemExist(true)) { _onNaturalEnd = true; RMVideoFileList::instance()->onPlayNextVideo(-1); } return; } if(pos < 0 && step < 0) { stopSeekUpdateTimer(); if(RMVideoFileList::instance()->isNextPlayItemExist(false)) { // stepping 처리시 _onNaturalEnd = true; RMVideoFileList::instance()->onPlayPreviousVideo(-1); //qInfo() << "BACK"; return; } } pos = MAX(MIN(pos,_playerF->duration()),0); _slider->setValue(pos); // 처리하지 않으면 swap 처리시 문제가됨 _pausePosition = pos; //qInfo() << __FUNCTION__ << pos << ":" << _slider->value() << _playerF->position(); emit RMPlayer::instance()->positionChanged(pos,_playerF->duration()); } void RMPlayer::onSeekUpdate() { // 연속 클릭시 바로 업데이트 하지 않고 취소 할 수 있도록 처리 if(_firstStep == true) // && _stepMode == STEP_MODE_B1 { return; } qint64 pos = _slider->value(); //qInfo() << __FUNCTION__ << pos << _playerF->isLoaded() << _playerR->isLoaded() << _playerF->position() << _playerR->position(); seek(pos,false); #if (DO_NOT_USE_STEP_PAUSE) _stepMode = STEP_MODE_NONE; #endif // DO_NOT_USE_STEP_PAUSE //qInfo() << __FUNCTION__ << " POS:" << pos; } void RMPlayer::onSeekLastUpdate() { onSeekUpdate(); stopSeekUpdateTimer(); } void RMPlayer::onSeekForwardStart() { if(!_playerF->isLoaded()) { return; } _stepMode = STEP_MODE_F1; _firstStep = false; // 정지 #if !(DO_NOT_USE_STEP_PAUSE) if(_playerF->isPaused() == false) { _pause(true,true); // _pause 는 slider 등에서 잠깐 이동시에도 사용하기 때문에 // 직접 아이콘 변경 해야함 emit playEvent(PLAY_DID_PAUSED,_currentItem); } #endif // DO_NOT_USE_STEP_PAUSE // 최초 1회 실행 (슬라이드만 이동, 실제 SEEK 는 onSeekUpdate 에서 발생함) onSeekPressed(); startSeekUpdateTimer(); } void RMPlayer::onSeekBackwardStart() { if(!_playerF->isLoaded()) { return; } // 연속해서 press+release 반복시 마지막 업데이트 취소 stopSeekUpdateTimer(); _stepMode = STEP_MODE_B1; _firstStep = false; #if !(DO_NOT_USE_STEP_PAUSE) // 정지 if(_playerF->isPaused() == false) { _pause(true,true); // _pause 는 slider 등에서 잠깐 이동시에도 사용하기 때문에 // 직접 아이콘 변경 해야함 emit playEvent(PLAY_DID_PAUSED,_currentItem); } #endif // DO_NOT_USE_STEP_PAUSE onSeekPressed(); startSeekUpdateTimer(); } #if (PLAY_SYNC_FIX2) void RMPlayer::onSeekFrameBackwardStart() { _stepMode = STEP_MODE_BF; _firstStep = false; if(_playerF->isPaused() == false) { _pause(true,true); // _pause 는 slider 등에서 잠깐 이동시에도 사용하기 때문에 // 직접 아이콘 변경 해야함 emit playEvent(PLAY_DID_PAUSED,_currentItem); } else // 처리 해 주지 않으면 뒤로 돌아가서 시작함 { //qInfo() << __FUNCTION__ << _slider->value() << _playerF->position(); //_slider->setValue(_playerF->position()); } onSeekPressed(); startSeekUpdateTimer(); } #endif // #if (PLAY_SYNC_FIX2) void RMPlayer::onSeekFrameForwardStart() { _stepMode = STEP_MODE_FF; _firstStep = false; if(_playerF->isPaused() == false) { _pause(true,true); // _pause 는 slider 등에서 잠깐 이동시에도 사용하기 때문에 // 직접 아이콘 변경 해야함 emit playEvent(PLAY_DID_PAUSED,_currentItem); } else // 처리 해 주지 않으면 뒤로 돌아가서 시작함 { //qInfo() << __FUNCTION__ << _slider->value() << _playerF->position(); //_slider->setValue(_playerF->position()); } onSeekPressed(); startSeekUpdateTimer(); } // KEY/MOUSE RELEASE void RMPlayer::onSeekStepEnd() { // 모드 종료는 endStopMode() 에서 처리함.. stopSeekUpdateTimer(); _seekReleasedTimer = new QTimer(this); #if (FIX_PRESS_AND_RELEASE) // 연속해서 press+release 처리할 경우 임시로 해결 _seekReleasedTimer->setInterval(300); #else _seekReleasedTimer->setInterval(100); #endif _seekReleasedTimer->setSingleShot(true); connect(_seekReleasedTimer,SIGNAL(timeout()),SLOT(onSeekLastUpdate())); _seekReleasedTimer->start(); } quint64 RMPlayer::_stepPosition() { #if (SEEK_STEP_SIZE == 10) const double stepSize = 10000.0; #else const double stepSize = 1000.0; #endif qint64 nexPos = 0; if(_stepMode == STEP_MODE_F1 || _stepMode == STEP_MODE_B1) { int step = _stepMode == STEP_MODE_F1 ? 1 : -1; //double tolerance = 0; step *= _seekAcceleration; // 이전이동 + 마지막일 경우 if(step < 0 && _isLastStep()) { //tolerance = 100; } #if (SEEK_SIMPLE_STEP) // 단순이동 모드 if(_stepMode == STEP_MODE_F1) { nexPos = MIN((_slider->value() + stepSize),_playerF->duration()); } else { nexPos = MAX((_slider->value() - stepSize),0); } #else // SEEK_SIMPLE_STEP int currentIndex = (int)(((double)_slider->value()) / stepSize); currentIndex += step; nexPos = (qint64)(((double)currentIndex) * stepSize); if(fabs( (double)(nexPos-_slider->value())) < 100) { nexPos = (qint64)(((double)currentIndex + step) * stepSize); } // 다음이 마지막 STEP 일 경우 영상 마지막으로 이동 if(step > 0 && (_playerF->duration() - nexPos) < 500) { nexPos = _playerF->duration();//_durationAddPTS; } #endif // #else // SEEK_SIMPLE_STEP //qInfo() << nexPos; } else if (_stepMode == STEP_MODE_FF) { nexPos = MIN((_slider->value() + STEP_SEEK_DURATION),_playerF->duration()); //qInfo() << __FUNCTION__ << nexPos << _playerF->position() << _slider->value(); } #if (PLAY_SYNC_FIX2) else if (_stepMode == STEP_MODE_BF) { nexPos = MAX((_slider->value() - STEP_SEEK_DURATION),0); //qInfo() << __FUNCTION__ << nexPos << _playerF->position() << _slider->value(); } #endif // #if (PLAY_SYNC_FIX2) _seekAcceleration = MIN(_seekAcceleration+1,3); return nexPos; } bool RMPlayer::_isLastStep() { if(_stepMode != STEP_MODE_FF) { return (_playerF->duration() - _slider->value()) < 500; } return (_playerF->duration() - _slider->value()) < 1; } void RMPlayer::onPlayRestart() { _clock->updateExternalClock(0); seek(0,false); } void RMPlayer::seekFrontOnly(qint64 pos,bool refresh) { _refreshSeeking = refresh; // 화면갱신을 위한.. if(_playerF->isLoaded()) { _playerF->seek(pos); } } #if !(FORCE_SINGLE_PLAYER || SINGLE_CH_VIEWER) void RMPlayer::seekRearOnly(qint64 pos,bool refresh) { _refreshSeeking = refresh; // 화면갱신을 위한.. if(_playerR->isLoaded()) { _playerR->seek(pos); } } #endif #if (CAPTURE_DEBUG) void RMPlayer::onSeekCapture() { requestVideoCapture(); } #endif void RMPlayer::onSpeedSliderMoved(int position) { RMSlider* slider = qobject_cast(QObject::sender()); if(slider != NULL) { if(position >= 0 && position < PLAY_SPEED_COUNT) { #if !(DEBUG_HIGH_SPEED_STOP) _speedValue = g_PlaySpeedList[position]; #endif if(_speedLabel != NULL) { _speedLabel->setText(g_PlaySpeedNames.at(position)); } } } } void RMPlayer::onSpeedSliderReleased() { RMSlider* slider = qobject_cast(QObject::sender()); if(slider != NULL) { int index = slider->value(); if(index >= 0 && index < PLAY_SPEED_COUNT) { _speedValue = g_PlaySpeedList[index]; if(_speedLabel != NULL) { _speedLabel->setText(g_PlaySpeedNames.at(slider->value())); } updateSpeed(_speedValue); } } } #if !(SINGLE_CH_VIEWER) #if (TOGGLE_PLAYER) void RMPlayer::toggleSwap(bool forceEmit) { //qInfo() << "TOGGLE SWAP:" << LOG_FL; // 로딩되지 않은 상태에서도 처리 if(_eventBlocking == true) { return; } #if !(DO_NOT_USE_ZOOM) if(_videoOutputZOOMMain != NULL) { _playerF->removeVideoRenderer(_videoOutputZOOMMain); } #endif if(forceEmit == false) { blockEvents(200); } disconnect(_playerF,SIGNAL(positionChanged(qint64)),this,SLOT(onPositionChanged(qint64))); disconnect(_playerF,SIGNAL(firstFrameNotify(qreal)),this,SLOT(onFirstFrame(qreal))); disconnect(_playerF,SIGNAL(error(const FAV::AVError&)),this,SLOT(onPlayerF(const FAV::AVError&))); _swaped = !_swaped; bool isPaused = _playerF->isPaused(); // Player SWAP FAV::AVPlayer* pt = _playerR; _playerR = _playerF; _playerF = pt; // 전방을 후방에 표시 if(_playerF->isLoaded()) { // 2021/04/28-> 1000->100 으로 변경 _playerF->setPosition(qMax((qint64)(_playerR->position()-100),(qint64)0)); } _playerF->setRenderer(_videoOutputF); _playerR->removeVideoRenderer(_videoOutputF); if(_playerF->isLoaded()) { _playerF->pause(isPaused); _playerR->pause(true); _playerF->masterClock()->updateExternalClock(*_clock); _playerR->masterClock()->updateExternalClock(*_clock); } connect(_playerF, SIGNAL(positionChanged(qint64)), SLOT(onPositionChanged(qint64))); connect(_playerF,SIGNAL(firstFrameNotify(qreal)),this,SLOT(onFirstFrame(qreal))); connect(_playerF,SIGNAL(error(const FAV::AVError&)), SLOT(onPlayerF(const FAV::AVError&))); //qInfo() << "SET POSITION:" << _playerR->position() << __FUNCTION__; #if !(DO_NOT_USE_ZOOM) if(_videoOutputZOOMMain != NULL) { _playerF->addVideoRenderer(_videoOutputZOOMMain); } #endif _playerF->audio()->setMute(false); _playerR->audio()->setMute(true); updatePausedScreen(); emit playEvent(_swaped ? PLAY_DID_SWAPPED : PLAY_DID_UNSWAPPED, _currentItem); } #else // TOGGLE_PLAYER void RMPlayer::toggleSwap(bool forceEmit) { // 로딩되지 않은 상태에서도 처리 if(_eventBlocking == true) { return; } #if !(DO_NOT_USE_ZOOM) if(_videoOutputZOOMMain != NULL) { mainScreenPlayer()->removeVideoRenderer(_videoOutputZOOMMain); } if(_videoOutputZOOMSub != NULL) { subScreenPlayer()->removeVideoRenderer(_videoOutputZOOMSub); } #endif if(forceEmit == false) { blockEvents(200); } _swaped = !_swaped; //qInfo() << "_swaped:" << _swaped << __FUNCTION__; if(_isSwaped()) { #if (RM_MODEL_360) //_videoOutputF->widget()->setHidden(true); //_videoOutputR->widget()->setHidden(true); // 현재 모드/각도 저장 후 복원 FAV::OpenGLVideo::RENDER_INFO_360 fi = {0,}; FAV::OpenGLVideo::RENDER_INFO_360 ri = {0,}; fi.aspectMode = (int)_videoOutputF->outAspectRatioMode(); fi.sourceAspectRatio = _videoOutputF->sourceAspectRatio(); //qInfo() << _videoOutputF->outAspectRatioMode(); ri.aspectMode = (int)_videoOutputR->outAspectRatioMode(); ri.sourceAspectRatio = _videoOutputR->sourceAspectRatio(); _videoOutputF->opengl()->getRenderInfo(&fi); _videoOutputR->opengl()->getRenderInfo(&ri); // 전환시 모드 리셋! fi.mode = _videoOutputF->opengl()->mode(); // 전방 FISHEYE ri.mode = 0; #endif // 전방을 후방에 표시 _playerF->setRenderer(_videoOutputR); // 전방 파일이 존재하지 않을 경우 #if (FORCE_2CH) if(_currentItem != NULL && _currentItem->filePath.length() == 0) #else if(_currentItem != NULL && (_currentItem->filePath.length() == 0 || _currentItem->filePathCH2.length() == 0)) #endif { _videoOutputF->receive(FAV::VideoFrame()); // clear } else { #if (TRI_CHANNEL) if(isIndoor()) { _playerI->setRenderer(_videoOutputF); } else { _playerR->setRenderer(_videoOutputF); } #else // TRI_CHANNEL _playerR->setRenderer(_videoOutputF); #endif // TRI_CHANNEL } #if (RM_MODEL_360) //qInfo() << "----------------------- SWAP F-----------------------"; _videoOutputR->opengl()->setRenderInfo(&fi); _videoOutputR->setOutAspectRatioMode((FAV::VideoRenderer::OutAspectRatioMode)fi.aspectMode); _videoOutputR->setSourceAspectRatio(fi.sourceAspectRatio); //qInfo() << "----------------------- R -----------------------"; _videoOutputF->opengl()->setRenderInfo(&ri); _videoOutputF->setOutAspectRatioMode((FAV::VideoRenderer::OutAspectRatioMode)ri.aspectMode); _videoOutputF->setSourceAspectRatio(ri.sourceAspectRatio); //_videoOutputF->widget()->setHidden(false); //_videoOutputR->widget()->setHidden(false); #endif } else { #if (RM_MODEL_360) FAV::OpenGLVideo::RENDER_INFO_360 fi = {0,}; FAV::OpenGLVideo::RENDER_INFO_360 ri = {0,}; fi.aspectMode = (int)_videoOutputR->outAspectRatioMode(); fi.sourceAspectRatio = _videoOutputR->sourceAspectRatio(); ri.aspectMode = (int)_videoOutputF->outAspectRatioMode(); ri.sourceAspectRatio = _videoOutputF->sourceAspectRatio(); _videoOutputR->opengl()->getRenderInfo(&fi); _videoOutputF->opengl()->getRenderInfo(&ri); ri.mode = 0; #endif _playerF->setRenderer(_videoOutputF); // 후방 파일이 존재하지 않을 경우 #if (FORCE_2CH) if(_currentItem != NULL && _currentItem->filePath.length() == 0) #else if(_currentItem != NULL && (_currentItem->filePath.length() == 0 || _currentItem->filePathCH2.length() == 0)) #endif { _videoOutputR->receive(FAV::VideoFrame()); } else { #if (TRI_CHANNEL) if(isIndoor()) { _playerI->setRenderer(_videoOutputR); } else { _playerR->setRenderer(_videoOutputR); } #else // TRI_CHANNEL _playerR->setRenderer(_videoOutputR); // clear #endif // TRI_CHANNEL } #if (RM_MODEL_360) //qInfo() << "----------------------- RESTORE F-----------------------"; _videoOutputF->opengl()->setRenderInfo(&fi); _videoOutputF->setSourceAspectRatio(fi.sourceAspectRatio); _videoOutputF->setOutAspectRatioMode((FAV::VideoRenderer::OutAspectRatioMode)fi.aspectMode); //qInfo() << "----------------------- RESTORE R-----------------------"; _videoOutputR->opengl()->setRenderInfo(&ri); _videoOutputR->setSourceAspectRatio(ri.sourceAspectRatio); _videoOutputR->setOutAspectRatioMode((FAV::VideoRenderer::OutAspectRatioMode)ri.aspectMode); #endif } #if !(DO_NOT_USE_ZOOM) if(_videoOutputZOOMMain != NULL) { mainScreenPlayer()->addVideoRenderer(_videoOutputZOOMMain); } if(_videoOutputZOOMSub != NULL) { subScreenPlayer()->addVideoRenderer(_videoOutputZOOMSub); } #endif #if (RM_MODEL_EMT_KR) int m0 = _videoOutputR->opengl()->mode(); _videoOutputR->opengl()->setMode(_videoOutputF->opengl()->mode()); _videoOutputF->opengl()->setMode(m0); #endif // #if (RM_MODEL_EMT_KR) #if (USE_SHADER_FLIP) updateFlip(true); updateFlip(false); #endif // USE_SHADER_FLIP updatePausedScreen(); emit playEvent(_swaped ? PLAY_DID_SWAPPED : PLAY_DID_UNSWAPPED, _currentItem); } #endif // TOGGLE_PLAYER #endif // #if !(SINGLE_CH_VIEWER) #if (TRI_CHANNEL) void RMPlayer::toggleIndoor(bool forceEmit) { // 로딩되지 않은 상태에서도 처리 if(_eventBlocking == true) { return; } #if !(DO_NOT_USE_ZOOM) if(_videoOutputZOOMMain != NULL) { mainScreenPlayer()->removeVideoRenderer(_videoOutputZOOMMain); } if(_videoOutputZOOMSub != NULL) { subScreenPlayer()->removeVideoRenderer(_videoOutputZOOMSub); } #endif if(forceEmit == false) { blockEvents(200); } _ch3mode = !_ch3mode; if(_ch3mode) { // 전방을 후방에 표시 _playerI->setRenderer(_isSwaped() ? _videoOutputF : _videoOutputR); // 전방 파일이 존재하지 않을 경우 if(_currentItem != NULL && _currentItem->filePathCH3.length() == 0) { _videoOutputR->receive(FAV::VideoFrame()); // clear } else { _playerR->setRenderer(NULL); } } else { _playerI->setRenderer(NULL); // 전방 파일이 존재하지 않을 경우 if(_currentItem != NULL && (_currentItem->filePathCH2.length() == 0)) { _videoOutputR->receive(FAV::VideoFrame()); } else { _playerR->setRenderer(_isSwaped() ? _videoOutputF : _videoOutputR); // clear } } #if !(DO_NOT_USE_ZOOM) if(_videoOutputZOOMMain != NULL) { mainScreenPlayer()->addVideoRenderer(_videoOutputZOOMMain); } if(_videoOutputZOOMSub != NULL) { subScreenPlayer()->addVideoRenderer(_videoOutputZOOMSub); } #endif updatePausedScreen(); emit playEvent(_ch3mode ? PLAY_DID_INDOOR_CH : PLAY_DID_OUTDOOR_CH, NULL); } #elif (TRI_CHANNEL2) void RMPlayer::toggleIndoor(bool forceEmit) { Q_UNUSED(forceEmit) if(_currentItem->realCHCount() < 3){ return; } _ch3mode = !_ch3mode; _playerR->setVideoStream(_ch3mode ? 2 : 1); // 정지시에도 업데이트 if(_playerF->isPaused()) { _playerR->setPosition(_playerF->position()); } //qInfo() << "_ch3mode:" << _ch3mode << __FUNCTION__; emit playEvent(_ch3mode ? PLAY_DID_INDOOR_CH : PLAY_DID_OUTDOOR_CH, NULL); } #endif // TRI_CHANNEL #if (SUPPORT_WIDE_MODE) bool RMPlayer::itemIsWideMode() { return _currentItem != NULL && _currentItem->isWideMode(); } bool RMPlayer::isWideMode() { if(_currentItem == NULL || !_currentItem->isWideMode()){ return false; } FAV::VideoRenderer *t = _currentVideoRenderer(playerR()); return (t->opengl()->mode() == 1); } void RMPlayer::toggleWide() // NORMAL <-> WIDE { if(!_currentItem->isWideMode()){ return; } FAV::VideoRenderer *t = _currentVideoRenderer(playerR()); int mode = t->opengl()->mode(); t->opengl()->setMode(mode == 1 ? 0 : 1); // RendererAspectRatio //Use renderer's aspect ratio, i.e. stretch to fit the renderer rect // , VideoAspectRatio //Use video's aspect ratio and align center in renderer. // , CustomAspectRation //Use the ratio set by setOutAspectRatio(qreal). Mode will be set to this if that function is called // 정지시에도 업데이트 t->widget()->update(); // if(_playerF->isPaused()) { // _playerR->setPosition(_playerF->position()); // } } #endif // RM_MODEL_EMT_KR void RMPlayer::blockEvents(int msec,bool b_show_indicator) { #if !(H265_SUPPORT) Q_UNUSED(b_show_indicator) #endif _eventBlocking = true; if (_blockTimer != NULL) { _blockTimer->stop(); delete _blockTimer; _blockTimer = NULL; } _blockTimer = new QTimer(this); _blockTimer->setSingleShot(true); _blockTimer->setInterval(msec); connect(_blockTimer,SIGNAL(timeout()),SLOT(onReleaseBlockEvent())); _blockTimer->start(); #if (H265_SUPPORT) if (b_show_indicator == true) { emit show_indicator(true); } #endif //QTimer::singleShot(msec,this, SLOT(onReleaseBlockEvent())); } void RMPlayer::onReleaseBlockEvent() { _eventBlocking = false; if (_blockTimer != NULL) { _blockTimer->stop(); delete _blockTimer; _blockTimer = NULL; } #if (H265_SUPPORT) emit show_indicator(false); #endif } QDateTime RMPlayer::currentTime(double* lon,double* lat) { if(!_playerF->isLoaded()) { return QDateTime(); } double ratio = ((double)_playerF->position()) / ((double)_playerF->duration()); return _currentItem->dateTimeInPosition(ratio,lat,lon); } // 타이머 void RMPlayer::timerEvent(QTimerEvent *e) { if (e->timerId() != _timer_id) { return; } sync_clock(); //qInfo() << "P:" << _playerF->position() << "D:" << _playerF->duration(); } void RMPlayer::sync_clock() { if (_sync_time == false) { return; } #if (PLAY_SYNC_FIX2) return; #endif // // seek 직후 clock 을 update 해버리면 seek 와 clock 이 delay / diff 발생 => 비디오 타이밍 매칭 하기위해 정지함 // 하지만 고속 플레이 등을 위해서는 정기적으로 동기화가 필요함.... if (!_clock->isActive()) { return; } // if(_clock->value() >= _playerF->durationF()) // { // _clock->pause(true); // _clock->updateValue(_playerF->durationF()); // } // 화면깨짐 방지 //#if (RM_MODEL == RM_MODEL_TYPE_KEIYO1) //qInfo() << "$$$SYNC CLOCK" << _clock->value() << __FUNCTION__; _playerF->masterClock()->updateExternalClock(*_clock); // if(_playerR->isLoaded()) { // _playerR->masterClock()->updateExternalClock(*_clock); // } FLOG_OT << "TIME SYNC:" << _clock->value() << FLOGE; //#endif } #if (RM_TESTING) void RMPlayer::applyTestFilter(QString filter) { if(_testFilterF != NULL) { _testFilterF->uninstall(); delete _testFilterF; _testFilterF = NULL; } if(filter.length() > 0) { _testFilterF = new FAV::LibAVFilterVideo(_playerF); _testFilterF->installTo(_playerF); _testFilterF->setOptions(filter); } updatePausedScreenFrontOnly(); } #endif