地面站终端 App
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
7.1 KiB

/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief QGC Video Subtitle Writer
* @author Willian Galvani <williangalvani@gmail.com>
*/
#include "SubtitleWriter.h"
#include "QGCApplication.h"
#include "QGCCorePlugin.h"
#include "FactValueGrid.h"
#include "HorizontalFactValueGrid.h"
#include "InstrumentValueData.h"
#include <QDateTime>
#include <QString>
#include <QDate>
QGC_LOGGING_CATEGORY(SubtitleWriterLog, "SubtitleWriterLog")
const int SubtitleWriter::_sampleRate = 1; // Sample rate in Hz for getting telemetry data, most players do weird stuff when > 1Hz
SubtitleWriter::SubtitleWriter(QObject* parent)
: QObject(parent)
{
connect(&_timer, &QTimer::timeout, this, &SubtitleWriter::_captureTelemetry);
}
void SubtitleWriter::startCapturingTelemetry(const QString& videoFile)
{
// Delete facts of last run
_facts.clear();
// Gather the facts currently displayed into _facts
FactValueGrid* grid = new FactValueGrid();
grid->setProperty("userSettingsGroup", HorizontalFactValueGrid::telemetryBarUserSettingsGroup);
grid->setProperty("defaultSettingsGroup", HorizontalFactValueGrid::telemetryBarDefaultSettingsGroup);
grid->_loadSettings();
for (int colIndex = 0; colIndex < grid->columns()->count(); colIndex++) {
QmlObjectListModel* list = grid->columns()->value<QmlObjectListModel*>(colIndex);
for (int rowIndex = 0; rowIndex < list->count(); rowIndex++) {
InstrumentValueData* value = list->value<InstrumentValueData*>(rowIndex);
_facts += value->fact();
}
}
grid->deleteLater();
// One subtitle always starts where the previous ended
_lastEndTime = QTime(0, 0);
QFileInfo videoFileInfo(videoFile);
QString subtitleFilePath = QStringLiteral("%1/%2.ass").arg(videoFileInfo.path(), videoFileInfo.completeBaseName());
qCDebug(SubtitleWriterLog) << "Writing overlay to file:" << subtitleFilePath;
_file.setFileName(subtitleFilePath);
if (!_file.open(QIODevice::ReadWrite)) {
qCWarning(SubtitleWriterLog) << "Unable to write subtitle data to file";
return;
}
QTextStream stream( &_file );
// This is file header
stream << QStringLiteral(
"[Script Info]\n"
"Title: QGroundControl Subtitle Telemetry file\n"
"ScriptType: v4.00+\n"
"WrapStyle: 0\n"
"ScaledBorderAndShadow: yes\n"
"YCbCr Matrix: TV.601\n"
"PlayResX: 1920\n"
"PlayResY: 1080\n"
"\n"
"[V4+ Styles]\n"
"Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n"
"Style: Default,Monospace,30,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,1,10,10,10,1\n"
"\n"
"[Events]\n"
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
);
// TODO: Find a good way to input title
//stream << QStringLiteral("Dialogue: 0,0:00:00.00,999:00:00.00,Default,,0,0,0,,{\\pos(5,35)}%1\n");
_timer.start(1000/_sampleRate);
}
void SubtitleWriter::stopCapturingTelemetry()
{
qCDebug(SubtitleWriterLog) << "Stopping writing";
_timer.stop();
_file.close();
}
void SubtitleWriter::_captureTelemetry()
{
static const float nRows = 3; // number of rows used for displaying data
static const int offsetFactor = 700; // Used to simulate a larger resolution and reduce the borders in the layout
auto *vehicle = qgcApp()->toolbox()->multiVehicleManager()->activeVehicle();
if (!vehicle) {
qCWarning(SubtitleWriterLog) << "Attempting to capture fact data with no active vehicle!";
return;
}
// Each list corresponds to a column in the subtitles
QStringList namesStrings;
QStringList valuesStrings;
// Make a list of "factname:" strings and other with the values, so one can be aligned left and the other right
for (const Fact* fact : _facts) {
valuesStrings << QStringLiteral("%2 %3").arg(fact->cookedValueString())
.arg(fact->cookedUnits());
namesStrings << QStringLiteral("%1:").arg(fact->shortDescription());
}
// The time to start displaying this subtitle text
QTime start = _lastEndTime;
// The time to stop displaying this subtitle text
QTime end = start.addMSecs(1000/_sampleRate);
_lastEndTime = end;
// This splits the screen in N parts and uses the N-1 internal parts to align the subtitles to.
// Should we try to get the resolution from the pipeline? This seems to work fine with other resolutions too.
static const int rowWidth = (1920 + offsetFactor)/(nRows+1);
int nValuesByRow = ceil(_facts.length() / nRows);
QList<QStringList> dataColumns;
QStringList stringColumns;
// These templates are used for the data columns, one right-aligned for names and one for
// the facts values. The arguments expected are: start time, end time, xposition, and string content.
QString namesLine = QStringLiteral("Dialogue: 0,%2,%3,Default,,0,0,0,,{\\an3\\pos(%1,1075)}%4\n");
QString valuesLine = QStringLiteral("Dialogue: 0,%2,%3,Default,,0,0,0,,{\\pos(%1,1075)}%4\n");
// Split values into N columns and create a subtitle entry for each column
for (int i=0; i<nRows; i++) {
QStringList currentColumnNameStrings = namesStrings.mid((i)*nValuesByRow, nValuesByRow);
QStringList currentColumnValueStrings = valuesStrings.mid((i)*nValuesByRow, nValuesByRow);
// Fill templates for names of column i
QString names = namesLine.arg(-offsetFactor/2 + rowWidth*(i+1) - 10)
.arg(start.toString("H:mm:ss.zzz").chopped(2))
.arg(end.toString("H:mm:ss.zzz").chopped(2))
.arg(currentColumnNameStrings.join("\\N"));
stringColumns << names;
// Fill templates for values of column i
QString values = valuesLine.arg(-offsetFactor/2 +rowWidth*(i+1))
.arg(start.toString("H:mm:ss.zzz").chopped(2))
.arg(end.toString("H:mm:ss.zzz").chopped(2))
.arg(currentColumnValueStrings.join("\\N"));
stringColumns << values;
}
// Write the date to the corner
stringColumns << QStringLiteral("Dialogue: 0,%1,%2,Default,,0,0,0,,{\\pos(10,35)}%3\n")
.arg(start.toString("H:mm:ss.zzz").chopped(2))
.arg(end.toString("H:mm:ss.zzz").chopped(2))
.arg(QDateTime::currentDateTime().toString(Qt::SystemLocaleShortDate));
// Write new data
QTextStream stream(&_file);
for (const auto& i : stringColumns) {
stream << i;
}
}