1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Merge pull request #3392

Diadlo (12):
      refactor(avform): Extracted functions with best mode search and combo box filling
      refactor(avform): Replace `bestModeInds` on videoMode index and video height as quality name
      refactor(avform): Separeted search of preferred index in function
      fix(avform): Took default resolution from middle of list
      feat(videomode): Added possible video shift
      feat(cameradevice, avform): Added ability of screen selection
      feat(avform, screenshotgrabber): Added custom screen region selection
      style(corevideosource): Small style fixes
      fix(videoframe): Added correct image copy
      fix(avform): Added restoring selected region
      feat(camerasource): Change default video mode to preferred
      fix(directshow): Fixed problem with crosses initialization
This commit is contained in:
sudden6 2016-07-12 19:56:41 +02:00
commit e97a870c0f
No known key found for this signature in database
GPG Key ID: 279509B499E032B9
16 changed files with 403 additions and 202 deletions

View File

@ -295,7 +295,7 @@ void CoreAV::sendCallVideo(uint32_t callId, shared_ptr<VideoFrame> vframe)
if (frame->fmt == VPX_IMG_FMT_NONE) if (frame->fmt == VPX_IMG_FMT_NONE)
{ {
qWarning() << "Invalid frame"; qWarning() << "Invalid frame";
delete frame; vpx_img_free(frame);
return; return;
} }
@ -321,7 +321,7 @@ void CoreAV::sendCallVideo(uint32_t callId, shared_ptr<VideoFrame> vframe)
if (err == TOXAV_ERR_SEND_FRAME_SYNC) if (err == TOXAV_ERR_SEND_FRAME_SYNC)
qDebug() << "toxav_video_send_frame error: Lock busy, dropping frame"; qDebug() << "toxav_video_send_frame error: Lock busy, dropping frame";
delete frame; vpx_img_free(frame);
} }
void CoreAV::micMuteToggle(uint32_t callId) void CoreAV::micMuteToggle(uint32_t callId)

View File

