diff --git a/daemon/src/account.cpp b/daemon/src/account.cpp index 6ff8cc0c64908a2705c5f7ea046d7c3f7ea3343d..e4c2cbf89cae3e2cee8d394bf9c2e22765d92556 100644 --- a/daemon/src/account.cpp +++ b/daemon/src/account.cpp @@ -46,6 +46,7 @@ const char * const Account::AUDIO_CODECS_KEY = "audioCodecs"; // 0/9/110/1 const char * const Account::VIDEO_CODECS_KEY = "videoCodecs"; const char * const Account::VIDEO_CODEC_ENABLED = "enabled"; const char * const Account::VIDEO_CODEC_NAME = "name"; +const char * const Account::VIDEO_CODEC_PARAMETERS ="parameters"; const char * const Account::VIDEO_CODEC_BITRATE = "bitrate"; const char * const Account::RINGTONE_PATH_KEY = "ringtonePath"; const char * const Account::RINGTONE_ENABLED_KEY = "ringtoneEnabled"; @@ -146,7 +147,7 @@ namespace { bool isCodecValid(const map<string, string> &codec, const vector<map<string, string> > &defaults) { - map<string, string>::const_iterator name(codec.find(Account::VIDEO_CODEC_NAME)); + const map<string, string>::const_iterator name(codec.find(Account::VIDEO_CODEC_NAME)); if (name == codec.end()) { ERROR("Field \"name\" missing in codec specification"); return false; @@ -154,7 +155,7 @@ namespace { // check that it's in the list of valid codecs and that it has all the required fields for (vector<map<string, string> >::const_iterator i = defaults.begin(); i != defaults.end(); ++i) { - map<string, string>::const_iterator defaultName = i->find(Account::VIDEO_CODEC_NAME); + const map<string, string>::const_iterator defaultName = i->find(Account::VIDEO_CODEC_NAME); if (defaultName->second == name->second) { return isFieldValid(codec, Account::VIDEO_CODEC_BITRATE, isPositiveInteger) and isFieldValid(codec, Account::VIDEO_CODEC_ENABLED, isBoolean); diff --git a/daemon/src/account.h b/daemon/src/account.h index ee42256d8d5b110ce96c659132b122a063a6609d..d6d5140ae606ba253371e94d14975f97885103d8 100644 --- a/daemon/src/account.h +++ b/daemon/src/account.h @@ -185,6 +185,7 @@ class Account : public Serializable { static const char * const VIDEO_CODEC_ENABLED; static const char * const VIDEO_CODEC_NAME; + static const char * const VIDEO_CODEC_PARAMETERS; static const char * const VIDEO_CODEC_BITRATE; private: NON_COPYABLE(Account); diff --git a/daemon/src/sip/sdp.cpp b/daemon/src/sip/sdp.cpp index 9dc555a8fc074704771623cbd975cddf121a9127..3d3f09dc787a7c853b07cb91551d35fcb8f1df95 100644 --- a/daemon/src/sip/sdp.cpp +++ b/daemon/src/sip/sdp.cpp @@ -220,13 +220,15 @@ pjmedia_sdp_media *Sdp::setMediaDescriptorLine(bool audio) pjmedia_sdp_rtpmap_to_attr(memPool_, &rtpmap, &attr); med->attr[med->attr_count++] = attr; +#ifdef SFL_VIDEO if (enc_name == "H264") { std::ostringstream os; // FIXME: this should not be hardcoded, it will determine what profile and level // our peer will send us - os << "fmtp:" << dynamic_payload << " profile-level-id=428014"; + os << "fmtp:" << dynamic_payload << " " << libav_utils::DEFAULT_H264_PROFILE_LEVEL_ID; med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_, os.str().c_str(), NULL); } +#endif if (not audio) dynamic_payload++; } diff --git a/daemon/src/sip/sipaccount.cpp b/daemon/src/sip/sipaccount.cpp index 03d02cbd5b206fe096796b44e552bcb88f7889ac..5e04cb4ec87bc6160127d683c69ae3210c855e06 100644 --- a/daemon/src/sip/sipaccount.cpp +++ b/daemon/src/sip/sipaccount.cpp @@ -160,6 +160,7 @@ void SIPAccount::serialize(Conf::YamlEmitter &emitter) mapNode->setKeyValue(VIDEO_CODEC_NAME, new ScalarNode(codec[VIDEO_CODEC_NAME])); mapNode->setKeyValue(VIDEO_CODEC_BITRATE, new ScalarNode(codec[VIDEO_CODEC_BITRATE])); mapNode->setKeyValue(VIDEO_CODEC_ENABLED, new ScalarNode(codec[VIDEO_CODEC_ENABLED])); + mapNode->setKeyValue(VIDEO_CODEC_PARAMETERS, new ScalarNode(codec[VIDEO_CODEC_PARAMETERS])); videoCodecs.addNode(mapNode); } #endif @@ -287,6 +288,7 @@ void SIPAccount::serialize(Conf::YamlEmitter &emitter) delete node->getValue(VIDEO_CODEC_NAME); delete node->getValue(VIDEO_CODEC_BITRATE); delete node->getValue(VIDEO_CODEC_ENABLED); + delete node->getValue(VIDEO_CODEC_PARAMETERS); delete node; } #endif @@ -326,6 +328,7 @@ void SIPAccount::unserialize(const Conf::MappingNode &mapNode) codec->getValue(VIDEO_CODEC_NAME, &codecMap[VIDEO_CODEC_NAME]); codec->getValue(VIDEO_CODEC_BITRATE, &codecMap[VIDEO_CODEC_BITRATE]); codec->getValue(VIDEO_CODEC_ENABLED, &codecMap[VIDEO_CODEC_ENABLED]); + codec->getValue(VIDEO_CODEC_PARAMETERS, &codecMap[VIDEO_CODEC_PARAMETERS]); videoCodecDetails.push_back(codecMap); } // these must be validated diff --git a/daemon/src/video/libav_utils.cpp b/daemon/src/video/libav_utils.cpp index 52d842e86c77824b1c5187367b16e8bfffdba850..2258ae87e4a24188b5acc146f3227673daab47e2 100644 --- a/daemon/src/video/libav_utils.cpp +++ b/daemon/src/video/libav_utils.cpp @@ -52,7 +52,6 @@ vector<string> installed_video_codecs_; /* application wide mutex to protect concurrent access to avcodec */ ost::Mutex avcodec_lock_; - void findInstalledVideoCodecs() { vector<string> libav_codecs; @@ -73,7 +72,6 @@ void findInstalledVideoCodecs() namespace libav_utils { - vector<string> getVideoCodecList() { if (installed_video_codecs_.empty()) @@ -154,6 +152,9 @@ getDefaultCodecs() codec["name"] = *iter; codec["bitrate"] = DEFAULT_BITRATE; codec["enabled"] = "true"; + // FIXME: make a nicer version of this + if (*iter == "H264") + codec["parameters"] = DEFAULT_H264_PROFILE_LEVEL_ID; result.push_back(codec); } return result; diff --git a/daemon/src/video/libav_utils.h b/daemon/src/video/libav_utils.h index a18714ec1bedffacc31fbe32ddb34ac5057b3fca..585d3201cb3ce1486dca1430c847dbbe40ad7c07 100644 --- a/daemon/src/video/libav_utils.h +++ b/daemon/src/video/libav_utils.h @@ -47,6 +47,7 @@ namespace libav_utils { std::vector<std::map<std::string, std::string> > getDefaultCodecs(); + const char *const DEFAULT_H264_PROFILE_LEVEL_ID = "profile-level-id=428014"; } #endif // __LIBAV_UTILS_H__ diff --git a/daemon/src/video/video_send_thread.cpp b/daemon/src/video/video_send_thread.cpp index 1848e4e3c30d9b8208bf659772cb919dfe933146..f731fe81517987252a7ca14d6eb1821458e1f360 100644 --- a/daemon/src/video/video_send_thread.cpp +++ b/daemon/src/video/video_send_thread.cpp @@ -121,6 +121,53 @@ void VideoSendThread::prepareEncoderContext(AVCodec *encoder) // encoderCtx_->flags |= CODEC_FLAG_GLOBAL_HEADER; } +namespace { +void +extractProfileLevelID(const std::string ¶meters, AVCodecContext *ctx) +{ + // From RFC3984: + // If no profile-level-id is present, the Baseline Profile without + // additional constraints at Level 1 MUST be implied. + ctx->profile = FF_PROFILE_H264_BASELINE; + ctx->level = 0x1; + // ctx->level = 0x0d; // => 13 aka 1.3 + if (parameters.empty()) + return; + + const std::string target("profile-level-id="); + size_t needle = parameters.find(target); + if (needle == std::string::npos) + return; + + needle += target.length(); + const size_t id_length = 6; /* digits */ + const std::string profileLevelID(parameters.substr(needle, id_length)); + if (profileLevelID.length() != id_length) + return; + + int result; + std::stringstream ss; + ss << profileLevelID; + ss >> std::hex >> result; + // profile-level id consists of three bytes + const unsigned char profile_idc = result >> 16; // 42xxxx -> 42 + const unsigned char profile_iop = ((result >> 8) & 0xff); // xx80xx -> 80 + ctx->level = result & 0xff; // xxxx0d -> 0d + switch (profile_idc) { + case FF_PROFILE_H264_BASELINE: + // check constraint_set_1_flag + ctx->profile |= (profile_iop & 0x40) >> 6 ? FF_PROFILE_H264_CONSTRAINED : 0; + break; + case FF_PROFILE_H264_HIGH_10: + case FF_PROFILE_H264_HIGH_422: + case FF_PROFILE_H264_HIGH_444_PREDICTIVE: + // check constraint_set_3_flag + ctx->profile |= (profile_iop & 0x10) >> 4 ? FF_PROFILE_H264_INTRA : 0; + break; + } + DEBUG("Using profile %x and level %d", ctx->profile, ctx->level); +} +} void VideoSendThread::setup() { @@ -191,12 +238,11 @@ void VideoSendThread::setup() /* let x264 preset override our encoder settings */ if (args_["codec"] == "libx264") { - forcePresetX264(); // FIXME: this should be parsed from the fmtp:profile-level-id // attribute of our peer, it will determine what profile and // level we are sending (i.e. that they can accept). - encoderCtx_->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE; - encoderCtx_->level = 0x0d; // => 13 aka 1.3 + extractProfileLevelID(args_["parameters"], encoderCtx_); + forcePresetX264(); } scaledPicture_ = avcodec_alloc_frame();