blob: 5bec944a68136c32c3d2314fe0a42cdda17c98b7 [file] [log] [blame]
/**
* Copyright (C) 2022 Savoir-faire Linux Inc.
*
* Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "TranscriptVideoSubscriber.h"
extern "C" {
#include <libavutil/display.h>
}
#include <pluglog.h>
#include <mediaStream.h>
#include <frameScaler.h>
#include <accel.h>
#include <common.h>
#include <frameUtils.h>
#include <fmt/core.h>
#include <fmt/format.h>
#include <bitset>
#include <string_view>
using namespace std::literals;
const std::string TAG = "TranscriptVideo";
const char sep = separator();
namespace jami {
TranscriptVideoSubscriber::TranscriptVideoSubscriber(const std::string& dataPath)
: path_ {dataPath}
{
fontFile_ = string_utils::ffmpegFormatString(dataPath + sep + "Muli-Light.ttf");
}
TranscriptVideoSubscriber::~TranscriptVideoSubscriber()
{
filter_.clean();
Plog::log(Plog::LogPriority::INFO, TAG, "~TranscriptMediaProcessor");
}
void
TranscriptVideoSubscriber::setText(const std::string& t)
{
Plog::log(Plog::LogPriority::INFO, TAG, "setText " + t);
auto text = string_utils::ffmpegScapeString(t);
std::vector<std::string> textWords = string_utils::getWords(text, " ");
subtitle_ = "";
auto idx = 0;
for (const auto& word : textWords) {
idx++;
subtitle_ = fmt::format("{} {}", subtitle_, word);
if (idx % maxWordsLength == 0)
subtitle_ = fmt::format("{}{}", subtitle_, "\n");
}
#ifdef __DEBUG__
Plog::log(Plog::LogPriority::INFO, TAG, subtitle_);
#endif
firstRun = true;
}
void
TranscriptVideoSubscriber::setParameter(std::string& parameter, Parameter type)
{
switch (type) {
case (Parameter::FONTSIZE):
fontSize_ = parameter;
break;
case (Parameter::BACKGROUND):
background_ = parameter;
if (background_.find("black") == std::string::npos) {
fontColor_ = "black";
fontBackground_ = "white@0.5";
} else {
fontColor_ = "white";
fontBackground_ = "black@0.5";
}
break;
case (Parameter::POSITION):
position_ = parameter;
break;
default:
return;
}
firstRun = true;
}
std::string_view getTransposeDescr(int rotation)
{
switch (rotation) {
case 90:
return "transpose=1,"sv;
case 180:
return "transpose=1, transpose=1,"sv;
case -180:
return "transpose=2, transpose=2,"sv;
case -90:
return "transpose=2,"sv;
default:
return {};
}
return {};
}
void
TranscriptVideoSubscriber::setFilterDescription()
{
Plog::log(Plog::LogPriority::INFO, TAG, "setFilterDescription() " + subtitle_);
if (pluginFrameSize_.first == 0 || pluginFrameSize_.second == 0)
return;
// 1, 2, 3, and 4 are cartesian positions
int margin = 10;
if (position_ == "1") {
point_ = {pluginFrameSize_.first - margin, margin};
} else if (position_ == "2") {
point_ = {margin, margin};
} else if (position_ == "3") {
point_ = {margin, pluginFrameSize_.second - margin};
} else if (position_ == "4") {
point_ = {pluginFrameSize_.first - margin, pluginFrameSize_.second - margin};
}
auto baseInfosDescription
= fmt::format("[input]{}"
"drawtext=fontcolor={}:fontsize={}:fontfile=\\'{}\\':expansion=none:text='{}"
"':line_spacing=5:box=1:boxcolor={}:boxborderw=5:x=",
getTransposeDescr(angle_),
fontColor_, fontSize_, fontFile_, subtitle_, fontBackground_);
auto position = "{}-text_w:y={}"sv;
if (position_ == "2")
position = "{}:y={}"sv;
else if (position_ == "3")
position = "{}:y={}-text_h"sv;
else if (position_ == "4")
position = "{}-text_w:y={}-text_h"sv;
filterDescription_ = baseInfosDescription + fmt::format(std::string(position) + ",{}format=yuv420p"s,
point_.first,
point_.second,
getTransposeDescr(-angle_));
Plog::log(Plog::LogPriority::INFO, TAG, filterDescription_);
}
void
TranscriptVideoSubscriber::update(jami::Observable<AVFrame*>*, AVFrame* const& pluginFrame)
{
if (!observable_ || !pluginFrame || subtitle_.empty())
return;
int newAngle {0};
if (AVFrameSideData* side_data = av_frame_get_side_data(pluginFrame, AV_FRAME_DATA_DISPLAYMATRIX)) {
auto matrix_rotation = reinterpret_cast<int32_t*>(side_data->data);
newAngle = static_cast<int>(av_display_rotation_get(matrix_rotation));
}
if (newAngle != angle_) {
angle_ = newAngle;
firstRun = true;
}
//======================================================================================
// GET RAW FRAME
uniqueFramePtr rgbFrame = {transferToMainMemory(pluginFrame, AV_PIX_FMT_NV12), frameFree};
if (!rgbFrame.get())
return;
if ((AVPixelFormat)rgbFrame->format != AV_PIX_FMT_YUV420P)
rgbFrame.reset(FrameScaler::convertFormat(rgbFrame.get(), AV_PIX_FMT_YUV420P));
if (!rgbFrame.get())
return;
if (sourceTimeBase_.num != pluginFrame->time_base.num || sourceTimeBase_.den != pluginFrame->time_base.den)
firstRun = true;
rgbFrame->pts = pluginFrame->pts;
rgbFrame->time_base = pluginFrame->time_base;
sourceTimeBase_ = pluginFrame->time_base;
if (firstRun) {
filter_.clean();
pluginFrameSize_ = {rgbFrame->width, rgbFrame->height};
if (std::abs(angle_) == 90)
pluginFrameSize_ = {rgbFrame->height, rgbFrame->width};
setFilterDescription();
rational<int> fr(sourceTimeBase_.den, sourceTimeBase_.num);
auto ms = MediaStream("input",
rgbFrame->format,
1 / fr,
rgbFrame->width,
rgbFrame->height,
0,
fr);
filter_.initialize(filterDescription_, {ms});
firstRun = false;
}
if (!filter_.initialized_)
return;
if (filter_.feedInput(rgbFrame.get(), "input") == 0) {
uniqueFramePtr filteredFrame = {filter_.readOutput(), frameFree};
if (filteredFrame) {
moveFrom(pluginFrame, filteredFrame.get());
}
}
}
void
TranscriptVideoSubscriber::attached(jami::Observable<AVFrame*>* observable)
{
Plog::log(Plog::LogPriority::INFO, TAG, "::Attached ! ");
observable_ = observable;
}
void
TranscriptVideoSubscriber::detached(jami::Observable<AVFrame*>*)
{
firstRun = true;
observable_ = nullptr;
Plog::log(Plog::LogPriority::INFO, TAG, "::Detached()");
}
void
TranscriptVideoSubscriber::detach()
{
if (observable_) {
firstRun = true;
std::ostringstream oss;
Plog::log(Plog::LogPriority::INFO, TAG, "::Calling detach()");
observable_->detach(this);
}
}
} // namespace jami