@ -102,6 +102,7 @@ ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
{ {
videoSource = new CoreVideoSource; videoSource = new CoreVideoSource;
CameraSource& source = CameraSource::getInstance(); CameraSource& source = CameraSource::getInstance();
if (!source.isOpen()) if (!source.isOpen())
source.open(); source.open();
source.subscribe(); source.subscribe();

View File

@ -251,7 +251,9 @@ void Settings::loadGlobal()
s.beginGroup("Video"); s.beginGroup("Video");
videoDev = s.value("videoDev", "").toString(); videoDev = s.value("videoDev", "").toString();
camVideoRes = s.value("camVideoRes",QSize()).toSize(); camVideoRes = s.value("camVideoRes", QRect()).toRect();
screenRegion = s.value("screenRegion", QRect()).toRect();
screenGrabbed = s.value("screenGrabbed", false).toBool();
camVideoFPS = s.value("camVideoFPS", 0).toUInt(); camVideoFPS = s.value("camVideoFPS", 0).toUInt();
s.endGroup(); s.endGroup();
@ -489,8 +491,10 @@ void Settings::saveGlobal()
s.beginGroup("Video"); s.beginGroup("Video");
s.setValue("videoDev", videoDev); s.setValue("videoDev", videoDev);
s.setValue("camVideoRes",camVideoRes); s.setValue("camVideoRes", camVideoRes);
s.setValue("camVideoFPS",camVideoFPS); s.setValue("camVideoFPS", camVideoFPS);
s.setValue("screenRegion", screenRegion);
s.setValue("screenGrabbed", screenGrabbed);
s.endGroup(); s.endGroup();
} }
@ -1465,13 +1469,35 @@ void Settings::setOutVolume(int volume)
outVolume = volume; outVolume = volume;
} }
QSize Settings::getCamVideoRes() const QRect Settings::getScreenRegion() const
{
return screenRegion;
}
void Settings::setScreenRegion(const QRect &value)
{
QMutexLocker locker{&bigLock};
screenRegion = value;
}
bool Settings::getScreenGrabbed() const
{
return screenGrabbed;
}
void Settings::setScreenGrabbed(bool value)
{
QMutexLocker locker{&bigLock};
screenGrabbed = value;
}
QRect Settings::getCamVideoRes() const
{ {
QMutexLocker locker{&bigLock}; QMutexLocker locker{&bigLock};
return camVideoRes; return camVideoRes;
} }
void Settings::setCamVideoRes(QSize newValue) void Settings::setCamVideoRes(QRect newValue)
{ {
QMutexLocker locker{&bigLock}; QMutexLocker locker{&bigLock};
camVideoRes = newValue; camVideoRes = newValue;

View File

@ -197,8 +197,14 @@ public:
QString getVideoDev() const; QString getVideoDev() const;
void setVideoDev(const QString& deviceSpecifier); void setVideoDev(const QString& deviceSpecifier);
QSize getCamVideoRes() const; QRect getScreenRegion() const;
void setCamVideoRes(QSize newValue); void setScreenRegion(const QRect &value);
bool getScreenGrabbed() const;
void setScreenGrabbed(bool value);
QRect getCamVideoRes() const;
void setCamVideoRes(QRect newValue);
unsigned short getCamVideoFPS() const; unsigned short getCamVideoFPS() const;
void setCamVideoFPS(unsigned short newValue); void setCamVideoFPS(unsigned short newValue);
@ -447,7 +453,9 @@ private:
// Video // Video
QString videoDev; QString videoDev;
QSize camVideoRes; QRect camVideoRes;
QRect screenRegion;
bool screenGrabbed;
unsigned short camVideoFPS; unsigned short camVideoFPS;
struct friendProp struct friendProp

View File

@ -187,6 +187,7 @@ QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
IPin *pin; IPin *pin;
if (devFilter->EnumPins(&pins) != S_OK) if (devFilter->EnumPins(&pins) != S_OK)
return modes; return modes;
while (pins->Next(1, &pin, nullptr) == S_OK) while (pins->Next(1, &pin, nullptr) == S_OK)
{ {
IKsPropertySet *p = nullptr; IKsPropertySet *p = nullptr;
@ -214,12 +215,14 @@ QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
goto next; goto next;
if (config->GetNumberOfCapabilities(&n, &size) != S_OK) if (config->GetNumberOfCapabilities(&n, &size) != S_OK)
goto pinend; goto pinend;
assert(size == sizeof(VIDEO_STREAM_CONFIG_CAPS)); assert(size == sizeof(VIDEO_STREAM_CONFIG_CAPS));
vcaps = new VIDEO_STREAM_CONFIG_CAPS; vcaps = new VIDEO_STREAM_CONFIG_CAPS;
for (int i=0; i<n; ++i) for (int i = 0; i < n; ++i)
{ {
AM_MEDIA_TYPE* type = nullptr; AM_MEDIA_TYPE* type = nullptr;
VideoMode mode;
if (config->GetStreamCaps(i, &type, (BYTE*)vcaps) != S_OK) if (config->GetStreamCaps(i, &type, (BYTE*)vcaps) != S_OK)
goto nextformat; goto nextformat;
@ -227,7 +230,6 @@ QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
&& !IsEqualGUID(type->formattype, FORMAT_VideoInfo2)) && !IsEqualGUID(type->formattype, FORMAT_VideoInfo2))
goto nextformat; goto nextformat;
VideoMode mode;
mode.width = vcaps->MaxOutputSize.cx; mode.width = vcaps->MaxOutputSize.cx;
mode.height = vcaps->MaxOutputSize.cy; mode.height = vcaps->MaxOutputSize.cy;
mode.FPS = 1e7 / vcaps->MinFrameInterval; mode.FPS = 1e7 / vcaps->MinFrameInterval;

View File

@ -73,7 +73,7 @@ CameraDevice* CameraDevice::open(QString devName, AVDictionary** options)
format = iformat; format = iformat;
} }
if (avformat_open_input(&fctx, devName.toStdString().c_str(), format, options)<0) if (avformat_open_input(&fctx, devName.toStdString().c_str(), format, options) < 0)
goto out; goto out;
// Fix avformat_find_stream_info hanging on garbage input // Fix avformat_find_stream_info hanging on garbage input
@ -103,12 +103,6 @@ out:
return dev; return dev;
} }
CameraDevice* CameraDevice::open(QString devName)
{
VideoMode mode{0,0,0,0};
return open(devName, mode);
}
CameraDevice* CameraDevice::open(QString devName, VideoMode mode) CameraDevice* CameraDevice::open(QString devName, VideoMode mode)
{ {
if (!getDefaultInputFormat()) if (!getDefaultInputFormat())
@ -149,26 +143,14 @@ CameraDevice* CameraDevice::open(QString devName, VideoMode mode)
screen.setHeight((screen.height() * pixRatio)-2); screen.setHeight((screen.height() * pixRatio)-2);
} }
av_dict_set(&options, "video_size", QString("%1x%2").arg(screen.width()).arg(screen.height()).toStdString().c_str(), 0); av_dict_set(&options, "video_size", QString("%1x%2").arg(screen.width()).arg(screen.height()).toStdString().c_str(), 0);
devName += QString("+%1,%2").arg(QString().setNum(mode.x), QString().setNum(mode.y));
int FPS = 5;
if (mode.FPS) if (mode.FPS)
av_dict_set(&options, "framerate", QString().setNum(mode.FPS).toStdString().c_str(), 0); FPS = mode.FPS;
else
av_dict_set(&options, "framerate", QString().setNum(5).toStdString().c_str(), 0); av_dict_set(&options, "framerate", QString().setNum(FPS).toStdString().c_str(), 0);
} }
#endif
#ifdef Q_OS_WIN
else if (devName.startsWith("gdigrab#"))
{
av_dict_set(&options, "framerate", QString().setNum(5).toStdString().c_str(), 0);
}
#endif
#ifdef Q_OS_WIN
else if (iformat->name == QString("dshow") && mode)
{
av_dict_set(&options, "video_size", QString("%1x%2").arg(mode.width).arg(mode.height).toStdString().c_str(), 0);
av_dict_set(&options, "framerate", QString().setNum(mode.FPS).toStdString().c_str(), 0);
}
#endif
#ifdef Q_OS_LINUX
else if (iformat->name == QString("video4linux2,v4l2") && mode) else if (iformat->name == QString("video4linux2,v4l2") && mode)
{ {
av_dict_set(&options, "video_size", QString("%1x%2").arg(mode.width).arg(mode.height).toStdString().c_str(), 0); av_dict_set(&options, "video_size", QString("%1x%2").arg(mode.width).arg(mode.height).toStdString().c_str(), 0);
@ -180,6 +162,20 @@ CameraDevice* CameraDevice::open(QString devName, VideoMode mode)
} }
} }
#endif #endif
#ifdef Q_OS_WIN
else if (devName.startsWith("gdigrab#"))
{
av_dict_set(&options, "framerate", QString().setNum(5).toStdString().c_str(), 0);
av_dict_set(&options, "offset_x", QString().setNum(mode.x).toStdString().c_str(), 0);
av_dict_set(&options, "offset_y", QString().setNum(mode.y).toStdString().c_str(), 0);
av_dict_set(&options, "video_size", QString("%1x%2").arg(mode.width).arg(mode.height).toStdString().c_str(), 0);
}
else if (iformat->name == QString("dshow") && mode)
{
av_dict_set(&options, "video_size", QString("%1x%2").arg(mode.width).arg(mode.height).toStdString().c_str(), 0);
av_dict_set(&options, "framerate", QString().setNum(mode.FPS).toStdString().c_str(), 0);
}
#endif
#ifdef Q_OS_OSX #ifdef Q_OS_OSX
else if (iformat->name == QString("avfoundation")) else if (iformat->name == QString("avfoundation"))
{ {
@ -215,19 +211,15 @@ void CameraDevice::open()
bool CameraDevice::close() bool CameraDevice::close()
{ {
if (--refcount <= 0) if (--refcount > 0)
{
openDeviceLock.lock();
openDevices.remove(devName);
openDeviceLock.unlock();
avformat_close_input(&context);
delete this;
return true;
}
else
{
return false; return false;
}
openDeviceLock.lock();
openDevices.remove(devName);
openDeviceLock.unlock();
avformat_close_input(&context);
delete this;
return true;
} }
QVector<QPair<QString, QString>> CameraDevice::getRawDeviceListGeneric() QVector<QPair<QString, QString>> CameraDevice::getRawDeviceListGeneric()
@ -351,9 +343,33 @@ QString CameraDevice::getDefaultDeviceName()
return devlist[0].first; return devlist[0].first;
} }
bool CameraDevice::isScreen(const QString &devName)
{
return devName.startsWith("x11grab") || devName.startsWith("gdigrab");
}
QVector<VideoMode> CameraDevice::getScreenModes()
{
QList<QScreen*> screens = QApplication::screens();
QVector<VideoMode> result;
std::for_each(screens.begin(), screens.end(), [&result](QScreen *s)
{
QRect rect = s->geometry();
QPoint p = rect.topLeft();
VideoMode mode(rect.width(), rect.height(), p.x(), p.y());
result.push_back(mode);
});
return result;
}
QVector<VideoMode> CameraDevice::getVideoModes(QString devName) QVector<VideoMode> CameraDevice::getVideoModes(QString devName)
{ {
if (!iformat); if (!iformat);
else if (isScreen(devName))
return getScreenModes();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
else if (iformat->name == QString("dshow")) else if (iformat->name == QString("dshow"))
return DirectShow::getDeviceModes(devName); return DirectShow::getDeviceModes(devName);
@ -417,6 +433,7 @@ bool CameraDevice::getDefaultInputFormat()
if ((iformat = av_find_input_format("dshow"))) if ((iformat = av_find_input_format("dshow")))
return true; return true;
if ((iformat = av_find_input_format("vfwcap"))) if ((iformat = av_find_input_format("vfwcap")))
return true;
#endif #endif
#ifdef Q_OS_OSX #ifdef Q_OS_OSX

View File

@ -41,15 +41,12 @@ struct AVDictionary;
class CameraDevice class CameraDevice
{ {
public: public:
/// Opens a device, creating a new one if needed
/// Returns a nullptr if the device couldn't be opened
static CameraDevice* open(QString devName);
/// Opens a device, creating a new one if needed /// Opens a device, creating a new one if needed
/// If the device is alreay open in another mode, the mode /// If the device is alreay open in another mode, the mode
/// will be ignored and the existing device is used /// will be ignored and the existing device is used
/// If the mode does not exist, a new device can't be opened /// If the mode does not exist, a new device can't be opened
/// Returns a nullptr if the device couldn't be opened /// Returns a nullptr if the device couldn't be opened
static CameraDevice* open(QString devName, VideoMode mode); static CameraDevice* open(QString devName, VideoMode mode = VideoMode());
void open(); ///< Opens the device again. Never fails void open(); ///< Opens the device again. Never fails
bool close(); ///< Closes the device. Never fails. If returns true, "this" becomes invalid bool close(); ///< Closes the device. Never fails. If returns true, "this" becomes invalid
@ -69,11 +66,15 @@ public:
/// or the system default. /// or the system default.
static QString getDefaultDeviceName(); static QString getDefaultDeviceName();
/// Checks if a device name specifies a display
static bool isScreen(const QString &devName);
private: private:
CameraDevice(const QString &devName, AVFormatContext *context); CameraDevice(const QString &devName, AVFormatContext *context);
static CameraDevice* open(QString devName, AVDictionary** options); static CameraDevice* open(QString devName, AVDictionary** options);
static bool getDefaultInputFormat(); ///< Sets CameraDevice::iformat, returns success/failure static bool getDefaultInputFormat(); ///< Sets CameraDevice::iformat, returns success/failure
static QVector<QPair<QString, QString> > getRawDeviceListGeneric(); ///< Uses avdevice_list_devices static QVector<QPair<QString, QString> > getRawDeviceListGeneric(); ///< Uses avdevice_list_devices
static QVector<VideoMode> getScreenModes(); ///< Returns avaliable screen modes with offset
public: public:
const QString devName; ///< Short name of the device const QString devName; ///< Short name of the device

View File

@ -28,6 +28,7 @@ extern "C" {
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
#include <memory> #include <memory>
#include <functional> #include <functional>
#include "src/persistence/settings.h"
#include "camerasource.h" #include "camerasource.h"
#include "cameradevice.h" #include "cameradevice.h"
#include "videoframe.h" #include "videoframe.h"
@ -35,7 +36,7 @@ extern "C" {
CameraSource* CameraSource::instance{nullptr}; CameraSource* CameraSource::instance{nullptr};
CameraSource::CameraSource() CameraSource::CameraSource()
: deviceName{"none"}, device{nullptr}, mode(VideoMode{0,0,0,0}), : deviceName{"none"}, device{nullptr}, mode(VideoMode()),
cctx{nullptr}, cctxOrig{nullptr}, videoStreamIndex{-1}, cctx{nullptr}, cctxOrig{nullptr}, videoStreamIndex{-1},
_isOpen{false}, streamBlocker{false}, subscriptions{0} _isOpen{false}, streamBlocker{false}, subscriptions{0}
{ {
@ -67,7 +68,15 @@ void CameraSource::open()
void CameraSource::open(const QString& deviceName) void CameraSource::open(const QString& deviceName)
{ {
open(deviceName, VideoMode{0,0,0,0}); bool isScreen = CameraDevice::isScreen(deviceName);
VideoMode mode = VideoMode(Settings::getInstance().getScreenRegion());
if (!isScreen)
{
mode = VideoMode(Settings::getInstance().getCamVideoRes());
mode.FPS = Settings::getInstance().getCamVideoFPS();
}
open(deviceName, mode);
} }
void CameraSource::open(const QString& DeviceName, VideoMode Mode) void CameraSource::open(const QString& DeviceName, VideoMode Mode)
@ -206,7 +215,7 @@ void CameraSource::unsubscribe()
bool CameraSource::openDevice() bool CameraSource::openDevice()
{ {
qDebug() << "Opening device "<<deviceName; qDebug() << "Opening device " << deviceName;
if (device) if (device)
{ {
@ -216,10 +225,8 @@ bool CameraSource::openDevice()
// We need to create a new CameraDevice // We need to create a new CameraDevice
AVCodec* codec; AVCodec* codec;
if (mode) device = CameraDevice::open(deviceName, mode);
device = CameraDevice::open(deviceName, mode);
else
device = CameraDevice::open(deviceName);
if (!device) if (!device)
{ {
qWarning() << "Failed to open device!"; qWarning() << "Failed to open device!";
@ -240,25 +247,36 @@ bool CameraSource::openDevice()
break; break;
} }
} }
if (videoStreamIndex == -1) if (videoStreamIndex == -1)
{
qWarning() << "Video stream not found";
return false; return false;
}
// Get a pointer to the codec context for the video stream // Get a pointer to the codec context for the video stream
cctxOrig = device->context->streams[videoStreamIndex]->codec; cctxOrig = device->context->streams[videoStreamIndex]->codec;
codec = avcodec_find_decoder(cctxOrig->codec_id); codec = avcodec_find_decoder(cctxOrig->codec_id);
if(!codec) if(!codec)
{
qWarning() << "Codec not found";
return false; return false;
}
// Copy context, since we apparently aren't allowed to use the original // Copy context, since we apparently aren't allowed to use the original
cctx = avcodec_alloc_context3(codec); cctx = avcodec_alloc_context3(codec);
if(avcodec_copy_context(cctx, cctxOrig) != 0) if(avcodec_copy_context(cctx, cctxOrig) != 0)
{
qWarning() << "Can't copy context";
return false; return false;
}
cctx->refcounted_frames = 1; cctx->refcounted_frames = 1;
// Open codec // Open codec
if(avcodec_open2(cctx, codec, nullptr)<0) if(avcodec_open2(cctx, codec, nullptr)<0)
{ {
qWarning() << "Can't open codec";
avcodec_free_context(&cctx); avcodec_free_context(&cctx);
return false; return false;
} }
@ -288,6 +306,7 @@ void CameraSource::closeDevice()
std::shared_ptr<VideoFrame> vframe = freelist[i].lock(); std::shared_ptr<VideoFrame> vframe = freelist[i].lock();
if (!vframe) if (!vframe)
continue; continue;
vframe->releaseFrame(); vframe->releaseFrame();
} }
freelist.clear(); freelist.clear();
@ -311,14 +330,15 @@ void CameraSource::stream()
AVFrame* frame = av_frame_alloc(); AVFrame* frame = av_frame_alloc();
if (!frame) if (!frame)
return; return;
frame->opaque = nullptr; frame->opaque = nullptr;
AVPacket packet; AVPacket packet;
if (av_read_frame(device->context, &packet)<0) if (av_read_frame(device->context, &packet) < 0)
return; return;
// Only keep packets from the right stream; // Only keep packets from the right stream;
if (packet.stream_index==videoStreamIndex) if (packet.stream_index == videoStreamIndex)
{ {
// Decode video frame // Decode video frame
int frameFinished; int frameFinished;
@ -340,7 +360,8 @@ void CameraSource::stream()
av_packet_unref(&packet); av_packet_unref(&packet);
}; };
forever { forever
{
biglock.lock(); biglock.lock();
// When a thread makes device null, it releases it, so we acquire here // When a thread makes device null, it releases it, so we acquire here
@ -357,6 +378,7 @@ void CameraSource::stream()
biglock.unlock(); biglock.unlock();
while (streamBlocker) while (streamBlocker)
QThread::yieldCurrentThread(); QThread::yieldCurrentThread();
QThread::yieldCurrentThread(); QThread::yieldCurrentThread();
} }
} }

View File

@ -17,11 +17,11 @@
along with qTox. If not, see <http://www.gnu.org/licenses/>. along with qTox. If not, see <http://www.gnu.org/licenses/>.
*/ */
extern "C" { extern "C" {
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h> #include <libavutil/imgutils.h>
} }
#include "corevideosource.h" #include "corevideosource.h"
#include "videoframe.h" #include "videoframe.h"
@ -39,15 +39,13 @@ void CoreVideoSource::pushFrame(const vpx_image_t* vpxframe)
QMutexLocker locker(&biglock); QMutexLocker locker(&biglock);
std::shared_ptr<VideoFrame> vframe; std::shared_ptr<VideoFrame> vframe;
AVFrame* avframe; int width = vpxframe->d_w;
uint8_t* buf; int height = vpxframe->d_h;
int width = vpxframe->d_w, height = vpxframe->d_h;
int dstStride, srcStride, minStride;
if (subscribers <= 0) if (subscribers <= 0)
return; return;
avframe = av_frame_alloc(); AVFrame* avframe = av_frame_alloc();
if (!avframe) if (!avframe)
return; return;
avframe->width = width; avframe->width = width;
@ -55,29 +53,35 @@ void CoreVideoSource::pushFrame(const vpx_image_t* vpxframe)
avframe->format = AV_PIX_FMT_YUV420P; avframe->format = AV_PIX_FMT_YUV420P;
int imgBufferSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width, height, 1); int imgBufferSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width, height, 1);
buf = (uint8_t*)av_malloc(imgBufferSize); uint8_t* buf = (uint8_t*)av_malloc(imgBufferSize);
if (!buf) if (!buf)
{ {
av_frame_free(&avframe); av_frame_free(&avframe);
return; return;
} }
avframe->opaque = buf;
uint8_t** data = avframe->data; uint8_t** data = avframe->data;
int* linesize = avframe->linesize; int* linesize = avframe->linesize;
av_image_fill_arrays(data, linesize, buf, AV_PIX_FMT_YUV420P, width, height, 1); av_image_fill_arrays(data, linesize, buf, AV_PIX_FMT_YUV420P, width, height, 1);
dstStride=avframe->linesize[0], srcStride=vpxframe->stride[0], minStride=std::min(dstStride, srcStride); for (int i = 0; i < 3; i++)
for (int i=0; i<height; i++) {
memcpy(avframe->data[0]+dstStride*i, vpxframe->planes[0]+srcStride*i, minStride); int dstStride = avframe->linesize[i];
dstStride=avframe->linesize[1], srcStride=vpxframe->stride[1], minStride=std::min(dstStride, srcStride); int srcStride = vpxframe->stride[i];
for (int i=0; i<height/2; i++) int minStride = std::min(dstStride, srcStride);
memcpy(avframe->data[1]+dstStride*i, vpxframe->planes[1]+srcStride*i, minStride); int size = (i == 0) ? height : height / 2;
dstStride=avframe->linesize[2], srcStride=vpxframe->stride[2], minStride=std::min(dstStride, srcStride);
for (int i=0; i<height/2; i++) for (int j = 0; j < size; j++)
memcpy(avframe->data[2]+dstStride*i, vpxframe->planes[2]+srcStride*i, minStride); {
uint8_t *dst = avframe->data[i] + dstStride * j;
uint8_t *src = vpxframe->planes[i] + srcStride * j;
memcpy(dst, src, minStride);
}
}
vframe = std::make_shared<VideoFrame>(avframe); vframe = std::make_shared<VideoFrame>(avframe);
av_free(buf);
emit frameAvailable(vframe); emit frameAvailable(vframe);
} }

View File

@ -84,12 +84,8 @@ NetCamView::NetCamView(int friendId, QWidget* parent)
videoSurface->setAvatar(pixmap); videoSurface->setAvatar(pixmap);
}); });
VideoMode videoMode; QRect videoSize = Settings::getInstance().getCamVideoRes();
QSize videoSize = Settings::getInstance().getCamVideoRes();
videoMode.width = videoSize.width();
videoMode.height = videoSize.height();
qDebug() << "SIZER" << videoSize; qDebug() << "SIZER" << videoSize;
videoMode.FPS = Settings::getInstance().getCamVideoFPS();
} }
NetCamView::~NetCamView() NetCamView::~NetCamView()

