From cf4acb3f74c638bda31ea4c132882f1f0ab35a7c Mon Sep 17 00:00:00 2001
From: philippegorley <philippe.gorley@savoirfairelinux.com>
Date: Tue, 25 Jun 2019 11:12:34 -0400
Subject: [PATCH] encoder: use avoption api

FFmpeg's AVOption API is more flexible and better suited than
AVDictionary for codec and format options.

This also allows to use strings in encoder.json, such as setting profile
to high (instead of 100).

Change-Id: I2039b2d039a87f8b3ed8ecb197dfa7b734c27950
---
 src/media/media_encoder.cpp | 41 +++++++++++++------------------------
 src/media/media_encoder.h   |  2 +-
 2 files changed, 15 insertions(+), 28 deletions(-)

diff --git a/src/media/media_encoder.cpp b/src/media/media_encoder.cpp
index b1fbb3c6ff..f4e6220b3a 100644
--- a/src/media/media_encoder.cpp
+++ b/src/media/media_encoder.cpp
@@ -107,7 +107,12 @@ MediaEncoder::setOptions(const MediaStream& opts)
 void
 MediaEncoder::setOptions(const MediaDescription& args)
 {
-    libav_utils::setDictValue(&options_, "payload_type", std::to_string(args.payload_type));
+    int ret;
+    if ((ret = av_opt_set_int(reinterpret_cast<void*>(outputCtx_),
+        "payload_type", args.payload_type, AV_OPT_SEARCH_CHILDREN)) < 0)
+        JAMI_ERR() << "Failed to set payload type: " << libav_utils::getError(ret);
+
+    // NOTE none of these are format options, and we don't have a codec context yet
     libav_utils::setDictValue(&options_, "max_rate", std::to_string(args.codec->bitrate));
     libav_utils::setDictValue(&options_, "crf", std::to_string(args.codec->quality));
 
@@ -203,7 +208,7 @@ MediaEncoder::initStream(const SystemCodecInfo& systemCodecInfo, AVBufferRef* fr
 
     currentStreamIdx_ = stream->index;
 
-    readConfig(&options_, encoderCtx);
+    readConfig(encoderCtx);
     if (avcodec_open2(encoderCtx, outputCodec_, &options_) < 0)
         throw MediaEncoderException("Could not open encoder");
 
@@ -846,7 +851,7 @@ MediaEncoder::stopEncoder()
 }
 
 void
-MediaEncoder::readConfig(AVDictionary** dict, AVCodecContext* encoderCtx)
+MediaEncoder::readConfig(AVCodecContext* encoderCtx)
 {
     std::string path = fileutils::get_config_dir() + DIR_SEPARATOR_STR + "encoder.json";
     std::string name = encoderCtx->codec->name;
@@ -868,7 +873,6 @@ MediaEncoder::readConfig(AVDictionary** dict, AVCodecContext* encoderCtx)
                 JAMI_ERR() << "Invalid encoder configuration: '" << name << "' is not an object";
                 return;
             }
-            // If users want to change these, they should use the settings page.
             for (Json::Value::const_iterator it = config.begin(); it != config.end(); ++it) {
                 Json::Value v = *it;
                 if (!it.key().isConvertibleTo(Json::ValueType::stringValue)
@@ -878,29 +882,12 @@ MediaEncoder::readConfig(AVDictionary** dict, AVCodecContext* encoderCtx)
                 }
                 const auto& key = it.key().asString();
                 const auto& value = v.asString();
-                // provides a way to override all AVCodecContext fields MediaEncoder sets
-                if (key == "parameters") // Used by MediaEncoder for profile-level-id, ignore
-                    continue;
-                else if (value.empty())
-                    libav_utils::setDictValue(dict, key, nullptr);
-                else if (key == "profile")
-                    encoderCtx->profile = v.asInt();
-                else if (key == "level")
-                    encoderCtx->level = v.asInt();
-                else if (key == "bit_rate")
-                    encoderCtx->bit_rate = v.asInt();
-                else if (key == "rc_buffer_size")
-                    encoderCtx->rc_buffer_size = v.asInt();
-                else if (key == "rc_min_rate")
-                    encoderCtx->rc_min_rate = v.asInt();
-                else if (key == "rc_max_rate")
-                    encoderCtx->rc_max_rate = v.asInt();
-                else if (key == "qmin")
-                    encoderCtx->qmin = v.asInt();
-                else if (key == "qmax")
-                    encoderCtx->qmax = v.asInt();
-                else
-                    libav_utils::setDictValue(dict, key, value);
+                int ret = av_opt_set(reinterpret_cast<void*>(encoderCtx),
+                                     key.c_str(), value.c_str(), AV_OPT_SEARCH_CHILDREN);
+                if (ret < 0) {
+                    JAMI_ERR() << "Failed to set option " << key << " in encoder context: "
+                        << libav_utils::getError(ret) << "\n";
+                }
             }
         } catch (const Json::Exception& e) {
             JAMI_ERR() << "Failed to load encoder configuration file: " << e.what();
diff --git a/src/media/media_encoder.h b/src/media/media_encoder.h
index 5dc9d5df10..800ec4bc95 100644
--- a/src/media/media_encoder.h
+++ b/src/media/media_encoder.h
@@ -152,7 +152,7 @@ private:
 #endif
 
 protected:
-    void readConfig(AVDictionary** dict, AVCodecContext* encoderCtx);
+    void readConfig(AVCodecContext* encoderCtx);
     AVDictionary* options_ = nullptr;
     MediaStream videoOpts_;
     MediaStream audioOpts_;
-- 
GitLab