WaterMark: add gif support
GitLab: #23
Change-Id: I8dc9708315097a46e1f3b5359a86d399da680b13
diff --git a/WaterMark/CMakeLists.txt b/WaterMark/CMakeLists.txt
index 4bfb015..aff84f7 100644
--- a/WaterMark/CMakeLists.txt
+++ b/WaterMark/CMakeLists.txt
@@ -2,7 +2,7 @@
# set the project name
set (ProjectName WaterMark)
-set (Version 2.0.0)
+set (Version 2.1.0)
project(${ProjectName} VERSION ${Version})
diff --git a/WaterMark/WatermarkVideoSubscriber.cpp b/WaterMark/WatermarkVideoSubscriber.cpp
index c53d0de..dbf79b5 100644
--- a/WaterMark/WatermarkVideoSubscriber.cpp
+++ b/WaterMark/WatermarkVideoSubscriber.cpp
@@ -71,12 +71,14 @@
if (avformat_open_input(&ctx, logoPath_.c_str(), NULL, NULL) != 0) {
avformat_free_context(ctx);
Plog::log(Plog::LogPriority::INFO, TAG, "Couldn't open input stream.");
+ validLogo_ = false;
return {};
}
pFormatCtx_.reset(ctx);
// Retrieve stream information
if (avformat_find_stream_info(pFormatCtx_.get(), NULL) < 0) {
Plog::log(Plog::LogPriority::INFO, TAG, "Couldn't find stream information.");
+ validLogo_ = false;
return {};
}
@@ -92,6 +94,7 @@
if (videoStream_ == -1) {
Plog::log(Plog::LogPriority::INFO, TAG, "Didn't find a video stream.");
+ validLogo_ = false;
return {};
}
@@ -124,7 +127,6 @@
logoFilter_.initialize(logoDescription_, {logoStream_});
AVCodecContext* pCodecCtx;
- AVPacket* packet;
const AVCodec* pCodec = avcodec_find_decoder(
pFormatCtx_->streams[videoStream_]->codecpar->codec_id);
@@ -144,7 +146,8 @@
return;
}
- packet = av_packet_alloc();
+ AVPacket* packet = av_packet_alloc();
+
if (av_read_frame(pFormatCtx_.get(), packet) < 0) {
avcodec_close(pCodecCtx);
avcodec_free_context(&pCodecCtx);
@@ -292,13 +295,18 @@
if (std::abs(angle_) == 90)
rotateSides = ":out_w=ih:out_h=iw";
+ auto gifDescription = "movie='" + logoPath_ + "':loop=0,setpts=N/(FR*TB)[logo]," + logoDescription_ + "[loop],";
+
if (angle_ != 0)
- pluginFilterDescription_ = "[input]rotate=" + rotation[angle_] + rotateSides
- + "[rot],[rot][mark]overlay=" + std::to_string(points_[0].first)
+ pluginFilterDescription_ = gifDescription
+ + "[input]rotate=" + rotation[angle_] + rotateSides
+ + "[rot],[rot][loop]overlay=" + std::to_string(points_[0].first)
+ ":" + std::to_string(points_[0].second)
+ ",rotate=" + rotation[-angle_] + rotateSides;
else
- pluginFilterDescription_ = "[input][mark]overlay=" + std::to_string(points_[0].first) + ":"
+ pluginFilterDescription_ = gifDescription
+ + "[input][loop]overlay="
+ + std::to_string(points_[0].first) + ":"
+ std::to_string(points_[0].second);
std::string baseInfosDescription = "[input]rotate=" + rotation[angle_] + rotateSides
@@ -329,6 +337,8 @@
void
WatermarkVideoSubscriber::setMarkPosition()
{
+ if (!validLogo_)
+ return;
// 1, 2, 3, and 4 are cartesian positions
int margin = 10;
int markWidth = showLogo_ ? mark_->width : 0;
@@ -370,7 +380,7 @@
void
WatermarkVideoSubscriber::update(jami::Observable<AVFrame*>*, AVFrame* const& pluginFrame)
{
- if (!observable_ || !pluginFrame || (!validLogo_ && showLogo_))
+ if (!observable_ || !pluginFrame || (showLogo_ && !validLogo_))
return;
AVFrameSideData* side_data = av_frame_get_side_data(pluginFrame, AV_FRAME_DATA_DISPLAYMATRIX);
@@ -390,7 +400,13 @@
rgbFrame.reset(FrameScaler::convertFormat(rgbFrame.get(), AV_PIX_FMT_YUV420P));
if (!rgbFrame.get())
return;
- rgbFrame->pts = 1;
+
+ 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) {
pluginFilter_.clean();
@@ -401,7 +417,7 @@
setFilterDescription();
- rational<int> fr(rgbFrame->pts, 1);
+ rational<int> fr(sourceTimeBase_.den, sourceTimeBase_.num);
pluginstream_ = MediaStream("input",
rgbFrame->format,
1 / fr,
@@ -410,17 +426,8 @@
0,
fr);
- if (showLogo_) {
- MediaStream markstream_ = MediaStream("mark",
- mark_->format,
- logoStream_.timeBase,
- mark_->width,
- mark_->height,
- 0,
- logoStream_.frameRate);
- pluginFilter_.initialize(pluginFilterDescription_, {markstream_, pluginstream_});
- pluginFilter_.feedInput(mark_.get(), "mark");
- pluginFilter_.feedEOF("mark");
+ if (showLogo_ && validLogo_) {
+ pluginFilter_.initialize(pluginFilterDescription_, {pluginstream_});
}
infosFilter_.initialize(infosDescription_, {pluginstream_});
@@ -430,7 +437,7 @@
if (!infosFilter_.initialized_ && !pluginFilter_.initialized_)
return;
- if (showLogo_) {
+ if (showLogo_ && validLogo_) {
if (pluginFilter_.feedInput(rgbFrame.get(), "input") == 0) {
uniqueFramePtr filteredFrame = {pluginFilter_.readOutput(), frameFree};
if (filteredFrame.get())
diff --git a/WaterMark/WatermarkVideoSubscriber.h b/WaterMark/WatermarkVideoSubscriber.h
index 4ec39e0..311d933 100644
--- a/WaterMark/WatermarkVideoSubscriber.h
+++ b/WaterMark/WatermarkVideoSubscriber.h
@@ -97,6 +97,7 @@
std::string dateFormat_ = "";
std::string timeFormat_ = "";
std::string fontFile_;
+ AVRational sourceTimeBase_;
FrameFilter pluginFilter_;
std::string pluginFilterDescription_;
@@ -104,7 +105,7 @@
FrameFilter logoFilter_;
MediaStream logoStream_;
std::string logoDescription_;
- uniqueFramePtr mark_ = {av_frame_alloc(), frameFree};
+ uniqueFramePtr mark_ = getUniqueFrame();
FrameFilter infosFilter_;
std::string infosDescription_;
diff --git a/WaterMark/data/accountpreferences.json b/WaterMark/data/accountpreferences.json
index ac2f416..b37f516 100644
--- a/WaterMark/data/accountpreferences.json
+++ b/WaterMark/data/accountpreferences.json
@@ -36,7 +36,7 @@
"defaultValue": "data/jamiwhite.png",
"scope": "plugin,Watermark",
"dependsOn": "showlogo",
- "mimeType": "*/png,*/jpg,*/jpeg"
+ "mimeType": "*/png,*/jpg,*/jpeg,*/gif"
},
{
"category": "{{image_category}}",
diff --git a/WaterMark/data/sample.gif b/WaterMark/data/sample.gif
new file mode 100644
index 0000000..b51b3af
--- /dev/null
+++ b/WaterMark/data/sample.gif
Binary files differ
diff --git a/WaterMark/ffmpeg/rules.mak b/WaterMark/ffmpeg/rules.mak
index 10139bf..63d5a30 100644
--- a/WaterMark/ffmpeg/rules.mak
+++ b/WaterMark/ffmpeg/rules.mak
@@ -50,6 +50,7 @@
--enable-muxer=hevc \
--enable-muxer=webm \
--enable-muxer=ogg \
+ --enable-muxer=gif \
--enable-muxer=pcm_s16be \
--enable-muxer=pcm_s16le \
--enable-demuxer=rtp \
@@ -59,6 +60,9 @@
--enable-demuxer=image_jpeg_pipe \
--enable-demuxer=image_png_pipe \
--enable-demuxer=image_webp_pipe \
+ --enable-demuxer=image_gif_pipe \
+ --enable-muxer=image_gif_pipe \
+ --enable-demuxer=gif \
--enable-demuxer=matroska \
--enable-demuxer=m4v \
--enable-demuxer=mp3 \
@@ -167,7 +171,9 @@
--enable-encoder=ljpeg \
--enable-decoder=jpeg2000 \
--enable-encoder=png \
- --enable-decoder=png
+ --enable-decoder=png \
+ --enable-encoder=gif \
+ --enable-decoder=gif
#filters
FFMPEGCONF += \
@@ -181,7 +187,12 @@
--enable-filter=drawbox \
--enable-filter=drawtext \
--enable-libfreetype \
- --enable-filter=rotate
+ --enable-filter=rotate \
+ --enable-filter=loop \
+ --enable-filter=fps \
+ --enable-filter=setpts \
+ --enable-filter=movie \
+ --enable-filter=realtime
#platform specific options
diff --git a/WaterMark/main.cpp b/WaterMark/main.cpp
index 65f2cd9..5c531f3 100644
--- a/WaterMark/main.cpp
+++ b/WaterMark/main.cpp
@@ -42,7 +42,7 @@
#endif
#define WaterMark_VERSION_MAJOR 2
-#define WaterMark_VERSION_MINOR 0
+#define WaterMark_VERSION_MINOR 1
#define WaterMark_VERSION_PATCH 0
extern "C" {
diff --git a/WaterMark/manifest.json b/WaterMark/manifest.json
index 0dcca79..266b745 100644
--- a/WaterMark/manifest.json
+++ b/WaterMark/manifest.json
@@ -1,5 +1,5 @@
{
"name": "WaterMark",
"description": "With this plugin you'll be able to add a water mark to a call video. This water mark is basically an image but can also contain location, day and time.",
- "version": "2.0.0"
+ "version": "2.1.0"
}
\ No newline at end of file
diff --git a/WaterMark/package.json b/WaterMark/package.json
index ffbdef8..a9705f9 100644
--- a/WaterMark/package.json
+++ b/WaterMark/package.json
@@ -1,6 +1,6 @@
{
"name": "WaterMark",
- "version": "2.0.0",
+ "version": "2.1.0",
"extractLibs": false,
"deps": [
"fmt",