View File

@ -90,25 +90,25 @@ QImage VideoFrame::toQImage(QSize size)
vpx_image *VideoFrame::toVpxImage() vpx_image *VideoFrame::toVpxImage()
{ {
// libvpx doesn't provide a clean way to reuse an existing external buffer vpx_image* img = vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, width, height, 0);
// so we'll manually fill-in the vpx_image fields and hope for the best.
vpx_image* img = new vpx_image;
memset(img, 0, sizeof(vpx_image));
if (!convertToYUV420()) if (!convertToYUV420())
return img; return img;
img->w = img->d_w = width; for (int i = 0; i < 3; i++)
img->h = img->d_h = height; {
img->fmt = VPX_IMG_FMT_I420; int dstStride = img->stride[i];
img->planes[0] = frameYUV420->data[0]; int srcStride = frameYUV420->linesize[i];
img->planes[1] = frameYUV420->data[1]; int minStride = std::min(dstStride, srcStride);
img->planes[2] = frameYUV420->data[2]; int size = (i == 0) ? img->d_h : img->d_h / 2;
img->planes[3] = nullptr;
img->stride[0] = frameYUV420->linesize[0]; for (int j = 0; j < size; j++)
img->stride[1] = frameYUV420->linesize[1]; {
img->stride[2] = frameYUV420->linesize[2]; uint8_t *dst = img->planes[i] + dstStride * j;
img->stride[3] = frameYUV420->linesize[3]; uint8_t *src = frameYUV420->data[i] + srcStride * j;
memcpy(dst, src, minStride);
}
}
return img; return img;
} }

