Commit 2f521d2e authored by Tristan Matthews's avatar Tristan Matthews

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

parent 1d3ebe19
......@@ -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);
......
......@@ -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);
......
......@@ -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++;
}
......
......@@ -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
......
......@@ -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;
......
......@@ -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__
......@@ -121,6 +121,53 @@ void VideoSendThread::prepareEncoderContext(AVCodec *encoder)
// 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()
{
......@@ -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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment