Skip to content
Snippets Groups Projects
Commit 2f521d2e authored by Tristan Matthews's avatar Tristan Matthews
Browse files

* #13814: video: parse encoder profile-level-id properly

parent 1d3ebe19
Branches
Tags
No related merge requests found
...@@ -46,6 +46,7 @@ const char * const Account::AUDIO_CODECS_KEY = "audioCodecs"; // 0/9/110/1 ...@@ -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_CODECS_KEY = "videoCodecs";
const char * const Account::VIDEO_CODEC_ENABLED = "enabled"; const char * const Account::VIDEO_CODEC_ENABLED = "enabled";
const char * const Account::VIDEO_CODEC_NAME = "name"; 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::VIDEO_CODEC_BITRATE = "bitrate";
const char * const Account::RINGTONE_PATH_KEY = "ringtonePath"; const char * const Account::RINGTONE_PATH_KEY = "ringtonePath";
const char * const Account::RINGTONE_ENABLED_KEY = "ringtoneEnabled"; const char * const Account::RINGTONE_ENABLED_KEY = "ringtoneEnabled";
...@@ -146,7 +147,7 @@ namespace { ...@@ -146,7 +147,7 @@ namespace {
bool isCodecValid(const map<string, string> &codec, const vector<map<string, string> > &defaults) 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()) { if (name == codec.end()) {
ERROR("Field \"name\" missing in codec specification"); ERROR("Field \"name\" missing in codec specification");
return false; return false;
...@@ -154,7 +155,7 @@ namespace { ...@@ -154,7 +155,7 @@ namespace {
// check that it's in the list of valid codecs and that it has all the required fields // 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) { 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) { if (defaultName->second == name->second) {
return isFieldValid(codec, Account::VIDEO_CODEC_BITRATE, isPositiveInteger) return isFieldValid(codec, Account::VIDEO_CODEC_BITRATE, isPositiveInteger)
and isFieldValid(codec, Account::VIDEO_CODEC_ENABLED, isBoolean); and isFieldValid(codec, Account::VIDEO_CODEC_ENABLED, isBoolean);
......
...@@ -185,6 +185,7 @@ class Account : public Serializable { ...@@ -185,6 +185,7 @@ class Account : public Serializable {
static const char * const VIDEO_CODEC_ENABLED; static const char * const VIDEO_CODEC_ENABLED;
static const char * const VIDEO_CODEC_NAME; static const char * const VIDEO_CODEC_NAME;
static const char * const VIDEO_CODEC_PARAMETERS;
static const char * const VIDEO_CODEC_BITRATE; static const char * const VIDEO_CODEC_BITRATE;
private: private:
NON_COPYABLE(Account); NON_COPYABLE(Account);
......
...@@ -220,13 +220,15 @@ pjmedia_sdp_media *Sdp::setMediaDescriptorLine(bool audio) ...@@ -220,13 +220,15 @@ pjmedia_sdp_media *Sdp::setMediaDescriptorLine(bool audio)
pjmedia_sdp_rtpmap_to_attr(memPool_, &rtpmap, &attr); pjmedia_sdp_rtpmap_to_attr(memPool_, &rtpmap, &attr);
med->attr[med->attr_count++] = attr; med->attr[med->attr_count++] = attr;
#ifdef SFL_VIDEO
if (enc_name == "H264") { if (enc_name == "H264") {
std::ostringstream os; std::ostringstream os;
// FIXME: this should not be hardcoded, it will determine what profile and level // FIXME: this should not be hardcoded, it will determine what profile and level
// our peer will send us // 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); med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_, os.str().c_str(), NULL);
} }
#endif
if (not audio) if (not audio)
dynamic_payload++; dynamic_payload++;
} }
......
...@@ -160,6 +160,7 @@ void SIPAccount::serialize(Conf::YamlEmitter &emitter) ...@@ -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_NAME, new ScalarNode(codec[VIDEO_CODEC_NAME]));
mapNode->setKeyValue(VIDEO_CODEC_BITRATE, new ScalarNode(codec[VIDEO_CODEC_BITRATE])); 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_ENABLED, new ScalarNode(codec[VIDEO_CODEC_ENABLED]));
mapNode->setKeyValue(VIDEO_CODEC_PARAMETERS, new ScalarNode(codec[VIDEO_CODEC_PARAMETERS]));
videoCodecs.addNode(mapNode); videoCodecs.addNode(mapNode);
} }
#endif #endif
...@@ -287,6 +288,7 @@ void SIPAccount::serialize(Conf::YamlEmitter &emitter) ...@@ -287,6 +288,7 @@ void SIPAccount::serialize(Conf::YamlEmitter &emitter)
delete node->getValue(VIDEO_CODEC_NAME); delete node->getValue(VIDEO_CODEC_NAME);
delete node->getValue(VIDEO_CODEC_BITRATE); delete node->getValue(VIDEO_CODEC_BITRATE);
delete node->getValue(VIDEO_CODEC_ENABLED); delete node->getValue(VIDEO_CODEC_ENABLED);
delete node->getValue(VIDEO_CODEC_PARAMETERS);
delete node; delete node;
} }
#endif #endif
...@@ -326,6 +328,7 @@ void SIPAccount::unserialize(const Conf::MappingNode &mapNode) ...@@ -326,6 +328,7 @@ void SIPAccount::unserialize(const Conf::MappingNode &mapNode)
codec->getValue(VIDEO_CODEC_NAME, &codecMap[VIDEO_CODEC_NAME]); codec->getValue(VIDEO_CODEC_NAME, &codecMap[VIDEO_CODEC_NAME]);
codec->getValue(VIDEO_CODEC_BITRATE, &codecMap[VIDEO_CODEC_BITRATE]); codec->getValue(VIDEO_CODEC_BITRATE, &codecMap[VIDEO_CODEC_BITRATE]);
codec->getValue(VIDEO_CODEC_ENABLED, &codecMap[VIDEO_CODEC_ENABLED]); codec->getValue(VIDEO_CODEC_ENABLED, &codecMap[VIDEO_CODEC_ENABLED]);
codec->getValue(VIDEO_CODEC_PARAMETERS, &codecMap[VIDEO_CODEC_PARAMETERS]);
videoCodecDetails.push_back(codecMap); videoCodecDetails.push_back(codecMap);
} }
// these must be validated // these must be validated
......
...@@ -52,7 +52,6 @@ vector<string> installed_video_codecs_; ...@@ -52,7 +52,6 @@ vector<string> installed_video_codecs_;
/* application wide mutex to protect concurrent access to avcodec */ /* application wide mutex to protect concurrent access to avcodec */
ost::Mutex avcodec_lock_; ost::Mutex avcodec_lock_;
void findInstalledVideoCodecs() void findInstalledVideoCodecs()
{ {
vector<string> libav_codecs; vector<string> libav_codecs;
...@@ -73,7 +72,6 @@ void findInstalledVideoCodecs() ...@@ -73,7 +72,6 @@ void findInstalledVideoCodecs()
namespace libav_utils { namespace libav_utils {
vector<string> getVideoCodecList() vector<string> getVideoCodecList()
{ {
if (installed_video_codecs_.empty()) if (installed_video_codecs_.empty())
...@@ -154,6 +152,9 @@ getDefaultCodecs() ...@@ -154,6 +152,9 @@ getDefaultCodecs()
codec["name"] = *iter; codec["name"] = *iter;
codec["bitrate"] = DEFAULT_BITRATE; codec["bitrate"] = DEFAULT_BITRATE;
codec["enabled"] = "true"; codec["enabled"] = "true";
// FIXME: make a nicer version of this
if (*iter == "H264")
codec["parameters"] = DEFAULT_H264_PROFILE_LEVEL_ID;
result.push_back(codec); result.push_back(codec);
} }
return result; return result;
......
...@@ -47,6 +47,7 @@ namespace libav_utils { ...@@ -47,6 +47,7 @@ namespace libav_utils {
std::vector<std::map<std::string, std::string> > std::vector<std::map<std::string, std::string> >
getDefaultCodecs(); getDefaultCodecs();
const char *const DEFAULT_H264_PROFILE_LEVEL_ID = "profile-level-id=428014";
} }
#endif // __LIBAV_UTILS_H__ #endif // __LIBAV_UTILS_H__
...@@ -121,6 +121,53 @@ void VideoSendThread::prepareEncoderContext(AVCodec *encoder) ...@@ -121,6 +121,53 @@ void VideoSendThread::prepareEncoderContext(AVCodec *encoder)
// encoderCtx_->flags |= CODEC_FLAG_GLOBAL_HEADER; // encoderCtx_->flags |= CODEC_FLAG_GLOBAL_HEADER;
} }
namespace {
void
extractProfileLevelID(const std::string &parameters, 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() void VideoSendThread::setup()
{ {
...@@ -191,12 +238,11 @@ void VideoSendThread::setup() ...@@ -191,12 +238,11 @@ void VideoSendThread::setup()
/* let x264 preset override our encoder settings */ /* let x264 preset override our encoder settings */
if (args_["codec"] == "libx264") { if (args_["codec"] == "libx264") {
forcePresetX264();
// FIXME: this should be parsed from the fmtp:profile-level-id // FIXME: this should be parsed from the fmtp:profile-level-id
// attribute of our peer, it will determine what profile and // attribute of our peer, it will determine what profile and
// level we are sending (i.e. that they can accept). // level we are sending (i.e. that they can accept).
encoderCtx_->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE; extractProfileLevelID(args_["parameters"], encoderCtx_);
encoderCtx_->level = 0x0d; // => 13 aka 1.3 forcePresetX264();
} }
scaledPicture_ = avcodec_alloc_frame(); scaledPicture_ = avcodec_alloc_frame();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment