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",
diff --git a/lib/AVFrameIO.h b/lib/AVFrameIO.h
index 6845d33..98b3dd5 100644
--- a/lib/AVFrameIO.h
+++ b/lib/AVFrameIO.h
@@ -214,6 +214,7 @@
             // Read frames from decoder
             while (avcodec_receive_frame(decCodecCtx, pFrame) == 0) {
                 // Publish frames for the plugin subscriber
+                pFrame->time_base = decCodecCtx->time_base;
                 subject->publish(pFrame);
 
                 // Send frame to encoder
diff --git a/lib/frameUtils.cpp b/lib/frameUtils.cpp
index e7c23ff..7289026 100644
--- a/lib/frameUtils.cpp
+++ b/lib/frameUtils.cpp
@@ -40,3 +40,9 @@
     av_frame_unref(frame);
     av_frame_free(&frame);
 }
+
+uniqueFramePtr
+getUniqueFrame() {
+    uniqueFramePtr frame = {av_frame_alloc(), frameFree};
+    return frame;
+}
diff --git a/lib/frameUtils.h b/lib/frameUtils.h
index a49bfdb..1c5c413 100644
--- a/lib/frameUtils.h
+++ b/lib/frameUtils.h
@@ -42,4 +42,9 @@
 
 using uniqueFramePtr = std::unique_ptr<AVFrame, decltype(&frameFree)>;
 
+/**
+ * @brief Crate allocated frame with destructor
+ */
+uniqueFramePtr getUniqueFrame();
+
 #endif // FRAMEUTILS_H