diff --git a/daemon/src/sip/sdp.h b/daemon/src/sip/sdp.h index 78636d97dab94fda4ec7290e9be38b45ff1bffff..c4e72551eb0050dc16079c61c3ed6f2b8a2ce382 100644 --- a/daemon/src/sip/sdp.h +++ b/daemon/src/sip/sdp.h @@ -202,6 +202,10 @@ class Sdp { return remoteVideoPort_; } + unsigned int getLocalVideoPort() const { + return localVideoPort_; + } + void addAttributeToLocalAudioMedia(const char *attr); void removeAttributeFromLocalAudioMedia(const char *attr); void addAttributeToLocalVideoMedia(const char *attr); diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp index 916b8f4e79703ac107a7d4034d817ad618bc4170..a40937da2e345cf7135f46059d032ef9af3f206b 100644 --- a/daemon/src/sip/sipvoiplink.cpp +++ b/daemon/src/sip/sipvoiplink.cpp @@ -1772,7 +1772,7 @@ void sdp_media_update_cb(pjsip_inv_session *inv, pj_status_t status) Manager::instance().getVideoControls()->stopPreview(); call->getVideoRtp().updateSDP(*call->getLocalSDP()); call->getVideoRtp().updateDestination(call->getLocalSDP()->getRemoteIP(), call->getLocalSDP()->getRemoteVideoPort()); - call->getVideoRtp().start(); + call->getVideoRtp().start(call->getLocalSDP()->getLocalVideoPort()); #endif // Get the crypto attribute containing srtp's cryptographic context (keys, cipher) diff --git a/daemon/src/video/Makefile.am b/daemon/src/video/Makefile.am index 4720bd22ad3732fa56418ead938126470fe81ca2..f479f9ada3386d990e00bfc3577b70d97078a41d 100644 --- a/daemon/src/video/Makefile.am +++ b/daemon/src/video/Makefile.am @@ -10,7 +10,8 @@ libvideo_la_SOURCES = libav_utils.cpp libav_utils.h video_rtp_session.cpp \ video_v4l2_list.cpp video_v4l2.h video_v4l2_list.h \ video_preferences.h video_preferences.cpp \ packet_handle.h packet_handle.cpp check.h shm_header.h \ - shm_sink.cpp shm_sink.h video_provider.h + shm_sink.cpp shm_sink.h video_provider.h \ + socket_pair.cpp socket_pair.h libvideo_la_LIBADD = @LIBAVCODEC_LIBS@ @LIBAVFORMAT_LIBS@ @LIBAVDEVICE_LIBS@ @LIBSWSCALE_LIBS@ @LIBAVUTIL_LIBS@ @UDEV_LIBS@ diff --git a/daemon/src/video/socket_pair.cpp b/daemon/src/video/socket_pair.cpp new file mode 100644 index 0000000000000000000000000000000000000000..54fd118e7b16871079d31449e5816a7c76ffb7df --- /dev/null +++ b/daemon/src/video/socket_pair.cpp @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2004-2013 Savoir-Faire Linux Inc. + * Copyright (c) 2002 Fabrice Bellard + * + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#include "socket_pair.h" +#include "scoped_lock.h" +#include "logger.h" +#include <cstring> +#include <stdexcept> +#include <unistd.h> +#include <poll.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + + +extern "C" { +#include <libavutil/avstring.h> +#include <libavformat/avformat.h> +} + +namespace { + +int ff_network_wait_fd(int fd) +{ + struct pollfd p = { .fd = fd, .events = POLLOUT, .revents = 0 }; + int ret; + ret = poll(&p, 1, 100); + return ret < 0 ? errno : p.revents & (POLLOUT | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN); +} + +struct addrinfo* +udp_resolve_host(const char *node, int service) +{ + struct addrinfo hints = { 0 }, *res = 0; + int error; + char sport[16]; + + snprintf(sport, sizeof(sport), "%d", service); + + hints.ai_socktype = SOCK_DGRAM; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + if ((error = getaddrinfo(node, sport, &hints, &res))) { + res = NULL; + ERROR("%s\n", gai_strerror(error)); + } + + return res; +} + +int udp_set_url(struct sockaddr_storage *addr, + const char *hostname, int port) +{ + struct addrinfo *res0; + int addr_len; + + res0 = udp_resolve_host(hostname, port); + if (res0 == 0) return AVERROR(EIO); + memcpy(addr, res0->ai_addr, res0->ai_addrlen); + addr_len = res0->ai_addrlen; + freeaddrinfo(res0); + + return addr_len; +} + +int +udp_socket_create(sockaddr_storage *addr, socklen_t *addr_len, + int local_port) +{ + int udp_fd = -1; + struct addrinfo *res0 = NULL, *res = NULL; + + res0 = udp_resolve_host(0, local_port); + if (res0 == 0) + goto fail; + for (res = res0; res; res=res->ai_next) { + udp_fd = socket(res->ai_family, SOCK_DGRAM | SOCK_NONBLOCK, 0); + if (udp_fd != -1) break; + ERROR("socket error"); + } + + if (udp_fd < 0) + goto fail; + + memcpy(addr, res->ai_addr, res->ai_addrlen); + *addr_len = res->ai_addrlen; + +#if HAVE_SDP_CUSTOM_IO + // bind socket so that we send from and receive + // on local port + bind(udp_fd, reinterpret_cast<sockaddr*>(addr), *addr_len); +#endif + + freeaddrinfo(res0); + + return udp_fd; + + fail: + if (udp_fd >= 0) + close(udp_fd); + if (res0) + freeaddrinfo(res0); + return -1; +} + +const int RTP_BUFFER_SIZE = 1472; + +} + +namespace sfl_video { + +SocketPair::SocketPair(const char *uri, int localPort) : + rtcpWriteMutex_(), + rtpHandle_(0), + rtcpHandle_(0), + interrupted_(false) +{ + pthread_mutex_init(&rtcpWriteMutex_, NULL); + openSockets(uri, localPort); +} + +SocketPair::~SocketPair() +{ + interrupted_ = true; + closeSockets(); + + // destroy in reverse order + pthread_mutex_destroy(&rtcpWriteMutex_); +} + +void +SocketPair::interrupt() +{ + interrupted_ = true; +} + +void +SocketPair::closeSockets() +{ + if (rtcpHandle_ > 0 and close(rtcpHandle_)) + ERROR("%s", strerror(errno)); + if (rtpHandle_ > 0 and close(rtpHandle_)) + ERROR("%s", strerror(errno)); +} + + +void +SocketPair::openSockets(const char *uri, int local_rtp_port) +{ + char hostname[256]; + char buf[1024]; + char path[1024]; + + int rtp_port; + av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port, + path, sizeof(path), uri); + + const int rtcp_port = rtp_port + 1; + +#if HAVE_SDP_CUSTOM_IO + const int local_rtcp_port = local_rtp_port + 1; +#else + WARN("libavformat too old for socket reuse, using random source ports"); + local_rtp_port = 0; + const int local_rtcp_port = 0; +#endif + + sockaddr_storage rtp_addr, rtcp_addr; + socklen_t rtp_len, rtcp_len; + + // Open sockets and store addresses for sending + if ((rtpHandle_ = udp_socket_create(&rtp_addr, &rtp_len, local_rtp_port)) == -1 or + (rtcpHandle_ = udp_socket_create(&rtcp_addr, &rtcp_len, local_rtcp_port)) == -1 or + (rtpDestAddrLen_ = udp_set_url(&rtpDestAddr_, hostname, rtp_port)) < 0 or + (rtcpDestAddrLen_ = udp_set_url(&rtcpDestAddr_, hostname, rtcp_port)) < 0) { + + // Handle failed socket creation + closeSockets(); + throw std::runtime_error("Socket creation failed"); + } +} + +AVIOContext * +SocketPair::createAVIOContext() +{ + // FIXME: Caller must free buffer! + unsigned char *buffer(static_cast<unsigned char *>(av_malloc(RTP_BUFFER_SIZE))); + + AVIOContext *context = avio_alloc_context(buffer, + RTP_BUFFER_SIZE, 1, reinterpret_cast<void*>(this), + &readCallback, &writeCallback, 0); + + context->max_packet_size = RTP_BUFFER_SIZE; + return context; +} + +int +SocketPair::readCallback(void *opaque, uint8_t *buf, int buf_size) +{ + SocketPair *context = static_cast<SocketPair*>(opaque); + + struct sockaddr_storage from; + socklen_t from_len; + int len, n; + struct pollfd p[2] = { {context->rtpHandle_, POLLIN, 0}, {context->rtcpHandle_, POLLIN, 0}}; + + for(;;) { + if (context->interrupted_) + return AVERROR_EXIT; + + /* build fdset to listen to RTP and RTCP packets */ + n = poll(p, 2, 100); + if (n > 0) { + /* first try RTCP */ + if (p[1].revents & POLLIN) { + from_len = sizeof(from); + + { + len = recvfrom(context->rtcpHandle_, buf, buf_size, 0, + (struct sockaddr *)&from, &from_len); + } + + if (len < 0) { + if (errno == EAGAIN or errno == EINTR) + continue; + return AVERROR(EIO); + } + break; + } + /* then RTP */ + if (p[0].revents & POLLIN) { + from_len = sizeof(from); + + { + len = recvfrom(context->rtpHandle_, buf, buf_size, 0, + (struct sockaddr *)&from, &from_len); + } + + if (len < 0) { + if (errno == EAGAIN or errno == EINTR) + continue; + return AVERROR(EIO); + } + break; + } + } else if (n < 0) { + if (errno == EINTR) + continue; + return AVERROR(EIO); + } + } + return len; +} + +/* RTCP packet types */ +enum RTCPType { + RTCP_FIR = 192, + RTCP_IJ = 195, + RTCP_SR = 200, + RTCP_TOKEN = 210 +}; + +#define RTP_PT_IS_RTCP(x) (((x) >= RTCP_FIR && (x) <= RTCP_IJ) || \ + ((x) >= RTCP_SR && (x) <= RTCP_TOKEN)) + +int +SocketPair::writeCallback(void *opaque, uint8_t *buf, int buf_size) +{ + SocketPair *context = static_cast<SocketPair*>(opaque); + + int ret; + + if (RTP_PT_IS_RTCP(buf[1])) { + /* RTCP payload type */ + sfl::ScopedLock lock(context->rtcpWriteMutex_); + ret = ff_network_wait_fd(context->rtcpHandle_); + if (ret < 0) + return ret; + + ret = sendto(context->rtcpHandle_, buf, buf_size, 0, + (sockaddr*) &context->rtcpDestAddr_, context->rtcpDestAddrLen_); + } else { + /* RTP payload type */ + ret = ff_network_wait_fd(context->rtpHandle_); + if (ret < 0) + return ret; + + ret = sendto(context->rtpHandle_, buf, buf_size, 0, + (sockaddr*) &context->rtpDestAddr_, context->rtpDestAddrLen_); + } + + return ret < 0 ? errno : ret; +} + +} diff --git a/daemon/src/video/socket_pair.h b/daemon/src/video/socket_pair.h new file mode 100644 index 0000000000000000000000000000000000000000..f1c12663e25cc551b5dce60f8c92da281eb21768 --- /dev/null +++ b/daemon/src/video/socket_pair.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2004-2013 Savoir-Faire Linux Inc. + * Copyright (C) 2012 VLC authors and VideoLAN + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#ifndef SOCKET_PAIR_H_ +#define SOCKET_PAIR_H_ + +#include <sys/socket.h> +#include <pthread.h> +#include <stdint.h> + +// FIXME: newer libavformat allows us to do forward declaration +// of AVIOContext and thus avoid this include +extern "C" { +#include <libavformat/avio.h> +} + +/* LIBAVFORMAT_VERSION_CHECK checks for the right version of libav and FFmpeg + * a is the major version + * b and c the minor and micro versions of libav + * d and e the minor and micro versions of FFmpeg */ +#define LIBAVFORMAT_VERSION_CHECK( a, b, c, d, e ) \ + ( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \ + (LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) ) + +#define HAVE_SDP_CUSTOM_IO LIBAVFORMAT_VERSION_CHECK(54,20,3,59,103) + +namespace sfl_video { + +class VideoSendThread; +class VideoReceiveThread; + +class SocketPair { + public: + SocketPair(const char *uri, int localPort); + ~SocketPair(); + + void interrupt(); + + AVIOContext * + createAVIOContext(); + + void openSockets(const char *uri, int localPort); + void closeSockets(); + static int readCallback(void *opaque, uint8_t *buf, int buf_size); + static int writeCallback(void *opaque, uint8_t *buf, int buf_size); + + pthread_mutex_t rtcpWriteMutex_; + + int rtpHandle_; + int rtcpHandle_; + sockaddr_storage rtpDestAddr_; + socklen_t rtpDestAddrLen_; + sockaddr_storage rtcpDestAddr_; + socklen_t rtcpDestAddrLen_; + bool interrupted_; +}; + +} + +#endif // SOCKET_PAIR_H_ diff --git a/daemon/src/video/test/test_video_rtp.cpp b/daemon/src/video/test/test_video_rtp.cpp index 842e73dc38a3f6bb5fcb4b7f8aa767c2e9292aa8..4259a0d9f3d2968693255c7f24b7356b51c0afa1 100644 --- a/daemon/src/video/test/test_video_rtp.cpp +++ b/daemon/src/video/test/test_video_rtp.cpp @@ -40,8 +40,8 @@ int main () { VideoPreference preference; sfl_video::VideoRtpSession session("test", preference.getSettings()); - session.start(); - sleep(10); + session.start(12345); + sleep(5); session.stop(); return 0; diff --git a/daemon/src/video/video_receive_thread.cpp b/daemon/src/video/video_receive_thread.cpp index c60b7c149f7a2169745fff3d99967ee1a27b4ebf..a0721517dac761085630073cb90b1682535bb012 100644 --- a/daemon/src/video/video_receive_thread.cpp +++ b/daemon/src/video/video_receive_thread.cpp @@ -30,6 +30,7 @@ */ #include "video_receive_thread.h" +#include "socket_pair.h" #include "dbus/video_controls.h" #include "check.h" #include "packet_handle.h" @@ -114,12 +115,19 @@ void VideoReceiveThread::setup() if (!args_["channel"].empty()) av_dict_set(&options, "channel", args_["channel"].c_str(), 0); - // Open video file + // Open Camera or SDP Demuxer inputCtx_ = avformat_alloc_context(); inputCtx_->interrupt_callback = interruptCb_; if (input == SDP_FILENAME) { +#if HAVE_SDP_CUSTOM_IO + // custom_io so the SDP demuxer will not open any UDP connections + av_dict_set(&options, "sdp_flags", "custom_io", 0); +#else + WARN("libavformat too old for custom SDP demuxing"); +#endif + EXIT_IF_FAIL(not stream_.str().empty(), "No SDP loaded"); - inputCtx_->pb = avioContext_.get(); + inputCtx_->pb = sdpContext_.get(); } int ret = avformat_open_input(&inputCtx_, input.c_str(), file_iformat, options ? &options : NULL); @@ -129,6 +137,18 @@ void VideoReceiveThread::setup() EXIT_IF_FAIL(false, "Could not open input \"%s\"", input.c_str()); } + if (input == SDP_FILENAME) { +#if HAVE_SDP_CUSTOM_IO + // Now replace our custom AVIOContext with one that will read + // packets + inputCtx_->pb = demuxContext_.get(); +#endif + } + + // FIXME: this is a hack because our peer sends us RTP before + // we're ready for it, and we miss the SPS/PPS. We should be + // ready earlier. + sleep(1); DEBUG("Finding stream info"); if (requestKeyFrameCallback_) requestKeyFrameCallback_(id_); @@ -225,15 +245,26 @@ VideoReceiveThread::VideoReceiveThread(const std::string &id, const std::map<str requestKeyFrameCallback_(0), sdpBuffer_(reinterpret_cast<unsigned char*>(av_malloc(SDP_BUFFER_SIZE)), &av_free), stream_(args_["receiving_sdp"]), - avioContext_(avio_alloc_context(sdpBuffer_.get(), SDP_BUFFER_SIZE, 0, + sdpContext_(avio_alloc_context(sdpBuffer_.get(), SDP_BUFFER_SIZE, 0, reinterpret_cast<void*>(static_cast<std::istream*>(&stream_)), &readFunction, 0, 0), &av_free), + demuxContext_(), thread_(0) { interruptCb_.callback = interruptCb; interruptCb_.opaque = this; } +void +VideoReceiveThread::addIOContext(SocketPair &socketPair) +{ +#if HAVE_SDP_CUSTOM_IO + demuxContext_.reset(socketPair.createAVIOContext(), &av_free); +#else + (void) socketPair; +#endif +} + void VideoReceiveThread::start() { threadRunning_ = true; @@ -285,8 +316,11 @@ struct VideoRxContextHandle { if (rx_.decoderCtx_) avcodec_close(rx_.decoderCtx_); + if (rx_.demuxContext_ and rx_.demuxContext_->buffer) + av_free(rx_.demuxContext_->buffer); + if (rx_.inputCtx_ and rx_.inputCtx_->nb_streams > 0) { -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 8, 0) +#if LIBAVFORMAT_VERSION_MAJOR < 55 av_close_input_file(rx_.inputCtx_); #else avformat_close_input(&rx_.inputCtx_); diff --git a/daemon/src/video/video_receive_thread.h b/daemon/src/video/video_receive_thread.h index 31586b8162ea365f0b564c42766920e5035da8fe..46bbfb80462c0c8d028a10900cd366b246958668 100644 --- a/daemon/src/video/video_receive_thread.h +++ b/daemon/src/video/video_receive_thread.h @@ -51,6 +51,8 @@ class AVFormatContext; class AVFrame; namespace sfl_video { +class SocketPair; + class VideoReceiveThread : public VideoProvider { private: NON_COPYABLE(VideoReceiveThread); @@ -79,7 +81,8 @@ class VideoReceiveThread : public VideoProvider { void (* requestKeyFrameCallback_)(const std::string &); std::tr1::shared_ptr<unsigned char> sdpBuffer_; std::istringstream stream_; - std::tr1::shared_ptr<AVIOContext> avioContext_; + std::tr1::shared_ptr<AVIOContext> sdpContext_; + std::tr1::shared_ptr<AVIOContext> demuxContext_; void setup(); void openDecoder(); @@ -95,6 +98,7 @@ class VideoReceiveThread : public VideoProvider { public: VideoReceiveThread(const std::string &id, const std::map<std::string, std::string> &args); + void addIOContext(SocketPair &socketPair); void addDetails(std::map<std::string, std::string> &details); ~VideoReceiveThread(); void start(); diff --git a/daemon/src/video/video_rtp_session.cpp b/daemon/src/video/video_rtp_session.cpp index 506abcad183ba19b730a7e0539819952d846bb43..ff85fbdb2ce4a0c9fbe2406c51b08bfc833b83bd 100644 --- a/daemon/src/video/video_rtp_session.cpp +++ b/daemon/src/video/video_rtp_session.cpp @@ -34,6 +34,7 @@ #include <string> #include "video_send_thread.h" #include "video_receive_thread.h" +#include "socket_pair.h" #include "sip/sdp.h" #include "sip/sipvoiplink.h" #include "manager.h" @@ -45,10 +46,15 @@ using std::map; using std::string; VideoRtpSession::VideoRtpSession(const string &callID, const map<string, string> &txArgs) : - sendThread_(), receiveThread_(), txArgs_(txArgs), + socketPair_(), sendThread_(), receiveThread_(), txArgs_(txArgs), rxArgs_(), sending_(false), receiving_(false), callID_(callID) {} +VideoRtpSession::~VideoRtpSession() +{ + stop(); +} + void VideoRtpSession::updateSDP(const Sdp &sdp) { string desc(sdp.getIncomingVideoDescription()); @@ -117,12 +123,18 @@ void VideoRtpSession::updateDestination(const string &destination, } } -void VideoRtpSession::start() +void VideoRtpSession::start(int localPort) { + if (not sending_ and not receiving_) + return; + + socketPair_.reset(new SocketPair(txArgs_["destination"].c_str(), localPort)); + if (sending_) { if (sendThread_.get()) WARN("Restarting video sender"); sendThread_.reset(new VideoSendThread("local", txArgs_)); + sendThread_->addIOContext(*socketPair_); sendThread_->start(); } else { DEBUG("Video sending disabled"); @@ -134,6 +146,7 @@ void VideoRtpSession::start() WARN("restarting video receiver"); receiveThread_.reset(new VideoReceiveThread(callID_, rxArgs_)); receiveThread_->setRequestKeyFrameCallback(&SIPVoIPLink::enqueueKeyframeRequest); + receiveThread_->addIOContext(*socketPair_); receiveThread_->start(); } else { DEBUG("Video receiving disabled"); @@ -143,8 +156,11 @@ void VideoRtpSession::start() void VideoRtpSession::stop() { + if (socketPair_.get()) + socketPair_->interrupt(); receiveThread_.reset(); sendThread_.reset(); + socketPair_.reset(); } void VideoRtpSession::forceKeyFrame() diff --git a/daemon/src/video/video_rtp_session.h b/daemon/src/video/video_rtp_session.h index 7546ac8852ecfe6bddfc5aa3c071b7717ef71b09..3c9aec07fb2ff4414b2827134a98d36090988f4a 100644 --- a/daemon/src/video/video_rtp_session.h +++ b/daemon/src/video/video_rtp_session.h @@ -41,12 +41,15 @@ namespace sfl_video { class VideoSendThread; class VideoReceiveThread; +class SocketPair; class VideoRtpSession { public: VideoRtpSession(const std::string &callID, const std::map<std::string, std::string> &txArgs); - void start(); + ~VideoRtpSession(); + + void start(int localPort); void stop(); void updateDestination(const std::string &destination, unsigned int port); @@ -55,6 +58,7 @@ class VideoRtpSession { void addReceivingDetails(std::map<std::string, std::string> &details); private: + std::tr1::shared_ptr<SocketPair> socketPair_; std::tr1::shared_ptr<VideoSendThread> sendThread_; std::tr1::shared_ptr<VideoReceiveThread> receiveThread_; std::map<std::string, std::string> txArgs_; diff --git a/daemon/src/video/video_send_thread.cpp b/daemon/src/video/video_send_thread.cpp index 5decc71a3739cfdf1e042d5563c860652ac3b3ae..9b7b68d8b9da117629f5926207abf5126ce0f52c 100644 --- a/daemon/src/video/video_send_thread.cpp +++ b/daemon/src/video/video_send_thread.cpp @@ -29,6 +29,7 @@ */ #include "video_send_thread.h" +#include "socket_pair.h" #include "dbus/video_controls.h" #include "packet_handle.h" #include "check.h" @@ -284,12 +285,10 @@ void VideoSendThread::setup() EXIT_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_open2(&outputCtx_->pb, outputCtx_->filename, AVIO_FLAG_WRITE, &interruptCb_, NULL); - EXIT_IF_FAIL(ret >= 0, "Could not open \"%s\"!", outputCtx_->filename); - } else - DEBUG("No need to open \"%s\"", outputCtx_->filename); + // Set our output AVIOcontext + outputCtx_->pb = muxContext_.get(); + outputCtx_->interrupt_callback = interruptCb_; + outputCtx_->packet_size = outputCtx_->pb->buffer_size; AVDictionary *outOptions = NULL; // write the stream header, if any @@ -306,7 +305,6 @@ void VideoSendThread::setup() av_dump_format(outputCtx_, 0, outputCtx_->filename, 1); print_sdp(); - // allocate buffers for both scaled (pre-encoder) and encoded frames encoderBufferSize_ = avpicture_get_size(encoderCtx_->pix_fmt, encoderCtx_->width, encoderCtx_->height); @@ -351,12 +349,19 @@ VideoSendThread::VideoSendThread(const std::string &id, const std::map<string, s threadRunning_(false), forceKeyFrame_(0), thread_(0), - frameNumber_(0) + frameNumber_(0), + muxContext_() { interruptCb_.callback = interruptCb; interruptCb_.opaque = this; } +void +VideoSendThread::addIOContext(SocketPair &socketPair) +{ + muxContext_.reset(socketPair.createAVIOContext(), &av_free); +} + struct VideoTxContextHandle { VideoTxContextHandle(VideoSendThread &tx) : tx_(tx) {} @@ -373,10 +378,11 @@ struct VideoTxContextHandle { // was freed on av_codec_close() if (tx_.outputCtx_ and tx_.outputCtx_->priv_data) { av_write_trailer(tx_.outputCtx_); - if (tx_.outputCtx_->pb) - avio_close(tx_.outputCtx_->pb); } + if (tx_.muxContext_ and tx_.muxContext_->buffer) + av_free(tx_.muxContext_->buffer); + if (tx_.scaledInputBuffer_) av_free(tx_.scaledInputBuffer_); @@ -402,12 +408,13 @@ struct VideoTxContextHandle { avcodec_close(tx_.inputDecoderCtx_); // close the video file - if (tx_.inputCtx_) -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 8, 0) + if (tx_.inputCtx_) { +#if LIBAVFORMAT_VERSION_MAJOR < 55 av_close_input_file(tx_.inputCtx_); #else - avformat_close_input(&tx_.inputCtx_); + avformat_close_input(&tx_.inputCtx_); #endif + } } VideoSendThread &tx_; }; diff --git a/daemon/src/video/video_send_thread.h b/daemon/src/video/video_send_thread.h index f41b4865b54e2d5fb412720fb92b7eb5a08d3558..f2c94679bff474a3b8ceb1e3f5f25c3a54f38381 100644 --- a/daemon/src/video/video_send_thread.h +++ b/daemon/src/video/video_send_thread.h @@ -33,6 +33,7 @@ #include <map> #include <string> +#include <tr1/memory> #include "noncopyable.h" #include "shm_sink.h" #include "video_provider.h" @@ -50,6 +51,8 @@ class AVCodec; namespace sfl_video { +class SocketPair; + class VideoSendThread : public VideoProvider { private: NON_COPYABLE(VideoSendThread); @@ -89,6 +92,7 @@ class VideoSendThread : public VideoProvider { static void *runCallback(void *); pthread_t thread_; int frameNumber_; + std::tr1::shared_ptr<AVIOContext> muxContext_; void run(); bool captureFrame(); void renderFrame(); @@ -98,6 +102,7 @@ class VideoSendThread : public VideoProvider { public: VideoSendThread(const std::string &id, const std::map<std::string, std::string> &args); ~VideoSendThread(); + void addIOContext(SocketPair &sock); void start(); std::string getSDP() const { return sdp_; } void forceKeyFrame();