mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-25 15:39:27 -05:00
added preview option to show decoded stream in Audio config
This commit is contained in:
parent
2107a1c858
commit
c0614e70ac
@ -55,9 +55,9 @@ void AudioInputDialog::showEvent(QShowEvent *) {
|
|||||||
class voipGraphSource: public RSGraphSource
|
class voipGraphSource: public RSGraphSource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
voipGraphSource() {}
|
voipGraphSource() : video_input(NULL) {}
|
||||||
|
|
||||||
void setVideoInput(QVideoInputDevice *vid) { video_input = vid ; }
|
void setVideoInput(const QVideoInputDevice *vid) { video_input = vid ; }
|
||||||
|
|
||||||
virtual QString displayName(int) const { return tr("Required bandwidth") ;}
|
virtual QString displayName(int) const { return tr("Required bandwidth") ;}
|
||||||
|
|
||||||
@ -73,21 +73,14 @@ public:
|
|||||||
|
|
||||||
virtual void getValues(std::map<std::string,float>& vals) const
|
virtual void getValues(std::map<std::string,float>& vals) const
|
||||||
{
|
{
|
||||||
RsVOIPDataChunk chunk ;
|
|
||||||
uint32_t total_size = 0 ;
|
|
||||||
vals.clear() ;
|
vals.clear() ;
|
||||||
|
|
||||||
while(video_input && video_input->getNextEncodedPacket(chunk))
|
if(video_input)
|
||||||
{
|
vals[std::string("bw")] = video_input->currentBandwidth() ;
|
||||||
total_size += chunk.size ;
|
|
||||||
chunk.clear() ;
|
|
||||||
}
|
|
||||||
|
|
||||||
vals[std::string("bw")] = (float)total_size ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVideoInputDevice *video_input ;
|
const QVideoInputDevice *video_input ;
|
||||||
};
|
};
|
||||||
|
|
||||||
void voipGraph::setVoipSource(voipGraphSource *gs)
|
void voipGraph::setVoipSource(voipGraphSource *gs)
|
||||||
@ -127,12 +120,31 @@ AudioInputConfig::AudioInputConfig(QWidget * parent, Qt::WindowFlags flags)
|
|||||||
videoInput->setEchoVideoTarget(ui.videoDisplay) ;
|
videoInput->setEchoVideoTarget(ui.videoDisplay) ;
|
||||||
videoInput->setVideoEncoder(new JPEGVideoEncoder()) ;
|
videoInput->setVideoEncoder(new JPEGVideoEncoder()) ;
|
||||||
|
|
||||||
|
videoDecoder = new JPEGVideoDecoder;
|
||||||
|
videoDecoder->setDisplayTarget(NULL) ;
|
||||||
|
|
||||||
graph_source = new voipGraphSource ;
|
graph_source = new voipGraphSource ;
|
||||||
ui.voipBwGraph->setSource(graph_source);
|
ui.voipBwGraph->setSource(graph_source);
|
||||||
|
|
||||||
graph_source->setVideoInput(videoInput) ;
|
graph_source->setVideoInput(videoInput) ;
|
||||||
graph_source->setCollectionTimeLimit(1000*300) ;
|
graph_source->setCollectionTimeLimit(1000*300) ;
|
||||||
graph_source->start() ;
|
graph_source->start() ;
|
||||||
|
|
||||||
|
QObject::connect(ui.showEncoded_CB,SIGNAL(toggled(bool)),this,SLOT(togglePreview(bool))) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputConfig::togglePreview(bool b)
|
||||||
|
{
|
||||||
|
if(b)
|
||||||
|
{
|
||||||
|
videoInput->setEchoVideoTarget(NULL) ;
|
||||||
|
videoDecoder->setDisplayTarget(ui.videoDisplay) ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
videoInput->setEchoVideoTarget(ui.videoDisplay) ;
|
||||||
|
videoDecoder->setDisplayTarget(NULL) ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioInputConfig::~AudioInputConfig()
|
AudioInputConfig::~AudioInputConfig()
|
||||||
@ -352,6 +364,15 @@ void AudioInputConfig::on_Tick_timeout() {
|
|||||||
abSpeech->iValue = iroundf(inputAudioProcessor->dVoiceAcivityLevel * 32767.0f + 0.5f);
|
abSpeech->iValue = iroundf(inputAudioProcessor->dVoiceAcivityLevel * 32767.0f + 0.5f);
|
||||||
|
|
||||||
abSpeech->update();
|
abSpeech->update();
|
||||||
|
|
||||||
|
// also transmit encoded video
|
||||||
|
RsVOIPDataChunk chunk ;
|
||||||
|
|
||||||
|
while(videoInput->getNextEncodedPacket(chunk))
|
||||||
|
{
|
||||||
|
videoDecoder->receiveEncodedData(static_cast<const unsigned char*>(chunk.data),chunk.size) ;
|
||||||
|
chunk.clear() ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioInputConfig::emptyBuffer() {
|
void AudioInputConfig::emptyBuffer() {
|
||||||
|
@ -69,6 +69,7 @@ class AudioInputConfig : public ConfigPage
|
|||||||
//VideoDecoder *videoDecoder ;
|
//VideoDecoder *videoDecoder ;
|
||||||
//VideoEncoder *videoEncoder ;
|
//VideoEncoder *videoEncoder ;
|
||||||
QVideoInputDevice *videoInput ;
|
QVideoInputDevice *videoInput ;
|
||||||
|
VideoDecoder *videoDecoder ;
|
||||||
bool loaded;
|
bool loaded;
|
||||||
|
|
||||||
voipGraphSource *graph_source ;
|
voipGraphSource *graph_source ;
|
||||||
@ -96,6 +97,7 @@ class AudioInputConfig : public ConfigPage
|
|||||||
private slots:
|
private slots:
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
void emptyBuffer();
|
void emptyBuffer();
|
||||||
|
void togglePreview(bool) ;
|
||||||
|
|
||||||
void on_qsTransmitHold_valueChanged(int v);
|
void on_qsTransmitHold_valueChanged(int v);
|
||||||
void on_qsAmp_valueChanged(int v);
|
void on_qsAmp_valueChanged(int v);
|
||||||
|
@ -391,6 +391,16 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="showEncoded_CB">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Display encoded (and then decoded) frame, to check the codec's quality. If not selected, the image above only shows the frame that is grabbed from your camera.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>preview</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer>
|
<spacer>
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -13,6 +13,9 @@ QVideoInputDevice::QVideoInputDevice(QWidget *parent)
|
|||||||
_capture_device = NULL ;
|
_capture_device = NULL ;
|
||||||
_video_encoder = NULL ;
|
_video_encoder = NULL ;
|
||||||
_echo_output_device = NULL ;
|
_echo_output_device = NULL ;
|
||||||
|
_estimated_bw = 0 ;
|
||||||
|
_total_encoded_size = 0 ;
|
||||||
|
_last_bw_estimate_TS = time(NULL) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QVideoInputDevice::stop()
|
void QVideoInputDevice::stop()
|
||||||
@ -54,36 +57,52 @@ void QVideoInputDevice::start()
|
|||||||
|
|
||||||
void QVideoInputDevice::grabFrame()
|
void QVideoInputDevice::grabFrame()
|
||||||
{
|
{
|
||||||
IplImage *img=cvQueryFrame(_capture_device);
|
IplImage *img=cvQueryFrame(_capture_device);
|
||||||
|
|
||||||
if(img == NULL)
|
if(img == NULL)
|
||||||
{
|
{
|
||||||
std::cerr << "(EE) Cannot capture image from camera. Something's wrong." << std::endl;
|
std::cerr << "(EE) Cannot capture image from camera. Something's wrong." << std::endl;
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
// get the image data
|
// get the image data
|
||||||
|
|
||||||
if(img->nChannels != 3)
|
if(img->nChannels != 3)
|
||||||
{
|
{
|
||||||
std::cerr << "(EE) expected 3 channels. Got " << img->nChannels << std::endl;
|
std::cerr << "(EE) expected 3 channels. Got " << img->nChannels << std::endl;
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert to RGB and copy to new buffer, because cvQueryFrame tells us to not modify the buffer
|
// convert to RGB and copy to new buffer, because cvQueryFrame tells us to not modify the buffer
|
||||||
cv::Mat img_rgb;
|
cv::Mat img_rgb;
|
||||||
cv::cvtColor(cv::Mat(img), img_rgb, CV_BGR2RGB);
|
cv::cvtColor(cv::Mat(img), img_rgb, CV_BGR2RGB);
|
||||||
|
|
||||||
static const int _encoded_width = 128 ;
|
QImage image = QImage(img_rgb.data,img_rgb.cols,img_rgb.rows,QImage::Format_RGB888);
|
||||||
static const int _encoded_height = 128 ;
|
|
||||||
|
|
||||||
QImage image = QImage(img_rgb.data,img_rgb.cols,img_rgb.rows,QImage::Format_RGB888).scaled(QSize(_encoded_width,_encoded_height),Qt::IgnoreAspectRatio,Qt::SmoothTransformation) ;
|
if(_video_encoder != NULL)
|
||||||
|
{
|
||||||
|
uint32_t encoded_size ;
|
||||||
|
|
||||||
if(_video_encoder != NULL)
|
_video_encoder->addImage(image,0,encoded_size) ;
|
||||||
{
|
|
||||||
_video_encoder->addImage(image) ;
|
std::cerr << "Encoded size = " << encoded_size << std::endl;
|
||||||
emit networkPacketReady() ;
|
_total_encoded_size += encoded_size ;
|
||||||
}
|
|
||||||
if(_echo_output_device != NULL) _echo_output_device->showFrame(image) ;
|
time_t now = time(NULL) ;
|
||||||
|
|
||||||
|
if(now > _last_bw_estimate_TS)
|
||||||
|
{
|
||||||
|
_estimated_bw = uint32_t(0.75*_estimated_bw + 0.25 * (_total_encoded_size / (float)(now - _last_bw_estimate_TS))) ;
|
||||||
|
|
||||||
|
_total_encoded_size = 0 ;
|
||||||
|
_last_bw_estimate_TS = now ;
|
||||||
|
|
||||||
|
std::cerr << "new bw estimate: " << _estimated_bw << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit networkPacketReady() ;
|
||||||
|
}
|
||||||
|
if(_echo_output_device != NULL)
|
||||||
|
_echo_output_device->showFrame(image) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QVideoInputDevice::getNextEncodedPacket(RsVOIPDataChunk& chunk)
|
bool QVideoInputDevice::getNextEncodedPacket(RsVOIPDataChunk& chunk)
|
||||||
|
@ -42,6 +42,12 @@ class QVideoInputDevice: public QObject
|
|||||||
//
|
//
|
||||||
bool getNextEncodedPacket(RsVOIPDataChunk&) ;
|
bool getNextEncodedPacket(RsVOIPDataChunk&) ;
|
||||||
|
|
||||||
|
// gets the estimated current bandwidth required to transmit the encoded data, in B/s
|
||||||
|
//
|
||||||
|
uint32_t currentBandwidth() const { return _estimated_bw ; }
|
||||||
|
|
||||||
|
// control
|
||||||
|
|
||||||
void start() ;
|
void start() ;
|
||||||
void stop() ;
|
void stop() ;
|
||||||
|
|
||||||
@ -59,5 +65,9 @@ class QVideoInputDevice: public QObject
|
|||||||
QVideoOutputDevice *_echo_output_device ;
|
QVideoOutputDevice *_echo_output_device ;
|
||||||
|
|
||||||
std::list<RsVOIPDataChunk> _out_queue ;
|
std::list<RsVOIPDataChunk> _out_queue ;
|
||||||
|
|
||||||
|
uint32_t _estimated_bw ;
|
||||||
|
time_t _last_bw_estimate_TS;
|
||||||
|
uint32_t _total_encoded_size ;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,9 +12,15 @@ VideoDecoder::VideoDecoder()
|
|||||||
_output_device = NULL ;
|
_output_device = NULL ;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoEncoder::addImage(const QImage& img)
|
VideoEncoder::VideoEncoder()
|
||||||
|
:_frame_size(128,128)
|
||||||
{
|
{
|
||||||
encodeData(img) ;
|
}
|
||||||
|
|
||||||
|
bool VideoEncoder::addImage(const QImage& img,uint32_t size_hint,uint32_t& encoded_size)
|
||||||
|
{
|
||||||
|
std::cerr << "reducing to " << _frame_size.width() << " x " << _frame_size.height() << std::endl;
|
||||||
|
encodeData(img.scaled(_frame_size,Qt::IgnoreAspectRatio,Qt::SmoothTransformation),size_hint,encoded_size) ;
|
||||||
|
|
||||||
return true ;
|
return true ;
|
||||||
}
|
}
|
||||||
@ -32,6 +38,7 @@ bool VideoEncoder::nextPacket(RsVOIPDataChunk& chunk)
|
|||||||
|
|
||||||
void VideoDecoder::receiveEncodedData(const unsigned char *data,uint32_t size)
|
void VideoDecoder::receiveEncodedData(const unsigned char *data,uint32_t size)
|
||||||
{
|
{
|
||||||
|
if(_output_device)
|
||||||
_output_device->showFrame(decodeData(data,size)) ;
|
_output_device->showFrame(decodeData(data,size)) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,24 +57,54 @@ QImage JPEGVideoDecoder::decodeData(const unsigned char *encoded_image_data,uint
|
|||||||
|
|
||||||
void VideoEncoder::setMaximumFrameRate(uint32_t bytes_per_sec)
|
void VideoEncoder::setMaximumFrameRate(uint32_t bytes_per_sec)
|
||||||
{
|
{
|
||||||
std::cerr << "Video Encoder: maximum frame rate is set to " << bytes_per_sec << " Bps" << std::endl;
|
std::cerr << "Video Encoder: maximum frame rate is set to " << bytes_per_sec << " Bps" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VideoEncoder::setInternalFrameSize(QSize s)
|
||||||
void JPEGVideoEncoder::encodeData(const QImage& image)
|
|
||||||
{
|
{
|
||||||
QByteArray qb ;
|
_frame_size = s ;
|
||||||
|
}
|
||||||
QBuffer buffer(&qb) ;
|
|
||||||
buffer.open(QIODevice::WriteOnly) ;
|
JPEGVideoEncoder::JPEGVideoEncoder()
|
||||||
image.save(&buffer,"JPEG") ;
|
: _ref_frame_max_distance(10),_ref_frame_count(10)
|
||||||
|
{
|
||||||
RsVOIPDataChunk voip_chunk ;
|
}
|
||||||
voip_chunk.data = malloc(qb.size());
|
|
||||||
memcpy(voip_chunk.data,qb.data(),qb.size()) ;
|
void JPEGVideoEncoder::encodeData(const QImage& image,uint32_t /* size_hint */,uint32_t& encoded_size)
|
||||||
voip_chunk.size = qb.size() ;
|
{
|
||||||
voip_chunk.type = RsVOIPDataChunk::RS_VOIP_DATA_TYPE_VIDEO ;
|
// check if we make a diff image, or if we use the full frame.
|
||||||
|
|
||||||
_out_queue.push_back(voip_chunk) ;
|
QImage encoded_frame ;
|
||||||
|
|
||||||
|
if(_ref_frame_count++ < _ref_frame_max_distance && image.size() == _reference_frame.size())
|
||||||
|
{
|
||||||
|
// compute difference with reference frame.
|
||||||
|
encoded_frame = image ;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<image.byteCount();++i)
|
||||||
|
encoded_frame.bits()[i] = image.bits()[i] - _reference_frame.bits()[i] + 128 ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ref_frame_count = 0 ;
|
||||||
|
_reference_frame = image ;
|
||||||
|
encoded_frame = image ;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray qb ;
|
||||||
|
|
||||||
|
QBuffer buffer(&qb) ;
|
||||||
|
buffer.open(QIODevice::WriteOnly) ;
|
||||||
|
encoded_frame.save(&buffer,"JPEG") ;
|
||||||
|
|
||||||
|
RsVOIPDataChunk voip_chunk ;
|
||||||
|
voip_chunk.data = malloc(qb.size());
|
||||||
|
memcpy(voip_chunk.data,qb.data(),qb.size()) ;
|
||||||
|
voip_chunk.size = qb.size() ;
|
||||||
|
voip_chunk.type = RsVOIPDataChunk::RS_VOIP_DATA_TYPE_VIDEO ;
|
||||||
|
|
||||||
|
_out_queue.push_back(voip_chunk) ;
|
||||||
|
|
||||||
|
encoded_size = voip_chunk.size ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,12 +49,12 @@ class VideoDecoder
|
|||||||
class VideoEncoder
|
class VideoEncoder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VideoEncoder() {}
|
VideoEncoder() ;
|
||||||
virtual ~VideoEncoder() {}
|
virtual ~VideoEncoder() {}
|
||||||
|
|
||||||
// Takes the next image to be encoded.
|
// Takes the next image to be encoded.
|
||||||
//
|
//
|
||||||
bool addImage(const QImage& Image) ;
|
bool addImage(const QImage& Image, uint32_t size_hint, uint32_t &encoded_size) ;
|
||||||
|
|
||||||
bool packetReady() const { return !_out_queue.empty() ; }
|
bool packetReady() const { return !_out_queue.empty() ; }
|
||||||
bool nextPacket(RsVOIPDataChunk& ) ;
|
bool nextPacket(RsVOIPDataChunk& ) ;
|
||||||
@ -62,12 +62,14 @@ class VideoEncoder
|
|||||||
// Used to tweak the compression ratio so that the video can stream ok.
|
// Used to tweak the compression ratio so that the video can stream ok.
|
||||||
//
|
//
|
||||||
void setMaximumFrameRate(uint32_t bytes_per_second) ;
|
void setMaximumFrameRate(uint32_t bytes_per_second) ;
|
||||||
|
void setInternalFrameSize(QSize) ;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//virtual bool sendEncodedData(unsigned char *mem,uint32_t size) = 0 ;
|
virtual void encodeData(const QImage& Image, uint32_t size_hint, uint32_t &encoded_size) =0;
|
||||||
virtual void encodeData(const QImage& image) = 0 ;
|
|
||||||
|
|
||||||
std::list<RsVOIPDataChunk> _out_queue ;
|
std::list<RsVOIPDataChunk> _out_queue ;
|
||||||
|
|
||||||
|
QSize _frame_size ;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Now derive various image encoding/decoding algorithms.
|
// Now derive various image encoding/decoding algorithms.
|
||||||
@ -75,16 +77,45 @@ class VideoEncoder
|
|||||||
|
|
||||||
class JPEGVideoDecoder: public VideoDecoder
|
class JPEGVideoDecoder: public VideoDecoder
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
virtual QImage decodeData(const unsigned char *encoded_image,uint32_t encoded_image_size) ;
|
virtual QImage decodeData(const unsigned char *encoded_image,uint32_t encoded_image_size) ;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage _last_reference_frame ;
|
||||||
};
|
};
|
||||||
|
|
||||||
class JPEGVideoEncoder: public VideoEncoder
|
class JPEGVideoEncoder: public VideoEncoder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
JPEGVideoEncoder() {}
|
JPEGVideoEncoder() ;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void encodeData(const QImage& Image) ;
|
virtual void encodeData(const QImage& Image, uint32_t size_hint, uint32_t &encoded_size) ;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage _reference_frame ;
|
||||||
|
uint32_t _ref_frame_max_distance ; // max distance between two reference frames.
|
||||||
|
uint32_t _ref_frame_count ;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DifferentialWaveletEncoder: public VideoEncoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DifferentialWaveletEncoder() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void encodeData(const QImage& Image, uint32_t size_hint, uint32_t &encoded_size) ;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class DifferentialWaveletDecoder: public VideoDecoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DifferentialWaveletDecoder() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QImage decodeData(const unsigned char *encoded_image,uint32_t encoded_image_size) ;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage _last_reference_frame ;
|
||||||
|
};
|
||||||
|
@ -51,7 +51,9 @@ class RsVOIPPongResult
|
|||||||
|
|
||||||
struct RsVOIPDataChunk
|
struct RsVOIPDataChunk
|
||||||
{
|
{
|
||||||
typedef enum { RS_VOIP_DATA_TYPE_AUDIO, RS_VOIP_DATA_TYPE_VIDEO } RsVOIPDataType ;
|
typedef enum { RS_VOIP_DATA_TYPE_UNKNOWN = 0x00,
|
||||||
|
RS_VOIP_DATA_TYPE_AUDIO = 0x01,
|
||||||
|
RS_VOIP_DATA_TYPE_VIDEO = 0x02 } RsVOIPDataType ;
|
||||||
|
|
||||||
void *data ; // create/delete using malloc/free.
|
void *data ; // create/delete using malloc/free.
|
||||||
uint32_t size ;
|
uint32_t size ;
|
||||||
|
Loading…
Reference in New Issue
Block a user