Commit 8a141b34 authored by Emmanuel Milou's avatar Emmanuel Milou
parents 80bb3a66 9e2b7258
......@@ -2381,7 +2381,12 @@ ManagerImpl::addAccount(const std::map<std::string, std::string>& details)
std::string newAccountID(accountID.str());
// Get the type
std::string accountType((*details.find(CONFIG_ACCOUNT_TYPE)).second);
std::string accountType;
if (details.find(CONFIG_ACCOUNT_TYPE) == details.end())
accountType = "SIP";
else
accountType = ((*details.find(CONFIG_ACCOUNT_TYPE)).second);
DEBUG("Adding account %s", newAccountID.c_str());
......
......@@ -202,7 +202,7 @@ pjmedia_sdp_media *Sdp::setMediaDescriptorLine(bool audio)
} else {
enc_name = video_codec_list_[i];
clock_rate = 90000;
payload = dynamic_payload++;
payload = dynamic_payload;
}
std::ostringstream s;
......@@ -225,9 +225,18 @@ pjmedia_sdp_media *Sdp::setMediaDescriptorLine(bool audio)
pjmedia_sdp_rtpmap_to_attr(memPool_, &rtpmap, &attr);
med->attr[med->attr_count++] = attr;
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";
med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_, os.str().c_str(), NULL);
}
if (not audio)
dynamic_payload++;
}
med->attr[ med->attr_count++] = pjmedia_sdp_attr_create(memPool_, "sendrecv", NULL);
med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_, "sendrecv", NULL);
if (!zrtpHelloHash_.empty())
addZrtpAttribute(med, zrtpHelloHash_);
......
......@@ -294,14 +294,14 @@ class Sdp {
* Explanation: each endpoint's offer is negotiated, and a new sdp offer results from this
* negotiation, with the compatible media from each part
*/
pjmedia_sdp_session *activeLocalSession_;
const pjmedia_sdp_session *activeLocalSession_;
/**
* The negotiated SDP remote session
* Explanation: each endpoint's offer is negotiated, and a new sdp offer results from this
* negotiation, with the compatible media from each part
*/
pjmedia_sdp_session *activeRemoteSession_;
const pjmedia_sdp_session *activeRemoteSession_;
/**
* Codec Map used for offer
......
......@@ -1467,7 +1467,7 @@ void sdp_media_update_cb(pjsip_inv_session *inv, pj_status_t status)
}
// Get active session sessions
const pjmedia_sdp_session *remote_sdp;
const pjmedia_sdp_session *remote_sdp = 0;
pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
if (pjmedia_sdp_validate(remote_sdp) != PJ_SUCCESS) {
......
......@@ -34,6 +34,6 @@
#include "logger.h"
// If condition A is false, print the error message in M and exit thread
#define CHECK(A, M, ...) if (!(A)) { ERROR(M, ##__VA_ARGS__); ost::Thread::exit(); }
#define RETURN_IF_FAIL(A, M, ...) if (!(A)) { ERROR(M, ##__VA_ARGS__); ost::Thread::exit(); }
#endif // CHECK_H_
......@@ -33,7 +33,6 @@
#include <algorithm>
#include <string>
#include <iostream>
#include <assert.h>
#include "cc_thread.h"
#include "logger.h"
......@@ -44,39 +43,42 @@ extern "C" {
}
namespace {
using std::string;
using std::map;
using std::vector;
static std::map<std::string, std::string> encoders;
static std::vector<std::string> video_codecs;
map<string, string> encoders_;
vector<string> installed_video_codecs_;
/* application wide mutex to protect concurrent access to avcodec */
static ost::Mutex avcodec_lock;
ost::Mutex avcodec_lock_;
void findInstalledVideoCodecs()
{
std::vector<std::string> libav_codecs;
vector<string> libav_codecs;
AVCodec *p = NULL;
while ((p = av_codec_next(p)))
if (p->type == AVMEDIA_TYPE_VIDEO)
libav_codecs.push_back(p->name);
std::map<std::string, std::string>::const_iterator it;
for (it = encoders.begin(); it != encoders.end(); ++it) {
for (map<string, string>::const_iterator it = encoders_.begin(); it != encoders_.end(); ++it) {
if (std::find(libav_codecs.begin(), libav_codecs.end(), it->second) != libav_codecs.end())
video_codecs.push_back(it->first);
installed_video_codecs_.push_back(it->first);
else
ERROR("Didn't find \"%s\" encoder\n", it->second.c_str());
ERROR("Didn't find \"%s\" encoder", it->second.c_str());
}
}
} // end anon namespace
namespace libav_utils {
std::vector<std::string> getVideoCodecList()
vector<string> getVideoCodecList()
{
return video_codecs;
if (installed_video_codecs_.empty())
findInstalledVideoCodecs();
return installed_video_codecs_;
}
static int avcodecManageMutex(void ** /*mutex*/, enum AVLockOp op)
......@@ -87,29 +89,29 @@ static int avcodecManageMutex(void ** /*mutex*/, enum AVLockOp op)
case AV_LOCK_DESTROY:
break; // our mutex doesn't need to be destroyed
case AV_LOCK_OBTAIN:
avcodec_lock.enter();
avcodec_lock_.enter();
break;
case AV_LOCK_RELEASE:
avcodec_lock.leave();
avcodec_lock_.leave();
break;
}
return 0;
}
std::map<std::string, std::string> encodersMap()
map<string, string> encodersMap()
{
return encoders;
return encoders_;
}
void sfl_avcodec_init()
{
static int done = 0;
ost::MutexLock lock(avcodec_lock);
static bool done = false;
ost::MutexLock lock(avcodec_lock_);
if (done)
return;
done = 1;
done = true;
av_register_all();
avdevice_register_all();
......@@ -120,10 +122,10 @@ void sfl_avcodec_init()
av_lockmgr_register(avcodecManageMutex);
/* list of codecs tested and confirmed to work */
encoders["H264"] = "libx264";
encoders["H263-2000"] = "h263p";
encoders["VP8"] = "libvpx";
encoders["MP4V-ES"] = "mpeg4";
encoders_["H264"] = "libx264";
encoders_["H263-2000"] = "h263p";
encoders_["VP8"] = "libvpx";
encoders_["MP4V-ES"] = "mpeg4";
//FFmpeg needs to be modified to allow us to send configuration
//inline, with CODEC_FLAG_GLOBAL_HEADER
......
......@@ -50,7 +50,6 @@ extern "C" {
#include <fstream>
#include "manager.h"
#include "dbus/video_controls.h"
#include "shared_memory.h"
static const enum PixelFormat VIDEO_RGB_FORMAT = PIX_FMT_BGRA;
......@@ -88,12 +87,12 @@ string openTemp(string path, std::ofstream& f)
void VideoReceiveThread::loadSDP()
{
assert(not args_["receiving_sdp"].empty());
RETURN_IF_FAIL(not args_["receiving_sdp"].empty(), "Cannot load empty SDP");
std::ofstream os;
sdpFilename_ = openTemp("/tmp", os);
os << args_["receiving_sdp"];
DEBUG("loaded SDP %s", args_["receiving_sdp"].c_str());
DEBUG("loaded SDP\n%s", args_["receiving_sdp"].c_str());
os.close();
}
......@@ -105,8 +104,6 @@ void VideoReceiveThread::setup()
dstWidth_ = atoi(args_["width"].c_str());
dstHeight_ = atoi(args_["height"].c_str());
AVInputFormat *file_iformat = 0;
std::string format_str;
std::string input;
if (args_["input"].empty()) {
......@@ -122,8 +119,8 @@ void VideoReceiveThread::setup()
}
DEBUG("Using %s format", format_str.c_str());
file_iformat = av_find_input_format(format_str.c_str());
CHECK(file_iformat, "Could not find format \"%s\"", format_str.c_str());
AVInputFormat *file_iformat = av_find_input_format(format_str.c_str());
RETURN_IF_FAIL(file_iformat, "Could not find format \"%s\"", format_str.c_str());
AVDictionary *options = NULL;
if (!args_["framerate"].empty())
......@@ -134,16 +131,17 @@ void VideoReceiveThread::setup()
av_dict_set(&options, "channel", args_["channel"].c_str(), 0);
// Open video file
int ret = avformat_open_input(&inputCtx_, input.c_str(), file_iformat, &options);
CHECK(ret == 0, "Could not open input \"%s\"", input.c_str());
DEBUG("Opening input");
int ret = avformat_open_input(&inputCtx_, input.c_str(), file_iformat, options ? &options : NULL);
RETURN_IF_FAIL(ret == 0, "Could not open input \"%s\"", input.c_str());
DEBUG("Finding stream info");
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 8, 0)
ret = av_find_stream_info(inputCtx_);
#else
ret = avformat_find_stream_info(inputCtx_, NULL);
ret = avformat_find_stream_info(inputCtx_, options ? &options : NULL);
#endif
if (ret < 0)
DEBUG("Could not find stream info!");
RETURN_IF_FAIL(ret >= 0, "Could not find stream info!");
// find the first video stream from the input
streamIndex_ = -1;
......@@ -151,24 +149,24 @@ void VideoReceiveThread::setup()
if (inputCtx_->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
streamIndex_ = i;
CHECK(streamIndex_ != -1, "Could not find video stream");
RETURN_IF_FAIL(streamIndex_ != -1, "Could not find video stream");
// Get a pointer to the codec context for the video stream
decoderCtx_ = inputCtx_->streams[streamIndex_]->codec;
// find the decoder for the video stream
AVCodec *inputDecoder = avcodec_find_decoder(decoderCtx_->codec_id);
CHECK(inputDecoder, "Unsupported codec");
RETURN_IF_FAIL(inputDecoder, "Unsupported codec");
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 6, 0)
ret = avcodec_open(decoderCtx_, inputDecoder);
#else
ret = avcodec_open2(decoderCtx_, inputDecoder, NULL);
#endif
CHECK(ret == 0, "Could not open codec");
RETURN_IF_FAIL(ret == 0, "Could not open codec");
scaledPicture_ = avcodec_alloc_frame();
CHECK(scaledPicture_, "Could not allocate output frame");
RETURN_IF_FAIL(scaledPicture_, "Could not allocate output frame");
if (dstWidth_ == 0 and dstHeight_ == 0) {
dstWidth_ = decoderCtx_->width;
......@@ -180,7 +178,8 @@ void VideoReceiveThread::setup()
try {
sharedMemory_.allocateBuffer(dstWidth_, dstHeight_, bufferSize);
} catch (const std::runtime_error &e) {
CHECK(false, "%s", e.what());
ERROR("%s", e.what());
ost::Thread::exit();
}
// allocate video frame
......@@ -195,7 +194,7 @@ void VideoReceiveThread::createScalingContext()
decoderCtx_->pix_fmt, dstWidth_,
dstHeight_, VIDEO_RGB_FORMAT,
SWS_BICUBIC, NULL, NULL, NULL);
CHECK(imgConvertCtx_, "Cannot init the conversion context!");
RETURN_IF_FAIL(imgConvertCtx_, "Cannot init the conversion context!");
}
VideoReceiveThread::VideoReceiveThread(const std::map<string, string> &args,
......@@ -212,6 +211,8 @@ void VideoReceiveThread::run()
createScalingContext();
receiving_ = true;
if (not args_["receiving_sdp"].empty())
sharedMemory_.publishShm();
while (receiving_) {
AVPacket inpacket;
......
......@@ -29,7 +29,6 @@
*/
#include "video_rtp_session.h"
#include <cassert>
#include <sstream>
#include <map>
#include <string>
......@@ -113,13 +112,19 @@ void VideoRtpSession::updateSDP(const Sdp &sdp)
void VideoRtpSession::updateDestination(const string &destination,
unsigned int port)
{
assert(not destination.empty());
if (destination.empty()) {
ERROR("Destination is empty, ignoring");
return;
}
std::stringstream tmp;
tmp << "rtp://" << destination << ":" << port;
// if destination has changed
if (tmp.str() != txArgs_["destination"]) {
assert(sendThread_.get() == 0);
if (sendThread_.get() != 0) {
ERROR("Video is already being sent");
return;
}
txArgs_["destination"] = tmp.str();
DEBUG("updated dest to %s", txArgs_["destination"].c_str());
}
......@@ -130,33 +135,6 @@ void VideoRtpSession::updateDestination(const string &destination,
}
}
void VideoRtpSession::test()
{
assert(sendThread_.get() == 0);
assert(receiveThread_.get() == 0);
sendThread_.reset(new VideoSendThread(txArgs_));
sendThread_->start();
/* block until SDP is ready */
sendThread_->waitForSDP();
}
void VideoRtpSession::test_loopback()
{
assert(sendThread_.get() == 0);
sendThread_.reset(new VideoSendThread(txArgs_));
sendThread_->start();
sendThread_->waitForSDP();
rxArgs_["input"] = "test.sdp";
VideoControls *controls(Manager::instance().getDbusManager()->getVideoControls());
sharedMemory_.reset(new SharedMemory(*controls));
receiveThread_.reset(new VideoReceiveThread(rxArgs_, *sharedMemory_));
receiveThread_->start();
}
void VideoRtpSession::start()
{
if (sending_) {
......@@ -175,7 +153,6 @@ void VideoRtpSession::start()
sharedMemory_.reset(new SharedMemory(*controls));
receiveThread_.reset(new VideoReceiveThread(rxArgs_, *sharedMemory_));
receiveThread_->start();
sharedMemory_->publishShm();
}
else
DEBUG("Video receiving disabled");
......
......@@ -51,8 +51,6 @@ class VideoRtpSession {
void start();
void stop();
void test();
void test_loopback();
void updateDestination(const std::string &destination,
unsigned int port);
void updateSDP(const Sdp &sdp);
......
......@@ -51,10 +51,11 @@ namespace sfl_video {
using std::string;
void VideoSendThread::print_and_save_sdp()
void VideoSendThread::print_sdp()
{
/* theora sdp can be huge */
const size_t sdp_size = outputCtx_->streams[0]->codec->extradata_size + 2048;
std::string sdp(sdp_size, 0); /* theora sdp can be huge */
std::string sdp(sdp_size, 0);
av_sdp_create(&outputCtx_, 1, &(*sdp.begin()), sdp_size);
std::istringstream iss(sdp);
string line;
......@@ -64,7 +65,7 @@ void VideoSendThread::print_and_save_sdp()
line = line.substr(0, line.length() - 1);
sdp_ += line + "\n";
}
DEBUG("%s", sdp_.c_str());
DEBUG("sending\n%s", sdp_.c_str());
sdpReady_.signal();
}
......@@ -83,16 +84,19 @@ void VideoSendThread::prepareEncoderContext(AVCodec *encoder)
{
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 12, 0)
encoderCtx_ = avcodec_alloc_context();
(void) encoder; /* unused */
avcodec_get_context_defaults(encoderCtx_);
(void) encoder;
#else
encoderCtx_ = avcodec_alloc_context3(encoder);
#endif
// set some encoder settings here
encoderCtx_->bit_rate = atoi(args_["bitrate"].c_str());
// emit one intra frame every gop_size frames
encoderCtx_->gop_size = 15;
encoderCtx_->max_b_frames = 0;
encoderCtx_->rtp_payload_size = 0; // Target GOB length
encoderCtx_->bit_rate = 400000;
encoderCtx_->rc_max_rate = encoderCtx_->bit_rate;
encoderCtx_->rc_min_rate = 0;
encoderCtx_->rc_buffer_size = encoderCtx_->rc_max_rate;
// resolution must be a multiple of two
if (args_["width"].empty() and inputDecoderCtx_)
encoderCtx_->width = inputDecoderCtx_->width;
......@@ -107,6 +111,11 @@ void VideoSendThread::prepareEncoderContext(AVCodec *encoder)
const int DEFAULT_FPS = 30;
const int fps = args_["framerate"].empty() ? DEFAULT_FPS : atoi(args_["framerate"].c_str());
encoderCtx_->time_base = (AVRational) {1, fps};
// emit one intra frame every gop_size frames
encoderCtx_->gop_size = 10 * fps;
encoderCtx_->max_b_frames = 0;
const int MTU = 1500;
encoderCtx_->rtp_payload_size = MTU / 2; // Target GOB length
encoderCtx_->pix_fmt = PIX_FMT_YUV420P;
// Fri Jul 22 11:37:59 EDT 2011:tmatth:XXX: DON'T set this, we want our
// pps and sps to be sent in-band for RTP
......@@ -123,7 +132,7 @@ void VideoSendThread::setup()
if (args_["input"].find(V4L_PATH) != std::string::npos) {
DEBUG("Using v4l2 format");
file_iformat = av_find_input_format("video4linux2");
CHECK(file_iformat, "Could not find format video4linux2");
RETURN_IF_FAIL(file_iformat, "Could not find format video4linux2");
}
AVDictionary *options = NULL;
......@@ -137,7 +146,7 @@ void VideoSendThread::setup()
// Open video file
int ret = avformat_open_input(&inputCtx_, args_["input"].c_str(),
file_iformat, &options);
CHECK(ret == 0, "Could not open input file %s", args_["input"].c_str());
RETURN_IF_FAIL(ret == 0, "Could not open input file %s", args_["input"].c_str());
// find the first video stream from the input
streamIndex_ = -1;
......@@ -145,27 +154,27 @@ void VideoSendThread::setup()
if (inputCtx_->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
streamIndex_ = i;
CHECK(streamIndex_ != -1, "Could not find video stream");
RETURN_IF_FAIL(streamIndex_ != -1, "Could not find video stream");
// Get a pointer to the codec context for the video stream
inputDecoderCtx_ = inputCtx_->streams[streamIndex_]->codec;
CHECK(inputDecoderCtx_, "Could not get input codec context");
RETURN_IF_FAIL(inputDecoderCtx_, "Could not get input codec context");
// find the decoder for the video stream
AVCodec *inputDecoder = avcodec_find_decoder(inputDecoderCtx_->codec_id);
CHECK(inputDecoder, "Could not decode video stream");
RETURN_IF_FAIL(inputDecoder, "Could not decode video stream");
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 6, 0)
ret = avcodec_open(inputDecoderCtx_, inputDecoder);
#else
ret = avcodec_open2(inputDecoderCtx_, inputDecoder, NULL);
#endif
CHECK(ret >= 0, "Could not open codec");
RETURN_IF_FAIL(ret >= 0, "Could not open codec");
outputCtx_ = avformat_alloc_context();
AVOutputFormat *file_oformat = av_guess_format("rtp", args_["destination"].c_str(), NULL);
CHECK(file_oformat, "Unable to find a suitable output format for %s",
RETURN_IF_FAIL(file_oformat, "Unable to find a suitable output format for %s",
args_["destination"].c_str());
outputCtx_->oformat = file_oformat;
......@@ -174,22 +183,28 @@ void VideoSendThread::setup()
/* find the video encoder */
AVCodec *encoder = avcodec_find_encoder_by_name(enc_name);
CHECK(encoder != 0, "Encoder \"%s\" not found!", enc_name);
RETURN_IF_FAIL(encoder != 0, "Encoder \"%s\" not found!", enc_name);
prepareEncoderContext(encoder);
/* 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
// 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
}
scaledPicture_ = avcodec_alloc_frame();
// open encoder
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 6, 0)
CHECK(avcodec_open(encoderCtx_, encoder) >= 0, "Could not open encoder")
RETURN_IF_FAIL(avcodec_open(encoderCtx_, encoder) >= 0, "Could not open encoder")
#else
CHECK(avcodec_open2(encoderCtx_, encoder, NULL) >= 0, "Could not open "
RETURN_IF_FAIL(avcodec_open2(encoderCtx_, encoder, NULL) >= 0, "Could not open "
"encoder")
#endif
......@@ -199,13 +214,13 @@ void VideoSendThread::setup()
#else
stream_ = avformat_new_stream(outputCtx_, 0);
#endif
CHECK(stream_ != 0, "Could not allocate stream.");
RETURN_IF_FAIL(stream_ != 0, "Could not allocate stream.");
stream_->codec = encoderCtx_;
// open the output file, if needed
if (!(file_oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&outputCtx_->pb, outputCtx_->filename, AVIO_FLAG_WRITE);
CHECK(ret >= 0, "Could not open \"%s\"!", outputCtx_->filename);
RETURN_IF_FAIL(ret >= 0, "Could not open \"%s\"!", outputCtx_->filename);
} else
DEBUG("No need to open \"%s\"", outputCtx_->filename);
......@@ -214,10 +229,10 @@ void VideoSendThread::setup()
if (!args_["payload_type"].empty())
av_dict_set(&options, "payload_type", args_["payload_type"].c_str(), 0);
CHECK(avformat_write_header(outputCtx_, &options) >= 0, "Could not write "
RETURN_IF_FAIL(avformat_write_header(outputCtx_, &options) >= 0, "Could not write "
"header for output file...check codec parameters");
print_and_save_sdp();
print_sdp();
av_dump_format(outputCtx_, 0, outputCtx_->filename, 1);
// allocate video frame
......@@ -248,7 +263,7 @@ void VideoSendThread::createScalingContext()
encoderCtx_->height,
encoderCtx_->pix_fmt, SWS_BICUBIC,