commit from PR#86 for VOIP, modified so as to restore real-time preview and video display

This commit is contained in:
csoler 2015-10-03 22:08:24 -04:00
parent 9158ed64ef
commit b5bfddd1bc
6 changed files with 321 additions and 215 deletions

View File

@ -30,11 +30,52 @@ linux-* {
win32 {
LIBS_DIR = $$PWD/../../../libs
LIBS += -L"$$LIBS_DIR/lib/opencv"
DEPENDPATH += . $$INC_DIR
INCLUDEPATH += . $$INC_DIR
OPENCV_VERSION = 249
LIBS += -lopencv_core$$OPENCV_VERSION -lopencv_highgui$$OPENCV_VERSION -lopencv_imgproc$$OPENCV_VERSION -llibjpeg -llibtiff -llibpng -llibjasper -lIlmImf -lole32 -loleaut32 -luuid -lavicap32 -lavifil32 -lvfw32 -lz
OPENCV_VERSION = "249"
USE_PRECOMPILED_LIBS =
for(lib, LIB_DIR) {
#message(Scanning $$lib)
exists( $$lib/opencv/libopencv_core249.a) {
isEmpty(USE_PRECOMPILED_LIBS) {
message(Get pre-compiled opencv 249 libraries here:)
message($$lib)
LIBS += -L"$$lib/opencv"
LIBS += -lopencv_core$$OPENCV_VERSION -lopencv_highgui$$OPENCV_VERSION -lopencv_imgproc$$OPENCV_VERSION
USE_PRECOMPILED_LIBS = 1
}
}
exists( $$lib/opencv/libopencv_core.a) {
isEmpty(USE_PRECOMPILED_LIBS) {
message(Get pre-compiled opencv libraries here:)
message($$lib)
LIBS += -L"$$lib/opencv"
LIBS += -lopencv_core -lopencv_highgui -lopencv_imgproc
USE_PRECOMPILED_LIBS = 1
}
}
exists( $$lib/libopencv_core.dll.a) {
isEmpty(USE_PRECOMPILED_LIBS) {
message(Get pre-compiled opencv libraries here:)
message($$lib)
LIBS += -L"$$lib/opencv"
LIBS += -lopencv_core -lopencv_highgui -lopencv_imgproc
USE_PRECOMPILED_LIBS = 1
}
}
exists( $$lib/libopencv_videoio.dll.a) {
message(videoio found in opencv libraries.)
message($$lib)
LIBS += -lopencv_videoio
}
}
isEmpty(USE_PRECOMPILED_LIBS) {
message(Use system opencv libraries.)
LIBS += -lopencv_core -lopencv_highgui -lopencv_imgproc
}
LIBS += -lz -lole32 -loleaut32 -luuid -lvfw32 -llibjpeg -llibtiff -llibpng -llibjasper -lIlmImf
LIBS += -lavifil32 -lavicap32 -lavcodec -lavutil -lswresample
}
# ffmpeg (and libavutil: https://github.com/ffms/ffms2/issues/11)

View File

@ -150,8 +150,8 @@ void AudioInputConfig::togglePreview(bool b)
}
else
{
videoInput->setEchoVideoTarget(ui.videoDisplay) ;
videoProcessor->setDisplayTarget(NULL) ;
videoInput->setEchoVideoTarget(ui.videoDisplay) ;
}
}

View File

@ -31,7 +31,9 @@ void QVideoInputDevice::stop()
}
if(_capture_device != NULL)
{
cvReleaseCapture(&_capture_device) ;
// the camera will be deinitialized automatically in VideoCapture destructor
_capture_device->release();
delete _capture_device ;
_capture_device = NULL ;
}
}
@ -43,9 +45,9 @@ void QVideoInputDevice::start()
// Initialise la capture
static const int cam_id = 0 ;
_capture_device = cvCaptureFromCAM(cam_id);
_capture_device = new cv::VideoCapture(cam_id);
if(_capture_device == NULL)
if(!_capture_device->isOpened())
{
std::cerr << "Cannot initialise camera. Something's wrong." << std::endl;
return ;
@ -62,25 +64,24 @@ void QVideoInputDevice::grabFrame()
if(!_timer)
return ;
IplImage *img=cvQueryFrame(_capture_device);
if(img == NULL)
cv::Mat frame;
if(!_capture_device->read(frame))
{
std::cerr << "(EE) Cannot capture image from camera. Something's wrong." << std::endl;
return ;
}
// get the image data
if(img->nChannels != 3)
if(frame.channels() != 3)
{
std::cerr << "(EE) expected 3 channels. Got " << img->nChannels << std::endl;
std::cerr << "(EE) expected 3 channels. Got " << frame.channels() << std::endl;
return ;
}
// convert to RGB and copy to new buffer, because cvQueryFrame tells us to not modify the buffer
cv::Mat img_rgb;
cv::cvtColor(cv::Mat(img), img_rgb, CV_BGR2RGB);
cv::cvtColor(frame, img_rgb, CV_BGR2RGB);
QImage image = QImage(img_rgb.data,img_rgb.cols,img_rgb.rows,QImage::Format_RGB888);
if(_video_processor != NULL)
@ -130,6 +131,6 @@ void QVideoOutputDevice::showFrameOff()
void QVideoOutputDevice::showFrame(const QImage& img)
{
std::cerr << "img.size = " << img.width() << " x " << img.height() << std::endl;
setPixmap(QPixmap::fromImage(img).scaled( QSize(height()*640/480,height()),Qt::IgnoreAspectRatio,Qt::SmoothTransformation)) ;
setPixmap(QPixmap::fromImage(img).scaled( QSize(height()*4/3,height()),Qt::IgnoreAspectRatio,Qt::SmoothTransformation)) ;
}

View File

@ -2,10 +2,12 @@
#include <QLabel>
#include "interface/rsVOIP.h"
#include "opencv2/opencv.hpp"
#include "gui/VideoProcessor.h"
class VideoEncoder ;
class CvCapture ;
// Responsible from displaying the video. The source of the video is
// a VideoDecoder object, which uses a codec.
@ -61,7 +63,7 @@ protected slots:
private:
VideoProcessor *_video_processor ;
QTimer *_timer ;
CvCapture *_capture_device ;
cv::VideoCapture *_capture_device ;
QVideoOutputDevice *_echo_output_device ;

View File

@ -12,10 +12,6 @@
#include <math.h>
#include <time.h>
#if defined(__MINGW32__)
#define memalign _aligned_malloc
#endif //MINGW
extern "C" {
#include <libavcodec/avcodec.h>
@ -28,6 +24,79 @@ extern "C" {
}
//#define DEBUG_MPEG_VIDEO 1
#ifndef AV_INPUT_BUFFER_PADDING_SIZE
#ifndef FF_INPUT_BUFFER_PADDING_SIZE
#define AV_INPUT_BUFFER_PADDING_SIZE 32
#else
#define AV_INPUT_BUFFER_PADDING_SIZE FF_INPUT_BUFFER_PADDING_SIZE
#endif
#endif
#if (LIBAVUTIL_VERSION_MAJOR < 52) || ((LIBAVUTIL_VERSION_MAJOR == 52) && (LIBAVUTIL_VERSION_MINOR < 63))
//since https://github.com/FFmpeg/FFmpeg/commit/3532dd52c51f3d4b95f31d1b195e64a04a8aea5d
static inline AVRational av_make_q(int num, int den)
{
AVRational r = { num, den };
return r;
}
#endif
#if (LIBAVUTIL_VERSION_MAJOR < 55) || ((LIBAVUTIL_VERSION_MAJOR == 55) && (LIBAVUTIL_VERSION_MINOR < 52))
//since https://github.com/FFmpeg/FFmpeg/commit/fd056029f45a9f6d213d9fce8165632042511d4f
void avcodec_free_context(AVCodecContext **pavctx)
{
AVCodecContext *avctx = *pavctx;
if (!avctx)
return;
avcodec_close(avctx);
av_freep(&avctx->extradata);
av_freep(&avctx->subtitle_header);
av_freep(pavctx);
}
#endif
#if (LIBAVUTIL_VERSION_MAJOR < 57) || ((LIBAVUTIL_VERSION_MAJOR == 57) && (LIBAVUTIL_VERSION_MINOR < 52))
//Since https://github.com/FFmpeg/FFmpeg/commit/7ecc2d403ce5c7b6ea3b1f368dccefd105209c7e
static void get_frame_defaults(AVFrame *frame)
{
if (frame->extended_data != frame->data)
av_freep(&frame->extended_data);
return;
frame->extended_data = NULL;
get_frame_defaults(frame);
return;
}
AVFrame *av_frame_alloc(void)
{
AVFrame *frame = (AVFrame *)av_mallocz(sizeof(*frame));
if (!frame)
return NULL;
get_frame_defaults(frame);
return frame;
}
void av_frame_free(AVFrame **frame)
{
if (!frame || !*frame)
return;
av_freep(frame);
}
#endif
VideoProcessor::VideoProcessor()
:_encoded_frame_size(640,480) , vpMtx("VideoProcessor")
{
@ -164,7 +233,6 @@ void VideoProcessor::receiveEncodedData(const RsVOIPDataChunk& chunk)
default:
codec = NULL ;
}
QImage img ;
if(codec == NULL)
{
@ -190,6 +258,8 @@ void VideoProcessor::receiveEncodedData(const RsVOIPDataChunk& chunk)
#endif
}
}
QImage img ;
if(!codec->decodeData(chunk,img))
{
std::cerr << "No image decoded. Probably in the middle of something..." << std::endl;
@ -266,7 +336,9 @@ bool JPEGVideo::encodeData(const QImage& image,uint32_t /* size_hint */,RsVOIPDa
QImage encoded_frame ;
bool differential_frame ;
if(_encoded_ref_frame_count++ < _encoded_ref_frame_max_distance && image.size() == _encoded_reference_frame.size())
if (_encoded_ref_frame_count++ < _encoded_ref_frame_max_distance
&& image.size() == _encoded_reference_frame.size()
&& image.byteCount() == _encoded_reference_frame.byteCount())
{
// compute difference with reference frame.
encoded_frame = image ;
@ -285,7 +357,7 @@ bool JPEGVideo::encodeData(const QImage& image,uint32_t /* size_hint */,RsVOIPDa
else
{
_encoded_ref_frame_count = 0 ;
_encoded_reference_frame = image ;
_encoded_reference_frame = image.copy() ;
encoded_frame = image ;
differential_frame = false ;
@ -317,6 +389,7 @@ bool JPEGVideo::encodeData(const QImage& image,uint32_t /* size_hint */,RsVOIPDa
FFmpegVideo::FFmpegVideo()
{
avcodec_register_all();
// Encoding
encoding_codec = NULL ;
@ -331,13 +404,15 @@ FFmpegVideo::FFmpegVideo()
AVCodecID codec_id = AV_CODEC_ID_MPEG4;
#endif
/* find the mpeg1 video encoder */
/* find the video encoder */
encoding_codec = avcodec_find_encoder(codec_id);
if (!encoding_codec) throw("AV codec not found for codec id ") ;
if (!encoding_codec) std::cerr << "AV codec not found for codec id " << std::endl;
if (!encoding_codec) throw std::runtime_error("AV codec not found for codec id ") ;
encoding_context = avcodec_alloc_context3(encoding_codec);
if (!encoding_context) std::cerr << "AV: Could not allocate video codec encoding context" << std::endl;
if (!encoding_context) throw std::runtime_error("AV: Could not allocate video codec encoding context");
/* put sample parameters */
@ -356,8 +431,9 @@ FFmpegVideo::FFmpegVideo()
encoding_context->rc_max_rate = 0;
encoding_context->rc_buffer_size = 0;
#endif
encoding_context->flags |= CODEC_FLAG_PSNR;
if (encoding_codec->capabilities & CODEC_CAP_TRUNCATED)
encoding_context->flags |= CODEC_FLAG_TRUNCATED;
encoding_context->flags |= CODEC_FLAG_PSNR;//Peak signal-to-noise ratio
encoding_context->flags |= CODEC_CAP_PARAM_CHANGE;
encoding_context->i_quant_factor = 0.769f;
encoding_context->b_quant_factor = 1.4f;
@ -378,7 +454,7 @@ FFmpegVideo::FFmpegVideo()
encoding_context->width = 640;//176;
encoding_context->height = 480;//144;
/* frames per second */
encoding_context->time_base = (AVRational){1,25};
encoding_context->time_base = av_make_q(1, 25);
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
@ -399,10 +475,18 @@ FFmpegVideo::FFmpegVideo()
/* open it */
if (avcodec_open2(encoding_context, encoding_codec, NULL) < 0)
{
std::cerr << "AV: Could not open codec context. Something's wrong." << std::endl;
throw std::runtime_error( "AV: Could not open codec context. Something's wrong.");
}
#if (LIBAVCODEC_VERSION_MAJOR < 57) | (LIBAVCODEC_VERSION_MAJOR == 57 && LIBAVCODEC_VERSION_MINOR <3 )
encoding_frame_buffer = avcodec_alloc_frame() ;//(AVFrame*)malloc(sizeof(AVFrame)) ;
#else
encoding_frame_buffer = av_frame_alloc() ;
#endif
if(!encoding_frame_buffer) std::cerr << "AV: could not allocate frame buffer." << std::endl;
if(!encoding_frame_buffer)
throw std::runtime_error("AV: could not allocate frame buffer.") ;
@ -416,20 +500,22 @@ FFmpegVideo::FFmpegVideo()
int ret = av_image_alloc(encoding_frame_buffer->data, encoding_frame_buffer->linesize,
encoding_context->width, encoding_context->height, encoding_context->pix_fmt, 32);
if (ret < 0) std::cerr << "AV: Could not allocate raw picture buffer" << std::endl;
if (ret < 0)
throw std::runtime_error("AV: Could not allocate raw picture buffer");
encoding_frame_count = 0 ;
// Decoding
decoding_codec = avcodec_find_decoder(codec_id);
if (!decoding_codec) std::cerr << "AV codec not found for codec id " << std::endl;
if (!decoding_codec)
throw("AV codec not found for codec id ") ;
decoding_context = avcodec_alloc_context3(decoding_codec);
if(!decoding_context) std::cerr << "AV: Could not allocate video codec decoding context" << std::endl;
if(!decoding_context)
throw std::runtime_error("AV: Could not allocate video codec decoding context");
@ -443,11 +529,18 @@ FFmpegVideo::FFmpegVideo()
if(decoding_codec->capabilities & CODEC_CAP_TRUNCATED)
decoding_context->flags |= CODEC_FLAG_TRUNCATED; // we do not send complete frames
//we can receive truncated frames
decoding_context->flags2 |= CODEC_FLAG2_CHUNKS;
if(avcodec_open2(decoding_context,decoding_codec,NULL) < 0)
AVDictionary* dictionary = NULL;
if(avcodec_open2(decoding_context, decoding_codec, &dictionary) < 0)
{
std::cerr << "AV codec open action failed! " << std::endl;
throw("AV codec open action failed! ") ;
}
decoding_frame_buffer = avcodec_alloc_frame() ;//(AVFrame*)malloc(sizeof(AVFrame)) ;
//decoding_frame_buffer = avcodec_alloc_frame() ;//(AVFrame*)malloc(sizeof(AVFrame)) ;
decoding_frame_buffer = av_frame_alloc() ;
av_init_packet(&decoding_buffer);
decoding_buffer.data = NULL ;
@ -467,14 +560,10 @@ FFmpegVideo::FFmpegVideo()
FFmpegVideo::~FFmpegVideo()
{
avcodec_close(encoding_context);
avcodec_close(decoding_context);
av_free(encoding_context);
av_free(decoding_context);
av_freep(&encoding_frame_buffer->data[0]);
av_freep(&decoding_frame_buffer->data[0]);
free(encoding_frame_buffer);
free(decoding_frame_buffer);
avcodec_free_context(&encoding_context);
avcodec_free_context(&decoding_context);
av_frame_free(&encoding_frame_buffer);
av_frame_free(&decoding_frame_buffer);
}
#define MAX_FFMPEG_ENCODING_BITRATE 81920
@ -543,8 +632,6 @@ bool FFmpegVideo::encodeData(const QImage& image, uint32_t target_encoding_bitra
int got_output = 0;
AVFrame *frame = encoding_frame_buffer ;
AVPacket pkt ;
av_init_packet(&pkt);
#if LIBAVCODEC_VERSION_MAJOR < 54
@ -553,7 +640,7 @@ bool FFmpegVideo::encodeData(const QImage& image, uint32_t target_encoding_bitra
// do
// {
int ret = avcodec_encode_video(encoding_context, pkt.data, pkt.size, frame) ;
int ret = avcodec_encode_video(encoding_context, pkt.data, pkt.size, encoding_frame_buffer) ;
if (ret > 0) {
got_output = ret;
}
@ -563,7 +650,7 @@ bool FFmpegVideo::encodeData(const QImage& image, uint32_t target_encoding_bitra
// do
// {
int ret = avcodec_encode_video2(encoding_context, &pkt, frame, &got_output) ;
int ret = avcodec_encode_video2(encoding_context, &pkt, encoding_frame_buffer, &got_output) ;
#endif
if (ret < 0)
@ -615,54 +702,40 @@ bool FFmpegVideo::decodeData(const RsVOIPDataChunk& chunk,QImage& image)
{
#ifdef DEBUG_MPEG_VIDEO
std::cerr << "Decoding data of size " << chunk.size << std::endl;
#endif
unsigned char *buff ;
if(decoding_buffer.data != NULL)
{
#ifdef DEBUG_MPEG_VIDEO
std::cerr << "Completing buffer with size " << chunk.size - HEADER_SIZE + decoding_buffer.size << ": copying existing "
<< decoding_buffer.size << " bytes. Adding new " << chunk.size - HEADER_SIZE<< " bytes " << std::endl;
#endif
uint32_t s = chunk.size - HEADER_SIZE + decoding_buffer.size ;
unsigned char *tmp = (unsigned char*)memalign(16,s + FF_INPUT_BUFFER_PADDING_SIZE) ;
memset(tmp,0,s+FF_INPUT_BUFFER_PADDING_SIZE) ;
memcpy(tmp,decoding_buffer.data,decoding_buffer.size) ;
free(decoding_buffer.data) ;
buff = &tmp[decoding_buffer.size] ;
decoding_buffer.size = s ;
decoding_buffer.data = tmp ;
}
else
{
#ifdef DEBUG_MPEG_VIDEO
std::cerr << "Allocating new buffer of size " << chunk.size - HEADER_SIZE << std::endl;
#endif
decoding_buffer.data = (unsigned char *)memalign(16,chunk.size - HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE) ;
decoding_buffer.size = chunk.size - HEADER_SIZE ;
memset(decoding_buffer.data,0,decoding_buffer.size + FF_INPUT_BUFFER_PADDING_SIZE) ;
buff = decoding_buffer.data ;
uint32_t s = chunk.size - HEADER_SIZE ;
#if defined(__MINGW32__)
unsigned char *tmp = (unsigned char*)_aligned_malloc(s + AV_INPUT_BUFFER_PADDING_SIZE, 16) ;
#else
unsigned char *tmp = (unsigned char*)memalign(16, s + AV_INPUT_BUFFER_PADDING_SIZE) ;
#endif //MINGW
if (tmp == NULL) {
std::cerr << "FFmpegVideo::decodeData() Unable to allocate new buffer of size " << s << std::endl;
return false;
}
/* copy chunk data without header to new buffer */
memcpy(tmp, &((unsigned char*)chunk.data)[HEADER_SIZE], s);
memcpy(buff,&((unsigned char*)chunk.data)[HEADER_SIZE],chunk.size - HEADER_SIZE) ;
/* set end of buffer to 0 (this ensures that no overreading happens for damaged mpeg streams) */
memset(&tmp[s], 0, AV_INPUT_BUFFER_PADDING_SIZE) ;
int got_frame = 0 ;
decoding_buffer.size = s ;
decoding_buffer.data = tmp;
int got_frame = 1 ;
while (decoding_buffer.size > 0 || (!decoding_buffer.data && got_frame)) {
int len = avcodec_decode_video2(decoding_context,decoding_frame_buffer,&got_frame,&decoding_buffer) ;
if (len < 0)
{
std::cerr << "Error decodign frame!" << std::endl;
std::cerr << "Error decoding frame! Return=" << len << std::endl;
return false ;
}
#ifdef DEBUG_MPEG_VIDEO
std::cerr << "Used " << len << " bytes out of " << decoding_buffer.size << ". got_frame = " << got_frame << std::endl;
#endif
decoding_buffer.data += len;
decoding_buffer.size -= len;
if(got_frame)
{
@ -686,22 +759,11 @@ bool FFmpegVideo::decodeData(const RsVOIPDataChunk& chunk,QImage& image)
image.setPixel(QPoint(x,y),QRgb( 0xff000000 + (R << 16) + (G << 8) + B)) ;
}
}
if(len == decoding_buffer.size)
{
free(decoding_buffer.data) ;
}
/* flush the decoder */
decoding_buffer.data = NULL;
decoding_buffer.size = 0;
}
else if(len != 0)
{
#ifdef DEBUG_MPEG_VIDEO
std::cerr << "Moving remaining data (" << decoding_buffer.size - len << " bytes) back to 0" << std::endl;
#endif
memmove(decoding_buffer.data,decoding_buffer.data+len,decoding_buffer.size - len) ;
decoding_buffer.size -= len ;
}
//avcodec_decode_video2(decoding_context,decoding_frame_buffer,&got_frame,&decoding_buffer) ;
return true ;
}