diff --git a/src/AnalyzeView/GeoTagController.cc b/src/AnalyzeView/GeoTagController.cc index 2db65e7..3f0627d 100644 --- a/src/AnalyzeView/GeoTagController.cc +++ b/src/AnalyzeView/GeoTagController.cc @@ -70,8 +70,7 @@ void GeoTagController::startTagging(void) QDir imageDirectory = QDir(_worker.imageDirectory()); if(!imageDirectory.exists()) { - _errorMessage = tr("Cannot find the image directory"); - emit errorMessageChanged(_errorMessage); + _setErrorMessage(tr("Cannot find the image directory")); return; } if(_worker.saveDirectory() == "") { @@ -83,23 +82,20 @@ void GeoTagController::startTagging(void) msgBox.setWindowModality(Qt::ApplicationModal); msgBox.addButton(tr("Replace"), QMessageBox::ActionRole); if (msgBox.exec() == QMessageBox::Cancel) { - _errorMessage = tr("Images have already been tagged"); - emit errorMessageChanged(_errorMessage); + _setErrorMessage(tr("Images have already been tagged")); return; } QDir oldTaggedFolder = QDir(_worker.imageDirectory() + "/TAGGED"); oldTaggedFolder.removeRecursively(); if(!imageDirectory.mkdir(_worker.imageDirectory() + "/TAGGED")) { - _errorMessage = tr("Couldn't replace the previously tagged images"); - emit errorMessageChanged(_errorMessage); + _setErrorMessage(tr("Couldn't replace the previously tagged images")); return; } } } else { QDir saveDirectory = QDir(_worker.saveDirectory()); if(!saveDirectory.exists()) { - _errorMessage = tr("Cannot find the save directory"); - emit errorMessageChanged(_errorMessage); + _setErrorMessage(tr("Cannot find the save directory")); return; } saveDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); @@ -115,15 +111,13 @@ void GeoTagController::startTagging(void) msgBox.setWindowModality(Qt::ApplicationModal); msgBox.addButton(tr("Replace"), QMessageBox::ActionRole); if (msgBox.exec() == QMessageBox::Cancel) { - _errorMessage = tr("Save folder not empty"); - emit errorMessageChanged(_errorMessage); + _setErrorMessage(tr("Save folder not empty")); return; } foreach(QString dirFile, imageList) { if(!saveDirectory.remove(dirFile)) { - _errorMessage = tr("Couldn't replace the existing images"); - emit errorMessageChanged(_errorMessage); + _setErrorMessage(tr("Couldn't replace the existing images")); return; } } @@ -144,6 +138,13 @@ void GeoTagController::_workerError(QString errorMessage) emit errorMessageChanged(errorMessage); } + +void GeoTagController::_setErrorMessage(const QString& error) +{ + _errorMessage = error; + emit errorMessageChanged(error); +} + GeoTagWorker::GeoTagWorker(void) : _cancel(false) , _logFile("") @@ -210,9 +211,10 @@ void GeoTagWorker::run(void) // Instantiate appropriate parser _triggerList.clear(); bool parseComplete = false; - if(isULog) { + QString errorString; + if (isULog) { ULogParser parser; - parseComplete = parser.getTagsFromLog(log, _triggerList); + parseComplete = parser.getTagsFromLog(log, _triggerList, errorString); } else { PX4LogParser parser; @@ -227,7 +229,8 @@ void GeoTagWorker::run(void) return; } else { qCDebug(GeotaggingLog) << "Log parsing failed"; - emit error(tr("Log parsing failed - tagging cancelled")); + errorString = tr("%1 - tagging cancelled").arg(errorString.isEmpty() ? tr("Log parsing failed") : errorString); + emit error(errorString); return; } } diff --git a/src/AnalyzeView/GeoTagController.h b/src/AnalyzeView/GeoTagController.h index aee58f6..f3955d2 100644 --- a/src/AnalyzeView/GeoTagController.h +++ b/src/AnalyzeView/GeoTagController.h @@ -117,8 +117,9 @@ signals: void errorMessageChanged (QString errorMessage); private slots: - void _workerProgressChanged(double progress); - void _workerError(QString errorMsg); + void _workerProgressChanged (double progress); + void _workerError (QString errorMsg); + void _setErrorMessage (const QString& error); private: QString _errorMessage; diff --git a/src/AnalyzeView/GeoTagPage.qml b/src/AnalyzeView/GeoTagPage.qml index d4f4386..c64147e 100644 --- a/src/AnalyzeView/GeoTagPage.qml +++ b/src/AnalyzeView/GeoTagPage.qml @@ -124,8 +124,9 @@ AnalyzePage { } QGCButton { - text: geoController.inProgress ? qsTr("Cancel Tagging") : qsTr("Start Tagging") - width: ScreenTools.defaultFontPixelWidth * 30 + text: geoController.inProgress ? qsTr("Cancel Tagging") : qsTr("Start Tagging") + width: ScreenTools.defaultFontPixelWidth * 30 + onClicked: { if (geoController.inProgress) { geoController.cancelTagging() diff --git a/src/AnalyzeView/LogDownloadController.cc b/src/AnalyzeView/LogDownloadController.cc index 133a556..3eeb02a 100644 --- a/src/AnalyzeView/LogDownloadController.cc +++ b/src/AnalyzeView/LogDownloadController.cc @@ -321,7 +321,7 @@ LogDownloadController::_logData(UASInterface* uas, uint32_t ofs, uint16_t id, ui if(ofs <= _downloadData->entry->size()) { const uint32_t chunk = ofs / kChunkSize; if (chunk != _downloadData->current_chunk) { - qWarning() << "Ignored packet for out of order chunk" << chunk; + qWarning() << "Ignored packet for out of order chunk actual:expected" << chunk << _downloadData->current_chunk; return; } const uint16_t bin = (ofs - chunk*kChunkSize) / MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN; diff --git a/src/AnalyzeView/LogDownloadPage.qml b/src/AnalyzeView/LogDownloadPage.qml index f2b684d..4b3d35a 100644 --- a/src/AnalyzeView/LogDownloadPage.qml +++ b/src/AnalyzeView/LogDownloadPage.qml @@ -155,7 +155,7 @@ AnalyzePage { fileDialog.qgcView = logDownloadPage fileDialog.title = qsTr("Select save directory") fileDialog.selectExisting = true - fileDialog.folder = QGroundControl.settingsManager.appSettings.telemetrySavePath + fileDialog.folder = QGroundControl.settingsManager.appSettings.logSavePath fileDialog.selectFolder = true fileDialog.openForLoad() } diff --git a/src/AnalyzeView/ULogParser.cc b/src/AnalyzeView/ULogParser.cc index 806dfd0..ebe76dd 100644 --- a/src/AnalyzeView/ULogParser.cc +++ b/src/AnalyzeView/ULogParser.cc @@ -86,11 +86,13 @@ bool ULogParser::parseFieldFormat(QString& fields) return false; } -bool ULogParser::getTagsFromLog(QByteArray& log, QList& cameraFeedback) +bool ULogParser::getTagsFromLog(QByteArray& log, QList& cameraFeedback, QString& errorMessage) { + errorMessage.clear(); + //verify it's an ULog file if(!log.contains(_ULogMagic)) { - qWarning() << "Could not detect ULog file header magic"; + errorMessage = tr("Could not detect ULog file header magic"); return false; } @@ -175,7 +177,11 @@ bool ULogParser::getTagsFromLog(QByteArray& log, QList>>>>>> b4f4085c6733b934358b00389d734600eab87bf7 return false; } diff --git a/src/AnalyzeView/ULogParser.h b/src/AnalyzeView/ULogParser.h index bb2de6e..af036b9 100644 --- a/src/AnalyzeView/ULogParser.h +++ b/src/AnalyzeView/ULogParser.h @@ -3,6 +3,7 @@ #include #include +#include #include "GeoTagController.h" @@ -10,10 +11,14 @@ class ULogParser { + Q_DECLARE_TR_FUNCTIONS(ULogParser) + public: ULogParser(); ~ULogParser(); - bool getTagsFromLog(QByteArray& log, QList& cameraFeedback); + + /// @return true: failed, errorMessage set + bool getTagsFromLog(QByteArray& log, QList& cameraFeedback, QString& errorMessage); private: diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 7f46d85..633688e 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -674,6 +674,14 @@ VideoReceiver::startRecording(const QString &videoFile) gst_element_sync_state_with_parent(_sink->mux); gst_element_sync_state_with_parent(_sink->filesink); + // Install a probe on the recording branch to drop buffers until we hit our first keyframe + // When we hit our first keyframe, we can offset the timestamps appropriately according to the first keyframe time + // This will ensure the first frame is a keyframe at t=0, and decoding can begin immediately on playback + GstPad* probepad = gst_element_get_static_pad(_sink->queue, "src"); + gst_pad_add_probe(probepad, (GstPadProbeType)(GST_PAD_PROBE_TYPE_BUFFER /* | GST_PAD_PROBE_TYPE_BLOCK */), _keyframeWatch, this, NULL); // to drop the buffer or to block the buffer? + gst_object_unref(probepad); + + // Link the recording branch to the pipeline GstPad* sinkpad = gst_element_get_static_pad(_sink->queue, "sink"); gst_pad_link(_sink->teepad, sinkpad); gst_object_unref(sinkpad); @@ -803,6 +811,33 @@ VideoReceiver::_unlinkCallBack(GstPad* pad, GstPadProbeInfo* info, gpointer user #endif //----------------------------------------------------------------------------- +#if defined(QGC_GST_STREAMING) +GstPadProbeReturn +VideoReceiver::_keyframeWatch(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) +{ + Q_UNUSED(pad); + if(info != NULL && user_data != NULL) { + GstBuffer* buf = gst_pad_probe_info_get_buffer(info); + if(GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT)) { // wait for a keyframe + return GST_PAD_PROBE_DROP; + } else { + VideoReceiver* pThis = (VideoReceiver*)user_data; + // reset the clock + GstClock* clock = gst_pipeline_get_clock(GST_PIPELINE(pThis->_pipeline)); + GstClockTime time = gst_clock_get_time(clock); + gst_object_unref(clock); + gst_element_set_base_time(pThis->_pipeline, time); // offset pipeline timestamps to start at zero again + buf->dts = 0; // The offset will not apply to this current buffer, our first frame, timestamp is zero + buf->pts = 0; + qCDebug(VideoReceiverLog) << "Got keyframe, stop dropping buffers"; + } + } + + return GST_PAD_PROBE_REMOVE; +} +#endif + +//----------------------------------------------------------------------------- void VideoReceiver::_updateTimer() { diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 843abdf..76aa304 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -119,6 +119,7 @@ private: static gboolean _onBusMessage (GstBus* bus, GstMessage* message, gpointer user_data); static GstPadProbeReturn _unlinkCallBack (GstPad* pad, GstPadProbeInfo* info, gpointer user_data); + static GstPadProbeReturn _keyframeWatch (GstPad* pad, GstPadProbeInfo* info, gpointer user_data); void _detachRecordingBranch (GstPadProbeInfo* info); void _shutdownRecordingBranch(); void _shutdownPipeline (); diff --git a/src/comm/BluetoothLink.cc b/src/comm/BluetoothLink.cc index f1b9cc4..b6e2009 100644 --- a/src/comm/BluetoothLink.cc +++ b/src/comm/BluetoothLink.cc @@ -75,30 +75,25 @@ QString BluetoothLink::getName() const void BluetoothLink::_writeBytes(const QByteArray bytes) { - if(_targetSocket) - { - if(_targetSocket->isWritable()) - { - if(_targetSocket->write(bytes) > 0) { - _logOutputDataRate(bytes.size(), QDateTime::currentMSecsSinceEpoch()); - } - else - qWarning() << "Bluetooth write error"; + if (_targetSocket) { + if(_targetSocket->write(bytes) > 0) { + _logOutputDataRate(bytes.size(), QDateTime::currentMSecsSinceEpoch()); + } else { + qWarning() << "Bluetooth write error"; } - else - qWarning() << "Bluetooth not writable error"; } } void BluetoothLink::readBytes() { - while (_targetSocket->bytesAvailable() > 0) - { - QByteArray datagram; - datagram.resize(_targetSocket->bytesAvailable()); - _targetSocket->read(datagram.data(), datagram.size()); - emit bytesReceived(this, datagram); - _logInputDataRate(datagram.length(), QDateTime::currentMSecsSinceEpoch()); + if (_targetSocket) { + while (_targetSocket->bytesAvailable() > 0) { + QByteArray datagram; + datagram.resize(_targetSocket->bytesAvailable()); + _targetSocket->read(datagram.data(), datagram.size()); + emit bytesReceived(this, datagram); + _logInputDataRate(datagram.length(), QDateTime::currentMSecsSinceEpoch()); + } } } @@ -114,7 +109,7 @@ void BluetoothLink::_disconnect(void) #endif if(_targetSocket) { - delete _targetSocket; + _targetSocket->deleteLater(); _targetSocket = NULL; emit disconnected(); } diff --git a/src/comm/SerialLink.cc b/src/comm/SerialLink.cc index 3ae34a3..be016ac 100644 --- a/src/comm/SerialLink.cc +++ b/src/comm/SerialLink.cc @@ -57,10 +57,6 @@ void SerialLink::requestReset() SerialLink::~SerialLink() { _disconnect(); - if (_port) { - delete _port; - } - _port = NULL; } bool SerialLink::_isBootloader() @@ -92,6 +88,7 @@ void SerialLink::_writeBytes(const QByteArray data) _port->write(data); } else { // Error occurred + qWarning() << "Serial port not writeable"; _emitLinkError(tr("Could not send data - link %1 is disconnected!").arg(getName())); } } @@ -105,7 +102,7 @@ void SerialLink::_disconnect(void) { if (_port) { _port->close(); - delete _port; + _port->deleteLater(); _port = NULL; } @@ -199,7 +196,7 @@ bool SerialLink::_hardwareConnect(QSerialPort::SerialPortError& error, QString& } } - _port = new QSerialPort(_serialConfig->portName()); + _port = new QSerialPort(_serialConfig->portName(), this); QObject::connect(_port, static_cast(&QSerialPort::error), this, &SerialLink::linkError); @@ -261,12 +258,18 @@ bool SerialLink::_hardwareConnect(QSerialPort::SerialPortError& error, QString& void SerialLink::_readBytes(void) { - qint64 byteCount = _port->bytesAvailable(); - if (byteCount) { - QByteArray buffer; - buffer.resize(byteCount); - _port->read(buffer.data(), buffer.size()); - emit bytesReceived(this, buffer); + if (_port && _port->isOpen()) { + qint64 byteCount = _port->bytesAvailable(); + if (byteCount) { + QByteArray buffer; + buffer.resize(byteCount); + _port->read(buffer.data(), buffer.size()); + emit bytesReceived(this, buffer); + } + } else { + // Error occurred + qWarning() << "Serial port not readable"; + _emitLinkError(tr("Could not read data - link %1 is disconnected!").arg(getName())); } } diff --git a/src/comm/TCPLink.cc b/src/comm/TCPLink.cc index 5760fb7..d91cdb1 100644 --- a/src/comm/TCPLink.cc +++ b/src/comm/TCPLink.cc @@ -78,11 +78,11 @@ void TCPLink::_writeBytes(const QByteArray data) #ifdef TCPLINK_READWRITE_DEBUG _writeDebugBytes(data); #endif - if (!_socket) - return; - _socket->write(data); - _logOutputDataRate(data.size(), QDateTime::currentMSecsSinceEpoch()); + if (_socket) { + _socket->write(data); + _logOutputDataRate(data.size(), QDateTime::currentMSecsSinceEpoch()); + } } /** @@ -93,17 +93,19 @@ void TCPLink::_writeBytes(const QByteArray data) **/ void TCPLink::readBytes() { - qint64 byteCount = _socket->bytesAvailable(); - if (byteCount) - { - QByteArray buffer; - buffer.resize(byteCount); - _socket->read(buffer.data(), buffer.size()); - emit bytesReceived(this, buffer); - _logInputDataRate(byteCount, QDateTime::currentMSecsSinceEpoch()); + if (_socket) { + qint64 byteCount = _socket->bytesAvailable(); + if (byteCount) + { + QByteArray buffer; + buffer.resize(byteCount); + _socket->read(buffer.data(), buffer.size()); + emit bytesReceived(this, buffer); + _logInputDataRate(byteCount, QDateTime::currentMSecsSinceEpoch()); #ifdef TCPLINK_READWRITE_DEBUG - writeDebugBytes(buffer.data(), buffer.size()); + writeDebugBytes(buffer.data(), buffer.size()); #endif + } } } @@ -118,9 +120,9 @@ void TCPLink::_disconnect(void) wait(); if (_socket) { _socketIsConnected = false; - _socket->deleteLater(); // Make sure delete happens on correct thread _socket->disconnectFromHost(); // Disconnect tcp _socket->waitForDisconnected(); + _socket->deleteLater(); // Make sure delete happens on correct thread _socket = NULL; emit disconnected(); } diff --git a/src/comm/UDPLink.cc b/src/comm/UDPLink.cc index ea80e3e..b2ba5ea 100644 --- a/src/comm/UDPLink.cc +++ b/src/comm/UDPLink.cc @@ -160,6 +160,7 @@ QString UDPLink::getName() const void UDPLink::_writeBytes(const QByteArray data) { +<<<<<<< HEAD if (!_socket) return; // Send to all manually targeted systems @@ -167,6 +168,37 @@ void UDPLink::_writeBytes(const QByteArray data) // Skip it if it's part of the session clients below if(!contains_target(_sessionTargets, target->address, target->port)) { _writeDataGram(data, target); +======= + if (_socket) { + QStringList goneHosts; + // Send to all connected systems + QString host; + int port; + if(_udpConfig->firstHost(host, port)) { + do { + QHostAddress currentHost(host); + if(_socket->writeDatagram(data, currentHost, (quint16)port) < 0) { + // This host is gone. Add to list to be removed + // We should keep track of hosts that were manually added (static) and + // hosts that were added because we heard from them (dynamic). Only + // dynamic hosts should be removed and even then, after a few tries, not + // the first failure. In the mean time, we don't remove anything. + if(REMOVE_GONE_HOSTS) { + goneHosts.append(host); + } + } else { + // Only log rate if data actually got sent. Not sure about this as + // "host not there" takes time too regardless of size of data. In fact, + // 1 byte or "UDP frame size" bytes are the same as that's the data + // unit sent by UDP. + _logOutputDataRate(data.size(), QDateTime::currentMSecsSinceEpoch()); + } + } while (_udpConfig->nextHost(host, port)); + //-- Remove hosts that are no longer there + foreach (const QString& ghost, goneHosts) { + _udpConfig->removeHost(ghost); + } +>>>>>>> b4f4085c6733b934358b00389d734600eab87bf7 } } // Send to all connected systems @@ -193,20 +225,32 @@ void UDPLink::_writeDataGram(const QByteArray data, const UDPCLient* target) **/ void UDPLink::readBytes() { - QByteArray databuffer; - while (_socket->hasPendingDatagrams()) - { - QByteArray datagram; - datagram.resize(_socket->pendingDatagramSize()); - QHostAddress sender; - quint16 senderPort; - _socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); - databuffer.append(datagram); - //-- Wait a bit before sending it over - if(databuffer.size() > 10 * 1024) { + if (_socket) { + QByteArray databuffer; + while (_socket->hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(_socket->pendingDatagramSize()); + QHostAddress sender; + quint16 senderPort; + _socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); + databuffer.append(datagram); + //-- Wait a bit before sending it over + if(databuffer.size() > 10 * 1024) { + emit bytesReceived(this, databuffer); + databuffer.clear(); + } + _logInputDataRate(datagram.length(), QDateTime::currentMSecsSinceEpoch()); + // TODO This doesn't validade the sender. Anything sending UDP packets to this port gets + // added to the list and will start receiving datagrams from here. Even a port scanner + // would trigger this. + // Add host to broadcast list if not yet present, or update its port + _udpConfig->addHost(sender.toString(), (int)senderPort); + } + //-- Send whatever is left + if(databuffer.size()) { emit bytesReceived(this, databuffer); - databuffer.clear(); } +<<<<<<< HEAD _logInputDataRate(datagram.length(), QDateTime::currentMSecsSinceEpoch()); // TODO: This doesn't validade the sender. Anything sending UDP packets to this port gets // added to the list and will start receiving datagrams from here. Even a port scanner @@ -225,6 +269,8 @@ void UDPLink::readBytes() //-- Send whatever is left if(databuffer.size()) { emit bytesReceived(this, databuffer); +======= +>>>>>>> b4f4085c6733b934358b00389d734600eab87bf7 } } @@ -272,7 +318,7 @@ bool UDPLink::_hardwareConnect() _socket = NULL; } QHostAddress host = QHostAddress::AnyIPv4; - _socket = new QUdpSocket(); + _socket = new QUdpSocket(this); _socket->setProxy(QNetworkProxy::NoProxy); _connectState = _socket->bind(host, _udpConfig->localPort(), QAbstractSocket::ReuseAddressHint | QUdpSocket::ShareAddress); if (_connectState) {