diff --git a/plugins/channeltx/modatv/atvmod.cpp b/plugins/channeltx/modatv/atvmod.cpp index f0a4f1e44..8da51a96a 100644 --- a/plugins/channeltx/modatv/atvmod.cpp +++ b/plugins/channeltx/modatv/atvmod.cpp @@ -37,7 +37,11 @@ ATVMod::ATVMod() : m_settingsMutex(QMutex::Recursive), m_horizontalCount(0), m_lineCount(0), - m_imageOK(false) + m_imageOK(false), + m_videoFPSq(1.0f), + m_videoFPSCount(0.0f), + m_videoPrevFPSCount(0), + m_videoOK(false) { setObjectName("ATVMod"); @@ -59,6 +63,7 @@ ATVMod::ATVMod() : ATVMod::~ATVMod() { + if (m_video.isOpened()) m_video.release(); } void ATVMod::configure(MessageQueue* messageQueue, @@ -171,10 +176,48 @@ void ATVMod::pullVideo(Real& sample) m_lineCount++; if (m_lineCount > (m_nbLines/2)) m_evenImage = !m_evenImage; } - else + else // new image { m_lineCount = 0; m_evenImage = !m_evenImage; + + if (m_running.m_atvModInput == ATVModInputVideo) + { + int grabOK; + int fpsIncrement = (int) m_videoFPSCount - m_videoPrevFPSCount; + + // move a number of frames according to increment + // use grab to test for EOF then retrieve to preserve last valid frame as the current original frame + // TODO: handle pause (no move) + for (int i = 0; i < fpsIncrement; i++) + { + grabOK = m_video.grab(); + if (!grabOK) break; + } + + if (grabOK) + { + cv::Mat colorFrame; + m_video.retrieve(colorFrame); + cv::cvtColor(colorFrame, m_frameOriginal, CV_BGR2GRAY); + resizeVideo(); + } + else + { + // TODO: handle play loop + } + + if (m_videoFPSCount < m_videoFPS) + { + m_videoPrevFPSCount = (int) m_videoFPSCount; + m_videoFPSCount += m_videoFPSq; + } + else + { + m_videoPrevFPSCount = 0; + m_videoFPSCount = m_videoFPSq; + } + } } m_horizontalCount = 0; @@ -342,7 +385,7 @@ void ATVMod::applyStandard() m_nbImageLines2 = 255; m_interlaced = true; m_nbHorizPoints = 64 * m_pointsPerTU; // full line - m_nbSyncLinesH = 5; + m_nbSyncLinesHead = 5; m_nbBlankLines = 15; // yields 480 lines (255 - 15) * 2 m_pointsPerHBar = m_pointsPerImgLine / m_nbBars; m_linesPerVBar = m_nbImageLines2 / m_nbBars; @@ -364,7 +407,7 @@ void ATVMod::applyStandard() m_nbImageLines2 = 305; m_interlaced = true; m_nbHorizPoints = 64 * m_pointsPerTU; // full line - m_nbSyncLinesH = 5; + m_nbSyncLinesHead = 5; m_nbBlankLines = 17; // yields 576 lines (305 - 17) * 2 m_pointsPerHBar = m_pointsPerImgLine / m_nbBars; m_linesPerVBar = m_nbImageLines2 / m_nbBars; @@ -377,6 +420,12 @@ void ATVMod::applyStandard() { resizeImage(); } + + if (m_videoOK) + { + calculateVideoSizes(); + resizeVideo(); + } } void ATVMod::openImage(const QString& fileName) @@ -392,6 +441,8 @@ void ATVMod::openImage(const QString& fileName) void ATVMod::openVideo(const QString& fileName) { + //if (m_videoOK && m_video.isOpened()) m_video.release(); should be done by OpenCV in open method + m_videoOK = m_video.open(qPrintable(fileName)); if (m_videoOK) @@ -400,6 +451,7 @@ void ATVMod::openVideo(const QString& fileName) m_videoWidth = (int) m_video.get(CV_CAP_PROP_FRAME_WIDTH); m_videoHeight = (int) m_video.get(CV_CAP_PROP_FRAME_HEIGHT); qDebug("ATVMod::openVideo(: FPS: %f size: %d x %d", m_videoFPS, m_videoWidth, m_videoHeight); + calculateVideoSizes(); } } @@ -410,3 +462,19 @@ void ATVMod::resizeImage() cv::resize(m_imageOriginal, m_image, cv::Size(), fx, fy); qDebug("ATVMod::resizeImage: %d x %d -> %d x %d", m_imageOriginal.cols, m_imageOriginal.rows, m_image.cols, m_image.rows); } + +void ATVMod::calculateVideoSizes() +{ + m_videoFy = (m_nbImageLines - 2*m_nbBlankLines) / (float) m_videoHeight; + m_videoFx = m_pointsPerImgLine / (float) m_videoWidth; + m_videoFPSq = m_videoFPS / m_fps; + + qDebug("ATVMod::resizeVideo: factors: %f x %f FPSq: %f", m_videoFx, m_videoFy, m_videoFPSq); +} + +void ATVMod::resizeVideo() +{ + if (!m_frameOriginal.empty()) { + cv::resize(m_frameOriginal, m_frame, cv::Size(), m_videoFx, m_videoFy); // resize current frame + } +} diff --git a/plugins/channeltx/modatv/atvmod.h b/plugins/channeltx/modatv/atvmod.h index 558e2bc3a..bcf9a520d 100644 --- a/plugins/channeltx/modatv/atvmod.h +++ b/plugins/channeltx/modatv/atvmod.h @@ -219,7 +219,7 @@ private: uint32_t m_nbImageLines; //!< number of image lines excluding synchronization lines uint32_t m_nbImageLines2; //!< same number as above (non interlaced) or half the number above (interlaced) uint32_t m_nbHorizPoints; //!< number of line points per horizontal line - uint32_t m_nbSyncLinesH; //!< number of header sync lines + uint32_t m_nbSyncLinesHead; //!< number of header sync lines uint32_t m_nbBlankLines; //!< number of lines in a frame (full or half) that are blanked (black) at the top of the image float m_hBarIncrement; //!< video level increment at each horizontal bar increment float m_vBarIncrement; //!< video level increment at each vertical bar increment @@ -240,10 +240,16 @@ private: bool m_imageOK; cv::VideoCapture m_video; //!< current video capture - cv::Mat m_frame; //!< current frame - float m_videoFPS; - int m_videoWidth; - int m_videoHeight; + cv::Mat m_frameOriginal; //!< current frame from video + cv::Mat m_frame; //!< current displayable video frame + float m_videoFPS; //!< current video FPS rate + int m_videoWidth; //!< current video frame width + int m_videoHeight; //!< current video frame height + float m_videoFx; //!< current video horizontal scaling factor + float m_videoFy; //!< current video vertictal scaling factor + float m_videoFPSq; //!< current video FPS sacaling factor + float m_videoFPSCount; //!< current video FPS fractional counter + int m_videoPrevFPSCount; //!< current video FPS previous integer counter bool m_videoOK; static const float m_blackLevel; @@ -260,6 +266,8 @@ private: void openImage(const QString& fileName); void openVideo(const QString& fileName); void resizeImage(); + void calculateVideoSizes(); + void resizeVideo(); inline void pullImageLine(Real& sample) { @@ -276,7 +284,7 @@ private: int pointIndex = m_horizontalCount - (m_pointsPerSync + m_pointsPerBP); int iLine = m_lineCount % m_nbLines2; int oddity = m_lineCount < m_nbLines2 ? 0 : 1; - int iLineImage = iLine - m_nbSyncLinesH - m_nbBlankLines; + int iLineImage = iLine - m_nbSyncLinesHead - m_nbBlankLines; switch(m_running.m_atvModInput) { @@ -296,16 +304,41 @@ private: sample = ((iLine -5) / (float) m_nbImageLines2) * m_spanLevel + m_blackLevel; break; case ATVModInputImage: - if (!m_imageOK || (iLineImage < 0)) + if (!m_imageOK || (iLineImage < 0) || m_image.empty()) { sample = m_spanLevel * m_running.m_uniformLevel + m_blackLevel; } else { - unsigned char pixv = m_image.at(2*iLineImage+ oddity, pointIndex); // row (y), col (x) + unsigned char pixv; + + if (m_interlaced) { + pixv = m_image.at(2*iLineImage + oddity, pointIndex); // row (y), col (x) + } else { + pixv = m_image.at(iLineImage, pointIndex); // row (y), col (x) + } + sample = (pixv / 256.0f) * m_spanLevel + m_blackLevel; } break; + case ATVModInputVideo: + if (!m_videoOK || (iLineImage < 0) || m_frame.empty()) + { + sample = m_spanLevel * m_running.m_uniformLevel + m_blackLevel; + } + else + { + unsigned char pixv; + + if (m_interlaced) { + pixv = m_frame.at(2*iLineImage + oddity, pointIndex); // row (y), col (x) + } else { + pixv = m_frame.at(iLineImage, pointIndex); // row (y), col (x) + } + + sample = (pixv / 256.0f) * m_spanLevel + m_blackLevel; + } + break; case ATVModInputUniform: default: sample = m_spanLevel * m_running.m_uniformLevel + m_blackLevel; diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp index 945a5b389..a0c758c33 100644 --- a/plugins/channeltx/modatv/atvmodgui.cpp +++ b/plugins/channeltx/modatv/atvmodgui.cpp @@ -69,7 +69,7 @@ void ATVModGUI::resetToDefaults() { blockApplySettings(true); - ui->rfBW->setValue(12); + ui->rfBW->setValue(10); ui->uniformLevel->setValue(35); ui->volume->setValue(10); ui->standard->setCurrentIndex(0); diff --git a/plugins/channeltx/modatv/atvmodgui.ui b/plugins/channeltx/modatv/atvmodgui.ui index 06c6c157f..c5eb1ffa0 100644 --- a/plugins/channeltx/modatv/atvmodgui.ui +++ b/plugins/channeltx/modatv/atvmodgui.ui @@ -390,6 +390,11 @@ Image + + + Video + +