View File

@ -21,13 +21,36 @@
#ifndef VIDEOMODE_H #ifndef VIDEOMODE_H
#define VIDEOMODE_H #define VIDEOMODE_H
#include <QRect>
#include <cstdint>
/// Describes a video mode supported by a device /// Describes a video mode supported by a device
struct VideoMode struct VideoMode
{ {
unsigned short width, height; ///< Displayed video resolution (NOT frame resolution) unsigned short width, height; ///< Displayed video resolution (NOT frame resolution)
unsigned short x, y; ///< Coordinates of upper-left corner
float FPS; ///< Max frames per second supported by the device at this resolution float FPS; ///< Max frames per second supported by the device at this resolution
uint32_t pixel_format; uint32_t pixel_format;
VideoMode(int width = 0, int height = 0, int x = 0, int y = 0,
int FPS = 0, int format = 0) :
width(width), height(height), x(x), y(y),
FPS(FPS), pixel_format(format)
{
}
VideoMode(QRect rect) :
width(rect.width()), height(rect.height()),
x(rect.x()), y(rect.y()),
FPS(0), pixel_format(0)
{
}
QRect toRect() const
{
return QRect(x, y, width, height);
}
/// All zeros means a default/unspecified mode /// All zeros means a default/unspecified mode
operator bool() const operator bool() const
{ {
@ -38,6 +61,8 @@ struct VideoMode
{ {
return width == other.width return width == other.width
&& height == other.height && height == other.height
&& x == other.x
&& y == other.y
&& FPS == other.FPS && FPS == other.FPS
&& pixel_format == other.pixel_format; && pixel_format == other.pixel_format;
} }

View File

@ -25,6 +25,7 @@
#include "src/video/cameradevice.h" #include "src/video/cameradevice.h"
#include "src/video/videosurface.h" #include "src/video/videosurface.h"
#include "src/widget/translator.h" #include "src/widget/translator.h"
#include "src/widget/tool/screenshotgrabber.h"
#include "src/core/core.h" #include "src/core/core.h"
#include "src/core/coreav.h" #include "src/core/coreav.h"
@ -32,14 +33,15 @@
#include <QShowEvent> #include <QShowEvent>
#include <map> #include <map>
#ifndef ALC_ALL_DEVICES_SPECIFIER #ifndef ALC_ALL_DEVICES_SPECIFIER
#define ALC_ALL_DEVICES_SPECIFIER ALC_DEVICE_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER ALC_DEVICE_SPECIFIER
#endif #endif
AVForm::AVForm() : AVForm::AVForm() :
GenericForm(QPixmap(":/img/settings/av.png")) GenericForm(QPixmap(":/img/settings/av.png"))
, subscribedToAudioIn{false} , subscribedToAudioIn(false)
, camVideoSurface{nullptr} , camVideoSurface(nullptr)
, camera(CameraSource::getInstance()) , camera(CameraSource::getInstance())
{ {
bodyUI = new Ui::AVSettings; bodyUI = new Ui::AVSettings;
@ -134,58 +136,80 @@ void AVForm::showEvent(QShowEvent* event)
GenericForm::showEvent(event); GenericForm::showEvent(event);
} }
void AVForm::open(const QString &devName, const VideoMode &mode)
{
QRect rect = mode.toRect();
Settings::getInstance().setCamVideoRes(rect);
Settings::getInstance().setCamVideoFPS(mode.FPS);
camera.open(devName, mode);
}
void AVForm::onVideoModesIndexChanged(int index) void AVForm::onVideoModesIndexChanged(int index)
{ {
if (index<0 || index>=videoModes.size()) if (index < 0 || index >= videoModes.size())
{ {
qWarning() << "Invalid mode index"; qWarning() << "Invalid mode index";
return; return;
} }
int devIndex = bodyUI->videoDevCombobox->currentIndex(); int devIndex = bodyUI->videoDevCombobox->currentIndex();
if (devIndex<0 || devIndex>=videoModes.size()) if (devIndex < 0 || devIndex >= videoDeviceList.size())
{ {
qWarning() << "Invalid device index"; qWarning() << "Invalid device index";
return; return;
} }
QString devName = videoDeviceList[devIndex].first; QString devName = videoDeviceList[devIndex].first;
VideoMode mode = videoModes[index]; VideoMode mode = videoModes[index];
Settings::getInstance().setCamVideoRes(QSize(mode.width, mode.height));
Settings::getInstance().setCamVideoFPS(mode.FPS);
camera.open(devName, mode);
}
void AVForm::updateVideoModes(int curIndex) if (CameraDevice::isScreen(devName) && mode == VideoMode())
{
if (curIndex<0 || curIndex>=videoDeviceList.size())
{ {
qWarning() << "Invalid index"; if (Settings::getInstance().getScreenGrabbed())
{
VideoMode mode(Settings::getInstance().getScreenRegion());
open(devName, mode);
return;
}
ScreenshotGrabber* screenshotGrabber = new ScreenshotGrabber(this);
auto onGrabbed = [screenshotGrabber, devName, this] (QRect region)
{
VideoMode mode(region);
mode.width = mode.width / 2 * 2;
mode.height = mode.height / 2 * 2;
Settings::getInstance().setScreenRegion(mode.toRect());
Settings::getInstance().setScreenGrabbed(true);
open(devName, mode);
delete screenshotGrabber;
};
connect(screenshotGrabber, &ScreenshotGrabber::regionChosen, this, onGrabbed, Qt::QueuedConnection);
screenshotGrabber->showGrabber();
return; return;
} }
QString devName = videoDeviceList[curIndex].first;
QVector<VideoMode> allVideoModes = CameraDevice::getVideoModes(devName);
std::sort(allVideoModes.begin(), allVideoModes.end(),
[](const VideoMode& a, const VideoMode& b)
{return a.width!=b.width ? a.width>b.width :
a.height!=b.height ? a.height>b.height :
a.FPS>b.FPS;});
bool previouslyBlocked = bodyUI->videoModescomboBox->blockSignals(true);
bodyUI->videoModescomboBox->clear();
Settings::getInstance().setScreenGrabbed(false);
open(devName, mode);
}
void AVForm::selectBestModes(QVector<VideoMode> &allVideoModes)
{
// Identify the best resolutions available for the supposed XXXXp resolutions. // Identify the best resolutions available for the supposed XXXXp resolutions.
std::map<int, VideoMode> idealModes; std::map<int, VideoMode> idealModes;
idealModes[120] = {160,120,0,0}; idealModes[120] = VideoMode(160, 120);
idealModes[240] = {460,240,0,0}; idealModes[240] = VideoMode(460, 240);
idealModes[360] = {640,360,0,0}; idealModes[360] = VideoMode(640, 360);
idealModes[480] = {854,480,0,0}; idealModes[480] = VideoMode(854, 480);
idealModes[720] = {1280,720,0,0}; idealModes[720] = VideoMode(1280, 720);
idealModes[1080] = {1920,1080,0,0}; idealModes[1080] = VideoMode(1920, 1080);
std::map<int, int> bestModeInds;
qDebug("available Modes:"); std::map<int, int> bestModeInds;
for (int i=0; i<allVideoModes.size(); ++i) for (int i = 0; i < allVideoModes.size(); ++i)
{ {
VideoMode mode = allVideoModes[i]; VideoMode mode = allVideoModes[i];
qDebug("width: %d, height: %d, FPS: %f, pixel format: %s", mode.width, mode.height, mode.FPS, CameraDevice::getPixelFormatString(mode.pixel_format).toStdString().c_str()); QString pixelFormat = CameraDevice::getPixelFormatString(mode.pixel_format);
qDebug("width: %d, height: %d, FPS: %f, pixel format: %s", mode.width, mode.height, mode.FPS, pixelFormat.toStdString().c_str());
// PS3-Cam protection, everything above 60fps makes no sense // PS3-Cam protection, everything above 60fps makes no sense
if(mode.FPS > 60) if(mode.FPS > 60)
@ -205,120 +229,188 @@ void AVForm::updateVideoModes(int curIndex)
bestModeInds[res] = i; bestModeInds[res] = i;
continue; continue;
} }
int ind = bestModeInds[res];
if (mode.norm(idealMode) < allVideoModes[ind].norm(idealMode)) int index = bestModeInds[res];
VideoMode best = allVideoModes[index];
if (mode.norm(idealMode) < best.norm(idealMode))
{ {
bestModeInds[res] = i; bestModeInds[res] = i;
continue;
} }
else if (mode.norm(idealMode) == allVideoModes[ind].norm(idealMode))
if (mode.norm(idealMode) == best.norm(idealMode))
{ {
// prefer higher FPS and "better" pixel formats // prefer higher FPS and "better" pixel formats
if (mode.FPS > allVideoModes[ind].FPS) if (mode.FPS > best.FPS)
{ {
bestModeInds[res] = i; bestModeInds[res] = i;
continue;
} }
else if (mode.FPS == allVideoModes[ind].FPS &&
CameraDevice::betterPixelFormat(mode.pixel_format, allVideoModes[ind].pixel_format)) bool better = CameraDevice::betterPixelFormat(mode.pixel_format, best.pixel_format);
{ if (mode.FPS == best.FPS && better)
bestModeInds[res] = i; bestModeInds[res] = i;
}
} }
} }
} }
qDebug("selected Modes:");
int prefResIndex = -1; QVector<VideoMode> newVideoModes;
QSize prefRes = Settings::getInstance().getCamVideoRes(); for (auto it = bestModeInds.rbegin(); it != bestModeInds.rend(); ++it)
unsigned short prefFPS = Settings::getInstance().getCamVideoFPS();
// Iterate backwards to show higest resolution first.
videoModes.clear();
for(auto iter = bestModeInds.rbegin(); iter != bestModeInds.rend(); ++iter)
{ {
int i = iter->second; VideoMode mode = allVideoModes[it->second];
VideoMode mode = allVideoModes[i]; auto result = std::find(newVideoModes.begin(), newVideoModes.end(), mode);
if (result == newVideoModes.end())
newVideoModes.push_back(mode);
}
allVideoModes = newVideoModes;
}
if (videoModes.contains(mode)) void AVForm::fillCameraModesComboBox()
continue; {
bool previouslyBlocked = bodyUI->videoModescomboBox->blockSignals(true);
bodyUI->videoModescomboBox->clear();
for(int i = 0; i < videoModes.size(); i++)
{
VideoMode mode = videoModes[i];
videoModes.append(mode);
if (mode.width==prefRes.width() && mode.height==prefRes.height() && mode.FPS == prefFPS && prefResIndex==-1)
prefResIndex = videoModes.size() - 1;
QString str; QString str;
qDebug("width: %d, height: %d, FPS: %f, pixel format: %s\n", mode.width, mode.height, mode.FPS, CameraDevice::getPixelFormatString(mode.pixel_format).toStdString().c_str()); QString pixelFormat = CameraDevice::getPixelFormatString(mode.pixel_format);
qDebug("width: %d, height: %d, FPS: %f, pixel format: %s\n", mode.width, mode.height, mode.FPS, pixelFormat.toStdString().c_str());
if (mode.height && mode.width) if (mode.height && mode.width)
str += tr("%1p").arg(iter->first); str += QString("%1p").arg(mode.height);
else else
str += tr("Default resolution"); str += tr("Default resolution");
bodyUI->videoModescomboBox->addItem(str); bodyUI->videoModescomboBox->addItem(str);
} }
if (videoModes.isEmpty()) if (videoModes.isEmpty())
bodyUI->videoModescomboBox->addItem(tr("Default resolution")); bodyUI->videoModescomboBox->addItem(tr("Default resolution"));
bodyUI->videoModescomboBox->blockSignals(previouslyBlocked); bodyUI->videoModescomboBox->blockSignals(previouslyBlocked);
if (prefResIndex != -1) }
int AVForm::searchPreferredIndex()
{
QRect prefRes = Settings::getInstance().getCamVideoRes();
unsigned short prefFPS = Settings::getInstance().getCamVideoFPS();
for (int i = 0; i < videoModes.size(); i++)
{ {
bodyUI->videoModescomboBox->setCurrentIndex(prefResIndex); VideoMode mode = videoModes[i];
if (mode.width == prefRes.width()
&& mode.height == prefRes.height()
&& mode.FPS == prefFPS)
return i;
}
return -1;
}
void AVForm::fillScreenModesComboBox()
{
bool previouslyBlocked = bodyUI->videoModescomboBox->blockSignals(true);
bodyUI->videoModescomboBox->clear();
for(int i = 0; i < videoModes.size(); i++)
{
VideoMode mode = videoModes[i];
QString pixelFormat = CameraDevice::getPixelFormatString(mode.pixel_format);
qDebug("%dx%d+%d,%d FPS: %f, pixel format: %s\n", mode.width, mode.height, mode.x, mode.y, mode.FPS, pixelFormat.toStdString().c_str());
QString name;
if (mode.width && mode.height)
name = QString("Screen %1").arg(i + 1);
else
name = tr("Select region");
bodyUI->videoModescomboBox->addItem(name);
}
bodyUI->videoModescomboBox->blockSignals(previouslyBlocked);
}
void AVForm::updateVideoModes(int curIndex)
{
if (curIndex < 0 || curIndex >= videoDeviceList.size())
{
qWarning() << "Invalid index";
return;
}
QString devName = videoDeviceList[curIndex].first;
QVector<VideoMode> allVideoModes = CameraDevice::getVideoModes(devName);
qDebug("available Modes:");
bool isScreen = CameraDevice::isScreen(devName);
if (isScreen)
{
// Add extra video mode to region selection
allVideoModes.push_back(VideoMode());
videoModes = allVideoModes;
fillScreenModesComboBox();
} }
else else
{ {
// If the user hasn't set a preffered resolution yet, selectBestModes(allVideoModes);
// we'll pick the resolution in the middle of the list, videoModes = allVideoModes;
// and the best FPS for that resolution.
// If we picked the lowest resolution, the quality would be awful
// but if we picked the largest, FPS would be bad and thus quality bad too.
int numRes=0;
QSize lastSize;
for (int i=0; i<videoModes.size(); i++)
{
if (lastSize != QSize{videoModes[i].width, videoModes[i].height})
{
numRes++;
lastSize = {videoModes[i].width, videoModes[i].height};
}
}
int target = numRes/2;
numRes=0;
for (int i=0; i<videoModes.size(); i++)
{
if (lastSize != QSize{videoModes[i].width, videoModes[i].height})
{
numRes++;
lastSize = {videoModes[i].width, videoModes[i].height};
}
if (numRes==target)
{
bodyUI->videoModescomboBox->setCurrentIndex(i);
break;
}
}
if (videoModes.size()) qDebug("selected Modes:");
{ fillCameraModesComboBox();
bodyUI->videoModescomboBox->setUpdatesEnabled(false);
bodyUI->videoModescomboBox->setCurrentIndex(-1);
bodyUI->videoModescomboBox->setUpdatesEnabled(true);
bodyUI->videoModescomboBox->setCurrentIndex(0);
}
else
{
// We don't have any video modes, open it with the default mode
camera.open(devName);
}
} }
int preferedIndex = searchPreferredIndex();
if (preferedIndex != -1)
{
bodyUI->videoModescomboBox->setCurrentIndex(preferedIndex);
return;
}
if (isScreen)
{
QRect rect = Settings::getInstance().getScreenRegion();
VideoMode mode(rect);
Settings::getInstance().setScreenGrabbed(true);
bodyUI->videoModescomboBox->setCurrentIndex(videoModes.size() - 1);
open(devName, mode);
return;
}
// If the user hasn't set a preferred resolution yet,
// we'll pick the resolution in the middle of the list,
// and the best FPS for that resolution.
// If we picked the lowest resolution, the quality would be awful
// but if we picked the largest, FPS would be bad and thus quality bad too.
int mid = (videoModes.size() - 1) / 2;
bodyUI->videoModescomboBox->setCurrentIndex(mid);
} }
void AVForm::onVideoDevChanged(int index) void AVForm::onVideoDevChanged(int index)
{ {
if (index<0 || index>=videoDeviceList.size()) if (index < 0 || index >= videoDeviceList.size())
{ {
qWarning() << "Invalid index"; qWarning() << "Invalid index";
return; return;
} }
Settings::getInstance().setScreenGrabbed(false);
QString dev = videoDeviceList[index].first; QString dev = videoDeviceList[index].first;
Settings::getInstance().setVideoDev(dev); Settings::getInstance().setVideoDev(dev);
bool previouslyBlocked = bodyUI->videoModescomboBox->blockSignals(true); bool previouslyBlocked = bodyUI->videoModescomboBox->blockSignals(true);
updateVideoModes(index); updateVideoModes(index);
bodyUI->videoModescomboBox->blockSignals(previouslyBlocked); bodyUI->videoModescomboBox->blockSignals(previouslyBlocked);
camera.open(dev);
if (Settings::getInstance().getScreenGrabbed())
return;
int modeIndex = bodyUI->videoModescomboBox->currentIndex();
VideoMode mode = VideoMode();
if (0 < modeIndex && modeIndex < videoModes.size())
mode = videoModes[modeIndex];
camera.open(dev, mode);
if (dev == "none") if (dev == "none")
Core::getInstance()->getAv()->sendNoVideo(); Core::getInstance()->getAv()->sendNoVideo();
} }

