Commit 58f3fa5f authored by Guillaume Roguez's avatar Guillaume Roguez

* #25259: separate libav code into dedicated classes

parent 81a5bcb3
......@@ -3,15 +3,19 @@ include $(top_srcdir)/globals.mak
SUBDIRS=test
noinst_LTLIBRARIES = libvideo.la
libvideo_la_SOURCES = libav_utils.cpp libav_utils.h video_rtp_session.cpp \
video_rtp_session.h video_send_thread.h video_send_thread.cpp \
video_receive_thread.h video_receive_thread.cpp \
video_preview.h video_preview.cpp video_v4l2.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 \
socket_pair.cpp socket_pair.h
libvideo_la_SOURCES = \
libav_utils.cpp libav_utils.h video_rtp_session.cpp \
video_rtp_session.h video_send_thread.h video_send_thread.cpp \
video_receive_thread.h video_receive_thread.cpp \
video_preview.h video_preview.cpp video_v4l2.cpp \
video_v4l2_list.cpp video_v4l2.h video_v4l2_list.h \
video_preferences.h video_preferences.cpp \
check.h shm_header.h \
shm_sink.cpp shm_sink.h video_provider.h \
socket_pair.cpp socket_pair.h \
video_decoder.cpp video_decoder.h \
video_encoder.cpp video_encoder.h
video_codec.h
libvideo_la_LIBADD = @LIBAVCODEC_LIBS@ @LIBAVFORMAT_LIBS@ @LIBAVDEVICE_LIBS@ @LIBSWSCALE_LIBS@ @LIBAVUTIL_LIBS@ @UDEV_LIBS@
......
......@@ -42,11 +42,6 @@
#include <netdb.h>
extern "C" {
#include <libavutil/avstring.h>
#include <libavformat/avformat.h>
}
namespace {
int ff_network_wait_fd(int fd)
......@@ -60,10 +55,11 @@ int ff_network_wait_fd(int fd)
struct addrinfo*
udp_resolve_host(const char *node, int service)
{
struct addrinfo hints = { 0 }, *res = 0;
struct addrinfo hints, *res = 0;
int error;
char sport[16];
memset(&hints, 0, sizeof(hints));
snprintf(sport, sizeof(sport), "%d", service);
hints.ai_socktype = SOCK_DGRAM;
......@@ -146,7 +142,8 @@ SocketPair::SocketPair(const char *uri, int localPort) :
rtpDestAddrLen_(),
rtcpDestAddr_(),
rtcpDestAddrLen_(),
interrupted_(false)
interrupted_(false),
ioHandle_(0)
{
pthread_mutex_init(&rtcpWriteMutex_, NULL);
openSockets(uri, localPort);
......@@ -159,16 +156,17 @@ SocketPair::~SocketPair()
// destroy in reverse order
pthread_mutex_destroy(&rtcpWriteMutex_);
if (!ioHandle_)
delete ioHandle_;
}
void
SocketPair::interrupt()
void SocketPair::interrupt()
{
interrupted_ = true;
}
void
SocketPair::closeSockets()
void SocketPair::closeSockets()
{
if (rtcpHandle_ > 0 and close(rtcpHandle_))
ERROR("%s", strerror(errno));
......@@ -176,12 +174,9 @@ SocketPair::closeSockets()
ERROR("%s", strerror(errno));
}
void
SocketPair::openSockets(const char *uri, int local_rtp_port)
void SocketPair::openSockets(const char *uri, int local_rtp_port)
{
char hostname[256];
char buf[1024];
char path[1024];
int rtp_port;
......@@ -213,18 +208,13 @@ SocketPair::openSockets(const char *uri, int local_rtp_port)
}
}
AVIOContext *
SocketPair::createAVIOContext()
VideoIOHandle* SocketPair::getIOContext()
{
// 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;
if (!ioHandle_)
ioHandle_ = new VideoIOHandle(RTP_BUFFER_SIZE,
&readCallback, &writeCallback, 0,
reinterpret_cast<void*>(this));
return ioHandle_;
}
int
......@@ -235,7 +225,8 @@ SocketPair::readCallback(void *opaque, uint8_t *buf, int buf_size)
struct sockaddr_storage from;
socklen_t from_len;
int len, n;
struct pollfd p[2] = { {context->rtpHandle_, POLLIN, 0}, {context->rtcpHandle_, POLLIN, 0}};
struct pollfd p[2] = { {context->rtpHandle_, POLLIN, 0},
{context->rtcpHandle_, POLLIN, 0}};
for(;;) {
if (context->interrupted_)
......
......@@ -32,41 +32,24 @@
#ifndef SOCKET_PAIR_H_
#define SOCKET_PAIR_H_
#include "video_base.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 VideoSendThread;
class VideoReceiveThread;
class SocketPair {
public:
class SocketPair {
public:
SocketPair(const char *uri, int localPort);
~SocketPair();
void interrupt();
AVIOContext *
createAVIOContext();
VideoIOHandle* getIOContext();
void openSockets(const char *uri, int localPort);
void closeSockets();
static int readCallback(void *opaque, uint8_t *buf, int buf_size);
......@@ -81,7 +64,11 @@ class SocketPair {
sockaddr_storage rtcpDestAddr_;
socklen_t rtcpDestAddrLen_;
bool interrupted_;
};
VideoIOHandle *ioHandle_;
private:
NON_COPYABLE(SocketPair);
};
}
......
/*
* Copyright (C) 2013 Savoir-Faire Linux Inc.
* Author: Guillaume Roguez <Guillaume.Roguez@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 _VIDEO_BASE_H_
#define _VIDEO_BASE_H_
#include "noncopyable.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libswscale/swscale.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)
typedef int(*io_readcallback)(void *opaque, uint8_t *buf, int buf_size);
typedef int(*io_writecallback)(void *opaque, uint8_t *buf, int buf_size);
typedef int64_t(*io_seekcallback)(void *opaque, int64_t offset, int whence);
class AVPacket;
class AVDictionary;
namespace sfl_video {
class VideoPacket {
public:
~VideoPacket() { av_free_packet(&inpacket_); };
AVPacket* get() const { return (AVPacket*)(&inpacket_); }
private:
AVPacket inpacket_;
};
class VideoIOHandle {
public:
VideoIOHandle(ssize_t buffer_size,
io_readcallback read_cb,
io_writecallback write_cb,
io_seekcallback seek_cb,
void *opaque) : ctx_(0), buf_(0)
{
buf_ = static_cast<unsigned char *>(av_malloc(buffer_size));
ctx_ = avio_alloc_context(buf_, buffer_size, 1, opaque, read_cb,
write_cb, seek_cb);
ctx_->max_packet_size = buffer_size;
}
~VideoIOHandle() { av_free(ctx_); av_free(buf_); }
AVIOContext *get() { return ctx_; }
private:
NON_COPYABLE(VideoIOHandle);
AVIOContext *ctx_;
unsigned char *buf_;
};
class VideoFrame {
public:
VideoFrame() : frame_(avcodec_alloc_frame()) {}
~VideoFrame() { avcodec_free_frame(&frame_); };
AVFrame *get() { return frame_; }
private:
NON_COPYABLE(VideoFrame);
AVFrame *frame_;
};
class VideoCodec {
public:
VideoCodec() : options_(0) {};
virtual ~VideoCodec() {};
static size_t getBufferSize(PixelFormat pix_fmt, int width, int height)
{
return avpicture_get_size(pix_fmt, width, height);
}
void setOption(const char *name, const char *value)
{
av_dict_set(&options_, name, value, 0);
}
private:
NON_COPYABLE(VideoCodec);
protected:
AVDictionary *options_;
};
}
#endif // _VIDEO_BASE_H_
/*
* Copyright (C) 2013 Savoir-Faire Linux Inc.
* Author: Guillaume Roguez <Guillaume.Roguez@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 "video_decoder.h"
#include "check.h"
#include <iostream>
// libav includes
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
}
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 28, 0)
// fallback to av_freep for older libavcodec
#define avcodec_free_frame av_freep
#endif
namespace sfl_video {
using std::string;
VideoDecoder::VideoDecoder() :
inputDecoder_(0),
decoderCtx_(0),
rawFrame_(0),
inputCtx_(avformat_alloc_context()),
imgConvertCtx_(0),
interruptCb_(),
scalerCtx_(0),
scaledPicture_(),
streamIndex_(-1),
dstWidth_(0),
dstHeight_(0)
{ }
VideoDecoder::~VideoDecoder()
{
if (decoderCtx_)
avcodec_close(decoderCtx_);
if (inputCtx_ and inputCtx_->nb_streams > 0) {
#if LIBAVFORMAT_VERSION_MAJOR < 54
av_close_input_file(inputCtx_);
#else
avformat_close_input(&inputCtx_);
#endif
}
sws_freeContext(scalerCtx_);
avcodec_free_frame(&rawFrame_);
}
int VideoDecoder::openInput(const std::string &source_str,
const std::string &format_str)
{
AVInputFormat *iformat = av_find_input_format(format_str.c_str());
if (!iformat) {
ERROR("Cannot find format \"%s\"", format_str.c_str());
return -1;
}
rawFrame_ = avcodec_alloc_frame();
if (!rawFrame_){
ERROR("avcodec_alloc_frame failed");
return -1;
}
int ret = avformat_open_input(&inputCtx_, source_str.c_str(), iformat,
NULL);
if (ret)
ERROR("avformat_open_input failed (%d)", ret);
return ret;
}
void VideoDecoder::setInterruptCallback(int (*cb)(void*), void *opaque)
{
if (cb) {
interruptCb_.callback = cb;
interruptCb_.opaque = opaque;
inputCtx_->interrupt_callback = interruptCb_;
} else {
inputCtx_->interrupt_callback.callback = 0;
}
}
void VideoDecoder::setIOContext(VideoIOHandle *ioctx)
{
inputCtx_->pb = ioctx->get();
}
int VideoDecoder::setupFromVideoData()
{
int ret;
if (decoderCtx_)
avcodec_close(decoderCtx_);
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 8, 0)
ret = av_find_stream_info(inputCtx_);
#else
ret = avformat_find_stream_info(inputCtx_, options_ ? &options_ : NULL);
#endif
if (ret < 0) {
// workaround for this bug:
// http://patches.libav.org/patch/22541/
if (ret == -1)
ret = AVERROR_INVALIDDATA;
char errBuf[64] = {0};
// print nothing for unknown errors
if (av_strerror(ret, errBuf, sizeof errBuf) < 0)
errBuf[0] = '\0';
// always fail here
ERROR("Could not find stream info: %s", errBuf);
return -1;
}
// find the first video stream from the input
for (size_t i = 0; streamIndex_ == -1 && i < inputCtx_->nb_streams; ++i)
if (inputCtx_->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
streamIndex_ = i;
if (streamIndex_ == -1) {
ERROR("Could not find video stream");
return -1;
}
// Get a pointer to the codec context for the video stream
decoderCtx_ = inputCtx_->streams[streamIndex_]->codec;
if (decoderCtx_ == 0) {
ERROR("Decoder context is NULL");
return -1;
}
// find the decoder for the video stream
inputDecoder_ = avcodec_find_decoder(decoderCtx_->codec_id);
if (!inputDecoder_) {
ERROR("Unsupported codec");
return -1;
}
decoderCtx_->thread_count = 1;
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 6, 0)
ret = avcodec_open(decoderCtx_, inputDecoder_);
#else
ret = avcodec_open2(decoderCtx_, inputDecoder_, NULL);
#endif
if (ret) {
ERROR("Could not open codec");
return -1;
}
dstWidth_ = decoderCtx_->width;
dstHeight_ = decoderCtx_->height;
return 0;
}
int VideoDecoder::decode()
{
int ret = 0;
// Guarantee that we free the packet every iteration
VideoPacket video_packet;
AVPacket *inpacket = video_packet.get();
ret = av_read_frame(inputCtx_, inpacket);
if (ret == AVERROR(EAGAIN))
return 0;
else if (ret < 0) {
ERROR("Couldn't read frame: %s\n", strerror(ret));
return -1;
}
avcodec_get_frame_defaults(rawFrame_);
// is this a packet from the video stream?
if (inpacket->stream_index != streamIndex_)
return 0;
int frameFinished = 0;
const int len = avcodec_decode_video2(decoderCtx_, rawFrame_,
&frameFinished, inpacket);
if (len <= 0)
return -2;
return frameFinished;
}
int VideoDecoder::flush()
{
AVPacket inpacket;
av_init_packet(&inpacket);
inpacket.data = NULL;
inpacket.size = 0;
avcodec_get_frame_defaults(rawFrame_);
int frameFinished = 0;
const int len = avcodec_decode_video2(decoderCtx_, rawFrame_,
&frameFinished, &inpacket);
if (len <= 0)
return -2;
return frameFinished;
}
void VideoDecoder::setScaleDest(void *data, int width, int height,
int pix_fmt)
{
AVFrame *output_frame = scaledPicture_.get();
avpicture_fill((AVPicture *) output_frame, (uint8_t *) data,
(PixelFormat) pix_fmt, width, height);
output_frame->format = pix_fmt;
output_frame->width = width;
output_frame->height = height;
}
void VideoDecoder::scale(int flags)
{
AVFrame *output_frame = scaledPicture_.get();
scalerCtx_ = sws_getCachedContext(scalerCtx_,
decoderCtx_->width,
decoderCtx_->height,
decoderCtx_->pix_fmt,
output_frame->width,
output_frame->height,
(PixelFormat) output_frame->format,
SWS_BICUBIC, /* FIXME: option? */
NULL, NULL, NULL);
if (!scalerCtx_) {
ERROR("Unable to create a scaler context");
return;
}
sws_scale(scalerCtx_,
rawFrame_->data, rawFrame_->linesize, 0, decoderCtx_->height,
output_frame->data, output_frame->linesize);
}
}
/*
* Copyright (C) 2011-2012 Savoir-Faire Linux Inc.
* Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
* Copyright (C) 2013 Savoir-Faire Linux Inc.
* Author: Guillaume Roguez <Guillaume.Roguez@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
......@@ -14,7 +14,8 @@
*
* 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.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*