View File

@ -46,6 +46,11 @@ private:
void getAudioOutDevices(); void getAudioOutDevices();
void getVideoDevices(); void getVideoDevices();
void selectBestModes(QVector<VideoMode> &allVideoModes);
void fillCameraModesComboBox();
void fillScreenModesComboBox();
int searchPreferredIndex();
void createVideoSurface(); void createVideoSurface();
void killVideoSurface(); void killVideoSurface();
@ -70,9 +75,9 @@ protected:
private: private:
bool eventFilter(QObject *o, QEvent *e) final override; bool eventFilter(QObject *o, QEvent *e) final override;
void hideEvent(QHideEvent* event) final override; void hideEvent(QHideEvent* event) final override;
void showEvent(QShowEvent*event) final override; void showEvent(QShowEvent*event) final override;
void open(const QString &devName, const VideoMode &mode);
private: private:
Ui::AVSettings *bodyUI; Ui::AVSettings *bodyUI;

View File

@ -131,6 +131,7 @@ void ScreenshotGrabber::acceptRegion()
if (rect.width() < 1 || rect.height() < 1) if (rect.width() < 1 || rect.height() < 1)
return; return;
emit regionChosen(rect);
qDebug() << "Screenshot accepted, chosen region" << rect; qDebug() << "Screenshot accepted, chosen region" << rect;
QPixmap pixmap = this->screenGrab.copy(rect); QPixmap pixmap = this->screenGrab.copy(rect);
this->window->close(); this->window->close();

View File

@ -52,6 +52,7 @@ public slots:
signals: signals:
void screenshotTaken(const QPixmap &pixmap); void screenshotTaken(const QPixmap &pixmap);
void regionChosen(QRect region);
void rejected(); void rejected();
private: private: