diff options
| author | 2010-04-06 11:03:56 +0000 | |
|---|---|---|
| committer | 2010-04-06 11:03:56 +0000 | |
| commit | 2e9a3fd886191137627ecb83bc9c8a4c8d3463c5 (patch) | |
| tree | 324e257977f64e2e62a7a68f48cd7ecbb1fe5741 /src | |
| parent | added a workaround for the userlist/ircview width problem (diff) | |
| download | KVIrc-2e9a3fd886191137627ecb83bc9c8a4c8d3463c5.tar.gz KVIrc-2e9a3fd886191137627ecb83bc9c8a4c8d3463c5.tar.bz2 KVIrc-2e9a3fd886191137627ecb83bc9c8a4c8d3463c5.zip | |
more work on dcc video: video&&text working in sjpeg; stay away from ogg/theora by now
git-svn-id: https://svn.kvirc.de/svn/trunk/kvirc@4242 17fca916-40b9-46aa-a4ea-0a15b648b75c
Diffstat (limited to 'src')
| -rw-r--r-- | src/kvilib/ext/kvi_oggtheora.cpp | 67 | ||||
| -rw-r--r-- | src/kvilib/ext/kvi_oggtheora.h | 98 | ||||
| -rw-r--r-- | src/kvirc/kernel/kvi_userinput.cpp | 1 | ||||
| -rw-r--r-- | src/kvirc/kvs/kvi_kvs_coresimplecommands_mr.cpp | 1 | ||||
| -rw-r--r-- | src/kvirc/ui/kvi_window.h | 3 | ||||
| -rw-r--r-- | src/modules/dcc/codec.cpp | 92 | ||||
| -rw-r--r-- | src/modules/dcc/codec.h | 12 | ||||
| -rw-r--r-- | src/modules/dcc/libkvidcc.cpp | 2 | ||||
| -rw-r--r-- | src/modules/dcc/video.cpp | 758 | ||||
| -rw-r--r-- | src/modules/dcc/video.h | 47 |
10 files changed, 619 insertions, 462 deletions
diff --git a/src/kvilib/ext/kvi_oggtheora.cpp b/src/kvilib/ext/kvi_oggtheora.cpp index eecb29773..a234dffaa 100644 --- a/src/kvilib/ext/kvi_oggtheora.cpp +++ b/src/kvilib/ext/kvi_oggtheora.cpp @@ -79,6 +79,10 @@ KviTheoraEncoder::KviTheoraEncoder(KviDataBuffer * stream, int iWidth, int iHeig audio_q=.6f; samples_sofar=0; + //text + text=true; + text_sofar=0; + //user settings m_pStream=stream; geometry.pic_w=iWidth; @@ -125,6 +129,18 @@ KviTheoraEncoder::KviTheoraEncoder(KviDataBuffer * stream, int iWidth, int iHeig vorbis_block_init(&vd,&vb); } + // initialize out text codec + if(text) + { + ogg_stream_init(&zo,rand()); + ret = KviOggIrcText::irct_encode_init(); + if(ret) + { + qDebug("error initializing irctext"); + return; + } + } + // Set up Theora encoder // Theora has a divisible-by-sixteen restriction for the encoded frame size // scale the picture size up to the nearest /16 and calculate offsets @@ -196,7 +212,8 @@ KviTheoraEncoder::KviTheoraEncoder(KviDataBuffer * stream, int iWidth, int iHeig ogg_stream_packetin(&to,&op); } - if(audio){ + if(audio) + { ogg_packet header; ogg_packet header_comm; ogg_packet header_code; @@ -213,6 +230,19 @@ KviTheoraEncoder::KviTheoraEncoder(KviDataBuffer * stream, int iWidth, int iHeig ogg_stream_packetin(&vo,&header_code); } + if(text) + { + ogg_packet header; + KviOggIrcText::irct_encode_headerout(&header); + ogg_stream_packetin(&zo,&header); // automatically placed in its own page + if(ogg_stream_pageout(&zo,&og)!=1){ + qDebug("Internal Ogg library error (irct)."); + return; + } + m_pStream->append(og.header,og.header_len); + m_pStream->append(og.body,og.body_len); + } + // Flush the rest of our headers. This ensures the actual data in each stream // will start on a new page, as per spec. for(;;){ @@ -320,6 +350,31 @@ void KviTheoraEncoder::addAudioFrame(unsigned char* audioPkt, int audioSize) audioflag=0; } +void KviTheoraEncoder::addTextFrame(unsigned char* textPkt, int textSize) +{ + if(!text) + return; + + ogg_page textpage; + ogg_packet op; + + text_sofar++; + + // is there an audio page flushed? If not, fetch one if possible + textflag=KviOggIrcText::irct_encode_packetout((char *) textPkt, textSize, text_sofar,&op); + ogg_stream_packetin(&zo, &op); + ogg_stream_pageout(&zo, &textpage); + + + + // no pages? Must be end of stream. + if(!textflag) return; + + m_pStream->append(textpage.header,textpage.header_len); + m_pStream->append(textpage.body,textpage.body_len); + textflag=0; +} + int KviTheoraEncoder::fetch_and_process_video_packet(quint8 * videoYuv,th_enc_ctx *td,ogg_packet *op) { th_ycbcr_buffer ycbcr; @@ -589,15 +644,20 @@ void KviTheoraDecoder::addData(KviDataBuffer * stream) /* identify the codec: try theora */ if(!theora_p && th_decode_headerin(&ti,&tc,&ts,&op)>=0){ -// qDebug("is theora, ts=%p &ts=%p",ts, &ts); + qDebug("is theora, ts=%p &ts=%p",ts, &ts); /* it is theora */ memcpy(&to,&test,sizeof(test)); theora_p=1; }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){ -// qDebug("is vorbis"); + qDebug("is vorbis"); /* it is vorbis */ memcpy(&vo,&test,sizeof(test)); vorbis_p=1; + }else if(!irct_p && KviOggIrcText::irct_decode_headerin(&op)>=0){ + qDebug("is irct"); + /* it is irct */ + memcpy(&zo,&test,sizeof(test)); + irct_p=1; }else{ // qDebug("is other"); /* whatever it is, we don't care about it */ @@ -885,6 +945,7 @@ int KviTheoraDecoder::queue_page(ogg_page *page) { if(theora_p)ogg_stream_pagein(&to,page); if(vorbis_p)ogg_stream_pagein(&vo,page); + if(irct_p)ogg_stream_pagein(&zo,page); return 0; } diff --git a/src/kvilib/ext/kvi_oggtheora.h b/src/kvilib/ext/kvi_oggtheora.h index c9db484e8..751579cc7 100644 --- a/src/kvilib/ext/kvi_oggtheora.h +++ b/src/kvilib/ext/kvi_oggtheora.h @@ -27,6 +27,7 @@ #ifndef COMPILE_DISABLE_OGG_THEORA #include "kvi_settings.h" +#include "kvi_malloc.h" #include "theora/theoradec.h" #include "theora/theoraenc.h" @@ -56,12 +57,98 @@ typedef struct _KviTheoraGeometry { int pic_y; } KviTheoraGeometry; -class KVILIB_API KviTheoraEncoder { +class KVILIB_API KviOggIrcText +{ +private: + static void _tp_readbuffer(oggpack_buffer *opb, char *buf, const long len) + { + long i; + + for (i = 0; i < len; i++) + *buf++=oggpack_read(opb,8); + } + + static void _tp_writebuffer(oggpack_buffer *opb, const char *buf, const long len) + { + long i; + + for (i = 0; i < len; i++) + oggpack_write(opb, *buf++, 8); + } + +public: + static int irct_encode_init() + { + return 0; + }; + + static int irct_encode_clear() + { + return 0; + }; + + static int irct_encode_headerout(ogg_packet *op) { + oggpack_buffer ob; + oggpack_writeinit(&ob); + oggpack_write(&ob, 0, 8); //header init + _tp_writebuffer(&ob, "irct", 32); + oggpack_write(&ob, 0, 8); //version 0 + oggpack_write(&ob, 1, 8); //subversion 1 + int bytes=oggpack_bytes(&ob); + op->packet= (unsigned char*) kvi_malloc(bytes); + memcpy(op->packet, oggpack_get_buffer(&ob), bytes); + op->bytes=bytes; + oggpack_writeclear(&ob); + op->b_o_s=1; //begins a logical bitstream + op->e_o_s=0; + op->packetno=0; + op->granulepos=0; + + return 0; + } + static int irct_encode_packetout(const char* textPkt, int textSize, int last_p, ogg_packet *op) { + if(!textSize)return(0); + + oggpack_buffer ob; + oggpack_writeinit(&ob); + _tp_writebuffer(&ob, textPkt, textSize); //pre-encoded text + int bytes=oggpack_bytes(&ob); + op->packet= (unsigned char *)kvi_malloc(bytes); + memcpy(op->packet, oggpack_get_buffer(&ob), bytes); + op->bytes=bytes; + oggpack_writeclear(&ob); + op->b_o_s=0; + op->e_o_s=last_p; + + op->packetno=last_p; + op->granulepos=0; + + return 0; + } + static int irct_decode_headerin(ogg_packet *op) { + oggpack_buffer ob; + oggpack_readinit(&ob, op->packet, op->bytes); + quint8 ret, version, subversion; + ret = oggpack_read(&ob,8); + if(ret!=0) return 1; + char * buf = (char*) kvi_malloc(4); + _tp_readbuffer(&ob, buf, 4); + if(strncmp(buf, "irct", 4)!=0) return 1; + version = oggpack_read(&ob,8); + subversion = oggpack_read(&ob,8); + qDebug("irct stream version %d.%d", version, subversion); + return 0; + } +}; + +class KVILIB_API KviTheoraEncoder +{ public: KviTheoraEncoder(KviDataBuffer * stream, int iWidth=320, int iHeight=240, int iFpsN=5, int iFpsD=1, int iParN=4, int iParD=3); virtual ~KviTheoraEncoder(); void addVideoFrame(QRgb * rgb32, int videoSize); void addAudioFrame(unsigned char* audioPkt, int audioSize); + void addTextFrame(unsigned char* textPkt, int textSize); private: int fetch_and_process_video(quint8 * videoYuv,ogg_page *videopage,ogg_stream_state *to,th_enc_ctx *td,int videoflag); int fetch_and_process_video_packet(quint8 * videoYuv,th_enc_ctx *td,ogg_packet *op); @@ -71,12 +158,15 @@ private: KviDataBuffer * m_pStream; quint8 * videoYuv; + bool text; + bool audio; int audio_ch; int audio_hz; float audio_q; ogg_int64_t samples_sofar; - + ogg_int64_t text_sofar; + int frame_state; ogg_int64_t frames; unsigned char *yuvframe[3]; @@ -90,6 +180,7 @@ private: // The amount to read into the auxilliary buffer. size_t y4m_aux_buf_read_sz; + ogg_stream_state zo; // take physical pages, weld into a logical stream of packets ogg_stream_state to; // take physical pages, weld into a logical stream of packets ogg_stream_state vo; // take physical pages, weld into a logical stream of packets ogg_page og; // one Ogg bitstream page. Vorbis packets are inside @@ -104,6 +195,7 @@ private: vorbis_dsp_state vd; // central working state for the packet->PCM decoder vorbis_block vb; // local working space for packet->PCM decode + int textflag; int audioflag; int videoflag; private: @@ -130,6 +222,7 @@ private: ogg_page og; ogg_stream_state vo; ogg_stream_state to; + ogg_stream_state zo; th_info ti; th_comment tc; th_dec_ctx *td; @@ -142,6 +235,7 @@ private: int theora_p; int vorbis_p; + int irct_p; int stateflag; int lu_Y[256]; diff --git a/src/kvirc/kernel/kvi_userinput.cpp b/src/kvirc/kernel/kvi_userinput.cpp index 524c1d727..455ca73da 100644 --- a/src/kvirc/kernel/kvi_userinput.cpp +++ b/src/kvirc/kernel/kvi_userinput.cpp @@ -152,6 +152,7 @@ namespace KviUserInput pWindow->ownMessage(buf); break; case KVI_WINDOW_TYPE_DCCCHAT: + case KVI_WINDOW_TYPE_DCCVIDEO: pWindow->ownMessage(buf); break; default: diff --git a/src/kvirc/kvs/kvi_kvs_coresimplecommands_mr.cpp b/src/kvirc/kvs/kvi_kvs_coresimplecommands_mr.cpp index 1bf4906e4..07b4ab5f9 100644 --- a/src/kvirc/kvs/kvi_kvs_coresimplecommands_mr.cpp +++ b/src/kvirc/kvs/kvi_kvs_coresimplecommands_mr.cpp @@ -89,6 +89,7 @@ namespace KviKvsCoreSimpleCommands case KVI_WINDOW_TYPE_QUERY: KVSCSC_REQUIRE_CONNECTION case KVI_WINDOW_TYPE_DCCCHAT: + case KVI_WINDOW_TYPE_DCCVIDEO: KVSCSC_pWindow->ownAction(szText); break; default: diff --git a/src/kvirc/ui/kvi_window.h b/src/kvirc/ui/kvi_window.h index da5b68404..1ed5392da 100644 --- a/src/kvirc/ui/kvi_window.h +++ b/src/kvirc/ui/kvi_window.h @@ -103,8 +103,9 @@ class KviTalHBox; #define KVI_WINDOW_TYPE_SCRIPTOBJECT 21 #define KVI_WINDOW_TYPE_USERWINDOW 22 #define KVI_WINDOW_TYPE_DEBUG 23 +#define KVI_WINDOW_TYPE_DCCVIDEO 24 -#define KVI_WINDOW_NUM_TYPES 24 +#define KVI_WINDOW_NUM_TYPES 25 #define KVI_WINDOW_TYPE_USER 10000 diff --git a/src/modules/dcc/codec.cpp b/src/modules/dcc/codec.cpp index ffcbf330a..7e1433f1a 100644 --- a/src/modules/dcc/codec.cpp +++ b/src/modules/dcc/codec.cpp @@ -109,11 +109,11 @@ void KviDccVideoCodec::encodeVideo(KviDataBuffer *,KviDataBuffer *) { } -void KviDccVideoCodec::encodeAudio(KviDataBuffer *,KviDataBuffer *) +void KviDccVideoCodec::encodeText(KviDataBuffer *,KviDataBuffer *) { } -void KviDccVideoCodec::decode(KviDataBuffer *,KviDataBuffer *) +void KviDccVideoCodec::decode(KviDataBuffer *,KviDataBuffer *,KviDataBuffer *) { } @@ -162,40 +162,90 @@ void KviDccVideoSJpegCodec::encodeVideo(KviDataBuffer * videoSignal,KviDataBuffe videoSignal->clear(); } -void KviDccVideoSJpegCodec::encodeAudio(KviDataBuffer * audioSignal,KviDataBuffer * stream) +void KviDccVideoSJpegCodec::encodeText(KviDataBuffer * textSignal, KviDataBuffer * stream) { - //no audio support - Q_UNUSED(stream); - if(audioSignal->size() < 1) return; + static unsigned const char *irct_magic_init = (unsigned const char*) "\0<text>\0"; + static unsigned const char *irct_magic_end = (unsigned const char*) "\0</text>\0"; - audioSignal->clear(); + qDebug("encodeText %s %d",textSignal->data(), textSignal->size()); + if(textSignal->size() < 1) return; + stream->append(irct_magic_init,8); + stream->append((const unsigned char*)textSignal->data(),textSignal->size()); + stream->append(irct_magic_end,9); + textSignal->clear(); return; } -void KviDccVideoSJpegCodec::decode(KviDataBuffer * stream,KviDataBuffer * signal) +void KviDccVideoSJpegCodec::decode(KviDataBuffer * stream,KviDataBuffer * videoSignal,KviDataBuffer * textSignal) { static unsigned const char jpg_magic_init[4] = { 0xFF, 0xD8, 0xFF, 0xE0}; //SOI + APP0 static unsigned const char jpg_magic_end[2] = { 0xFF, 0xD9}; //EOI + static unsigned const char *irct_magic_init = (unsigned const char*) "\0<text>\0"; + static unsigned const char *irct_magic_end = (unsigned const char*) "\0</text>\0"; + if(stream->size() < 1)return; - int start = stream->find(jpg_magic_init, 4); - int end = stream->find(jpg_magic_end, 2); - if(start != -1 && end != -1) + + int txtStart = stream->find(irct_magic_init, 8); + int txtEnd = stream->find(irct_magic_end, 9); + + int jpgStart = stream->find(jpg_magic_init, 4); + + if(txtStart!=-1 && txtEnd !=-1 && txtStart<jpgStart) + { + qDebug("a txtStart %d txtEnd %d",txtStart,txtEnd); + stream->remove(txtStart+8); + int len = txtEnd - txtStart -8; //-8 irct_magic_init + if(len>0) + { + textSignal->append(stream->data(), len); + char*txt=(char*)malloc(len+1); + memcpy(txt,stream->data(), len); + txt[len]='\0'; + qDebug("a recv |%s| %d",txt,len); + } + stream->remove(len+9); + } + + jpgStart = stream->find(jpg_magic_init, 4); + int jpgEnd = stream->find(jpg_magic_end, 2); + + if(jpgStart != -1 && jpgEnd != -1) { +// qDebug("jpgStart %d jpgEnd %d",jpgStart,jpgEnd); QImage img; //remove junk before jpeg start - stream->remove(start); - int len = end - start + 1; + stream->remove(jpgStart); + int len = jpgEnd - jpgStart + 1; img.loadFromData(stream->data(), stream->size()); if(!img.isNull()) { - signal->clear(); - signal->append(img.bits(),img.numBytes()); + videoSignal->clear(); + videoSignal->append(img.bits(),img.numBytes()); } stream->remove(len); } + + txtStart = stream->find(irct_magic_init, 8); + txtEnd = stream->find(irct_magic_end, 9); + + if(txtStart!=-1 && txtEnd !=-1) + { + qDebug("b txtStart %d txtEnd %d",txtStart,txtEnd); + stream->remove(txtStart+8); + int len = txtEnd - txtStart -8; //-8 irct_magic_init + if(len>0) + { + textSignal->append(stream->data(), len); + char*txt=(char*)malloc(len+1); + memcpy(txt,stream->data(), len); + txt[len]='\0'; + qDebug("b recv |%s| %d",txt,len); + } + stream->remove(len+9); + } } int KviDccVideoSJpegCodec::encodedFrameSize() @@ -240,23 +290,23 @@ void KviDccVideoTheoraCodec::encodeVideo(KviDataBuffer * videoSignal,KviDataBuff videoSignal->clear(); } -void KviDccVideoTheoraCodec::encodeAudio(KviDataBuffer * audioSignal,KviDataBuffer * stream) +void KviDccVideoTheoraCodec::encodeText(KviDataBuffer * textSignal, KviDataBuffer * stream) { - if(audioSignal->size() < 1) return; + if(textSignal->size() < 1) return; if(!m_pEncoder) m_pEncoder = new KviTheoraEncoder(stream); - m_pEncoder->addAudioFrame(audioSignal->data(),audioSignal->size()); - audioSignal->clear(); + m_pEncoder->addTextFrame(textSignal->data(),textSignal->size()); + textSignal->clear(); } -void KviDccVideoTheoraCodec::decode(KviDataBuffer * stream,KviDataBuffer * signal) +void KviDccVideoTheoraCodec::decode(KviDataBuffer * stream,KviDataBuffer * videoSignal,KviDataBuffer * textSignal) { if(stream->size() < 1)return; if(!m_pDecoder) - m_pDecoder = new KviTheoraDecoder(signal); + m_pDecoder = new KviTheoraDecoder(videoSignal); m_pDecoder->addData(stream); } diff --git a/src/modules/dcc/codec.h b/src/modules/dcc/codec.h index da3c24d18..62d5161c3 100644 --- a/src/modules/dcc/codec.h +++ b/src/modules/dcc/codec.h @@ -65,8 +65,8 @@ protected: public: const char * name(); virtual void encodeVideo(KviDataBuffer * videoSignal, KviDataBuffer * stream); - virtual void encodeAudio(KviDataBuffer * audioSignal, KviDataBuffer * stream); - virtual void decode(KviDataBuffer * stream,KviDataBuffer * signal); + virtual void encodeText(KviDataBuffer * textSignal, KviDataBuffer * stream); + virtual void decode(KviDataBuffer * stream,KviDataBuffer * videoSignal,KviDataBuffer * textSignal); virtual int encodedFrameSize(); virtual int decodedFrameSize(); }; @@ -78,8 +78,8 @@ public: virtual ~KviDccVideoSJpegCodec(); public: virtual void encodeVideo(KviDataBuffer * videoSignal, KviDataBuffer * stream); - virtual void encodeAudio(KviDataBuffer * audioSignal, KviDataBuffer * stream); - virtual void decode(KviDataBuffer * stream,KviDataBuffer * signal); + virtual void encodeText(KviDataBuffer * textSignal, KviDataBuffer * stream); + virtual void decode(KviDataBuffer * stream,KviDataBuffer * videoSignal,KviDataBuffer * textSignal); virtual int encodedFrameSize(); virtual int decodedFrameSize(); }; @@ -92,8 +92,8 @@ public: virtual ~KviDccVideoTheoraCodec(); public: virtual void encodeVideo(KviDataBuffer * videoSignal, KviDataBuffer * stream); - virtual void encodeAudio(KviDataBuffer * audioSignal, KviDataBuffer * stream); - virtual void decode(KviDataBuffer * stream,KviDataBuffer * signal); + virtual void encodeText(KviDataBuffer * textSignal, KviDataBuffer * stream); + virtual void decode(KviDataBuffer * stream,KviDataBuffer * videoSignal,KviDataBuffer * textSignal); virtual int encodedFrameSize(); virtual int decodedFrameSize(); private: diff --git a/src/modules/dcc/libkvidcc.cpp b/src/modules/dcc/libkvidcc.cpp index 261f8ae75..5f252728c 100644 --- a/src/modules/dcc/libkvidcc.cpp +++ b/src/modules/dcc/libkvidcc.cpp @@ -1210,7 +1210,7 @@ static bool dcc_kvs_cmd_video(KviKvsModuleCommandCall * c) if(!kvi_dcc_video_is_valid_codec(szCodec.toUtf8().data())) { - c->warning(__tr2qs_ctx("Invalid codec specified, defaulting to 'sjpeg'","dcc")); + c->warning(__tr2qs_ctx("Invalid codec specified","dcc")); d->szCodec = "sjpeg"; } else { d->szCodec = szCodec; diff --git a/src/modules/dcc/video.cpp b/src/modules/dcc/video.cpp index db0d0a23f..fd1fa8782 100644 --- a/src/modules/dcc/video.cpp +++ b/src/modules/dcc/video.cpp @@ -22,13 +22,15 @@ // //============================================================================= +#include "kvi_settings.h" + #include "video.h" #include "marshal.h" #include "broker.h" -#include "kvi_settings.h" #include "kvi_iconmanager.h" #include "kvi_ircview.h" +#include "kvi_kvs_eventtriggers.h" #include "kvi_locale.h" #include "kvi_out.h" #include "kvi_error.h" @@ -39,10 +41,22 @@ #include "kvi_socket.h" #include "kvi_ircconnection.h" #include "kvi_tal_vbox.h" +#include "kvi_mirccntrl.h" +#include "kvi_frame.h" #include <QToolTip> #include <QByteArray> #include <QBuffer> +#include <QTextDocument> //for Qt::escape + +#ifdef COMPILE_CRYPT_SUPPORT + #include "kvi_crypt.h" + #include "kvi_cryptcontroller.h" +#endif + +#ifdef COMPILE_SSL_SUPPORT + #include "kvi_sslmaster.h" +#endif #include <sys/ioctl.h> @@ -53,36 +67,6 @@ extern KviDccBroker * g_pDccBroker; int g_iVideoDevicePoolInstances=0; #endif -//Check for the *SS Api....we don't want to fail here... - -#ifndef COMPILE_DISABLE_DCC_VOICE - #ifdef HAVE_LINUX_SOUNDCARD_H - #include <linux/soundcard.h> - #else - #ifdef HAVE_SYS_SOUNDCARD_H - #include <sys/soundcard.h> - #else - #ifdef HAVE_SOUNDCARD_H - #include <soundcard.h> - #else - //CAN NOT COMPILE :( - #define COMPILE_DISABLE_DCC_VOICE - #if !defined(COMPILE_ON_WINDOWS) && !defined(COMPILE_ON_MINGW) - #warning "Cannot find the soundcard.h header; you will NOT be able to use DCC Voice" - #endif - #endif - #endif - #endif -#endif - - -//#define KVI_AUDIO_DEVICE "/dev/dsp" -// 32 fragments , 512 bytes -#define KVI_SNDCTL_FRAG_SIZE 0x00B00009 -#define KVI_FRAGMENT_SIZE_IN_BYTES 512 -#define KVI_FORMAT AFMT_S16_LE -#define KVI_NUM_CHANNELS 1 - bool kvi_dcc_video_is_valid_codec(const char * codecName) { if(kvi_strEqualCI("sjpeg",codecName))return true; @@ -111,11 +95,6 @@ KviDccVideoThread::KviDccVideoThread(KviWindow * wnd,kvi_socket_t fd,KviDccVideo m_pInfoMutex = new KviMutex(); m_bRecordingRequestPending = false; -#ifndef COMPILE_DISABLE_DCC_VOICE - m_bSoundcardChecked = false; - m_soundFd = -1; - m_soundFdMode = 0; -#endif //local video input if(!g_pVideoDevicePool) { @@ -146,163 +125,6 @@ KviDccVideoThread::~KviDccVideoThread() #endif } - -bool KviDccVideoThread::checkSoundcard() -{ -#ifdef COMPILE_DISABLE_DCC_VOICE - return false; -#else - bool bOpened = false; - if(m_soundFd == -1) - { - if(!openSoundcard(O_RDONLY))return false; - bOpened = true; - } - - int caps; - - m_bSoundcardChecked = true; - - if(ioctl(m_soundFd,SNDCTL_DSP_GETCAPS,&caps) < 0) - { - postMessageEvent(__tr2qs_ctx("WARNING: failed to check the soundcard duplex capabilities: if this is a half-duplex soundcard, use the DCC VOICE option to force half-duplex algorithm","dcc").toUtf8().data()); - if(bOpened)closeSoundcard(); - return false; - } - - if(!(caps & DSP_CAP_DUPLEX)) - { - m_pOpt->bForceHalfDuplex = true; // the device is half duplex...use it in that way - postMessageEvent(__tr2qs_ctx("Half duplex soundcard detected, you will not be able to talk and listen at the same time","dcc").toUtf8().data()); - } - - if(bOpened)closeSoundcard(); - - return true; -#endif -} - - -bool KviDccVideoThread::openSoundcard(int mode) -{ -#ifdef COMPILE_DISABLE_DCC_VOICE - return false; -#else - int speed = m_pOpt->iSampleRate; - static int chans=KVI_NUM_CHANNELS; - static int fmt=KVI_FORMAT; - static int frag = KVI_SNDCTL_FRAG_SIZE; - - - if(m_soundFd != -1) - { - if(m_soundFdMode == mode)return true; // already open - closeSoundcard(); - } - - m_soundFd = ::open(m_pOpt->szSoundDevice.ptr(),mode | O_NONBLOCK); - if(m_soundFd < 0)return false; - - if(!m_pOpt->bForceHalfDuplex) - { - if(ioctl(m_soundFd,SNDCTL_DSP_SETDUPLEX,0) < 0)goto exit_false; - } - - if(ioctl(m_soundFd,SNDCTL_DSP_SETFRAGMENT,&frag)<0)goto exit_false; - if(ioctl(m_soundFd,SNDCTL_DSP_SETFMT,&fmt)<0)goto exit_false; - if(ioctl(m_soundFd,SNDCTL_DSP_CHANNELS,&chans)<0)goto exit_false; - if(ioctl(m_soundFd,SNDCTL_DSP_SPEED,&speed)<0)goto exit_false; - if(speed != m_pOpt->iSampleRate) - { - KviStr tmp(KviStr::Format,__tr2qs_ctx("WARNING: failed to set the requested sample rate (%d): the device used closest match (%d)","dcc").toUtf8().data(), - m_pOpt->iSampleRate,speed); - postMessageEvent(tmp.ptr()); - } - - // TODO: #warning "We could also support blocking operations mode" - - m_soundFdMode = mode; - - - return true; - -exit_false: - closeSoundcard(); - return false; -#endif -} - -bool KviDccVideoThread::openSoundcardForWriting() -{ -#ifndef COMPILE_DISABLE_DCC_VOICE - return openSoundcardWithDuplexOption(O_WRONLY,O_RDONLY); -#else - return false; -#endif -} - -bool KviDccVideoThread::openSoundcardForReading() -{ -#ifndef COMPILE_DISABLE_DCC_VOICE - return openSoundcardWithDuplexOption(O_RDONLY,O_WRONLY); -#else - return false; -#endif -} - -bool KviDccVideoThread::openSoundcardWithDuplexOption(int openMode,int failMode) -{ -#ifndef COMPILE_DISABLE_DCC_VOICE - if(m_soundFd == -1) - { - // soundcard not open yet...open for write (at least) - if(m_pOpt->bForceHalfDuplex) - { - // Forcing half duplex... (user option or the card does not support full duplex mode) - if(!openSoundcard(openMode))return false; - } else { - // Try read/write open - if(!openSoundcard(O_RDWR)) - { - // half-duplex sound card ? - if(!m_bSoundcardChecked) - { - // We haven't checked the full-duplex support yet... - // Try to open in RDONLY o WRONLY mode - if(!openSoundcard(openMode))return false; - if(!checkSoundcard()) - { - postMessageEvent(__tr2qs_ctx("Ops...failed to test the soundcard capabilities...expect problems...","dcc").toUtf8().data()); - } - } // else the test has been done and it is a full duplex card that is just busy - } - } - } else { - // Hmmm...already open - // If it is open in O_RDWR or O_WRONLY mode...it is ok for us - // but if it is open in O_RDONLY mode...we can do nothing...just wait - return (m_soundFdMode != failMode); - } - - return true; -#else - return false; -#endif -} - -void KviDccVideoThread::closeSoundcard() -{ -#ifndef COMPILE_DISABLE_DCC_VOICE - if(m_soundFd != -1) - { - ::close(m_soundFd); - m_soundFd = -1; - m_soundFdMode = 0; - } -#endif -} - - bool KviDccVideoThread::readWriteStep() { #ifndef COMPILE_DISABLE_DCC_VIDEO @@ -320,7 +142,7 @@ bool KviDccVideoThread::readWriteStep() if(readLen > 0) { if(readLen < 4096)m_inFrameBuffer.resize(actualSize + readLen); - m_pOpt->pCodec->decode(&m_inFrameBuffer,&m_inSignalBuffer); + m_pOpt->pCodec->decode(&m_inFrameBuffer,&m_videoInSignalBuffer,&m_textInSignalBuffer); //#warning "A maximum length for the signal buffer is actually needed!!!" } else { bCanRead=false; @@ -355,10 +177,10 @@ bool KviDccVideoThread::videoStep() // Are we playing ? if(m_bPlaying) { - if(m_inSignalBuffer.size() > 0) + if(m_videoInSignalBuffer.size() > 0) { - QImage img(m_inSignalBuffer.data(), 320, 240, 1280, QImage::Format_ARGB32); -// qDebug("IMG data %p size %d", m_inSignalBuffer.data(), m_inSignalBuffer.size()); + QImage img(m_videoInSignalBuffer.data(), 320, 240, 1280, QImage::Format_ARGB32); +// qDebug("IMG data %p size %d", m_videoInSignalBuffer.data(), m_videoInSignalBuffer.size()); if(!img.isNull()) m_inImage = img; } @@ -381,167 +203,100 @@ bool KviDccVideoThread::videoStep() return true; } -bool KviDccVideoThread::audioStep() +bool KviDccVideoThread::textStep() { #ifndef COMPILE_DISABLE_DCC_VIDEO -#ifndef COMPILE_DISABLE_DCC_VOICE // Are we playing ? -/* if(m_bPlaying) { - // Do we have something to write ? - audio_buf_info info; - if(m_inSignalBuffer.size() > 0) + if(m_textInSignalBuffer.size() > 0) { - // Get the number of fragments that can be written to the soundcard without blocking - - if(ioctl(m_soundFd,SNDCTL_DSP_GETOSPACE,&info) < 0) - { - debug("get o space failed"); - info.bytes = KVI_FRAGMENT_SIZE_IN_BYTES; // dummy... if this is not correct...well...we will block for 1024/16000 of a sec - info.fragments = 1; - info.fragsize = KVI_FRAGMENT_SIZE_IN_BYTES; - } - if(info.fragments > 0) - { - int toWrite = info.fragments * info.fragsize; - //debug("Can write %d bytes",toWrite); - if(m_inSignalBuffer.size() < toWrite)toWrite = m_inSignalBuffer.size(); - int written = write(m_soundFd,m_inSignalBuffer.data(),toWrite); - if(written > 0)m_inSignalBuffer.remove(written); - else { -//#warning "Do something for -1 here ?" -//#warning "Usleep ?" - } - } //else { - // No stuff can be written...we are running too fast ? - // m_uSleepTime += 100; // sleep for a while - //} - } else { - // hmmmm....playing , but nothing to write , possible underrun or EOF - // a nice idea would be to use SNDCTL_DSP_GETODELAY here... - // but it appears to be broken on some audio devices - if(ioctl(m_soundFd,SNDCTL_DSP_GETOSPACE,&info) < 0)info.fragstotal = info.fragments; // dummy...but what should we do ? - if(info.fragstotal == info.fragments) - { - // underrun or EOF: close the device - stopPlaying(); - } - } - } else { - // do we have anything to play ? - if(m_inSignalBuffer.size() > 0) - { - if(m_inSignalBuffer.size() >= m_pOpt->iPreBufferSize) - { - // yep...stuff to play... open the soundcard , if possible - startPlaying(); - - m_iLastSignalBufferSize = m_inSignalBuffer.size(); - } else { - // have stuff to play , but it's not enough to fill the pre-buffer - // - struct timeval tv; - gettimeofday(&tv,0); - - long int sigBufferTime = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); - - if(m_inSignalBuffer.size() == m_iLastSignalBufferSize) - { - // the same signal buffer size... check the time - // m_pOpt->iPreBufferSize / 16 gives us the preBufferTime in msecs - // we calc the remaining preBufferTime by subtracting the - // size of buffer already filled and we also add 50 milliseconds... smart heuristic - int preBufferTime = ((m_pOpt->iPreBufferSize - m_iLastSignalBufferSize) / 16) + 50; - // if the buffer size hasn't changed since preBufferTime - // it's time to start playing anyway, since there is - // either a network stall or it was just a really short data stream - if((sigBufferTime - m_iLastSignalBufferTime) > preBufferTime) - { - startPlaying(); - if(m_bPlaying)m_iLastSignalBufferSize = 0; - } - } else { - // signal buffer size differs...we have received new packets - // and still pre-buffering - m_iLastSignalBufferSize = m_inSignalBuffer.size(); - m_iLastSignalBufferTime = sigBufferTime; - } - } + KviDccThreadIncomingData data; + data.iLen = m_textInSignalBuffer.size(); + data.buffer = (char*) kvi_malloc(data.iLen); + memcpy(data.buffer, m_textInSignalBuffer.data(), data.iLen); + handleIncomingData(&data,false); + m_textInSignalBuffer.clear(); } } -*/ + // Are we recording ? if(m_bRecording) { - fd_set rs; - FD_ZERO(&rs); - FD_SET(m_soundFd,&rs); - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 10; - int ret = select(m_soundFd + 1,&rs,0,0,&tv); - if(ret > 0) + if(((KviDccVideo*)parent())->m_tmpTextDataOut.size()) { - // This is rather easy... - audio_buf_info info; - if(ioctl(m_soundFd,SNDCTL_DSP_GETISPACE,&info) < 0) - { - debug("Ispace failed"); - info.fragments = 0; // dummy... - info.bytes = 0; - } + m_textOutSignalBuffer.append((const unsigned char*) ((KviDccVideo*)parent())->m_tmpTextDataOut.constData(), ((KviDccVideo*)parent())->m_tmpTextDataOut.size()); + ((KviDccVideo*)parent())->m_tmpTextDataOut.clear(); + } - //debug("INFO: fragments: %d, fragstotal: %d, fragsize: %d, bytes: %d",info.fragments,info.fragstotal,info.fragsize,info.bytes); + if(m_textOutSignalBuffer.size()) + m_pOpt->pCodec->encodeText(&m_textOutSignalBuffer,&m_outFrameBuffer); + } - if(info.fragments == 0 && info.bytes == 0) - { - // force a dummy read: the device needs to be triggered - info.fragments = 1; - } +#endif // !COMPILE_DISABLE_DCC_VIDEO + return true; +} - if(info.fragments > 0) +bool KviDccVideoThread::handleIncomingData(KviDccThreadIncomingData * data,bool bCritical) +{ + __range_valid(data->iLen); + __range_valid(data->buffer); + char * aux = data->buffer; + char * end = data->buffer + data->iLen; + while(aux != end) + { + if((*aux == '\n') || (*aux == '\0')) + { + KviThreadDataEvent<KviStr> * e = new KviThreadDataEvent<KviStr>(KVI_DCC_THREAD_EVENT_DATA); + // The left part is len chars long + int len = aux - data->buffer; +// debug("LEN = %d, iLen = %d",len,data->iLen); +//#warning "DO IT BETTER (the \r cutting)" + KviStr * s = new KviStr(data->buffer,len); + if(s->lastCharIs('\r'))s->cutRight(1); + e->setData(s); + // but we cut also \n (or \0) + ++aux; + // so len += 1; --> new data->iLen -= len; + data->iLen -= (len + 1); +// debug("iLen now = %d",data->iLen); + __range_valid(data->iLen >= 0); + if(data->iLen > 0) { - int oldSize = m_audioOutSignalBuffer.size(); - int available = info.fragments * info.fragsize; - m_audioOutSignalBuffer.addSize(available); - int readed = read(m_soundFd,m_audioOutSignalBuffer.data() + oldSize,available); - - if(readed < available) - { - // huh ? ...error ? - if(readed >= 0)m_audioOutSignalBuffer.resize(oldSize + readed); - else { - if((errno == EINTR) || (errno == EAGAIN)) - { - m_audioOutSignalBuffer.resize(oldSize); - } else { -//#warning "Critical error...do something reasonable!" - m_audioOutSignalBuffer.resize(oldSize); - } - } - } -/* - debug("Signal buffer:"); - for(int i=0;i<200;i+=2) - { - if(i >= m_outSignalBuffer.size())break; - printf("%04x ",*(((unsigned short *)(m_outSignalBuffer.data() + i)))); - if((i % 6) == 0)printf("\n"); - } - debug("END\n"); -*/ - m_pOpt->pCodec->encodeAudio(&m_audioOutSignalBuffer,&m_outFrameBuffer); + // memmove the remaining part to the beginning + // aux points after \n or \0 + kvi_memmove(data->buffer,aux,data->iLen); + data->buffer = (char *)kvi_realloc(data->buffer,data->iLen); + end = data->buffer + data->iLen; + aux = data->buffer; + } else { + // no more data in the buffer + __range_valid(data->iLen == 0); + kvi_free(data->buffer); + data->buffer = end = aux = 0; } - }// else { - // Nothing to read - // m_uSleepTime += 100; - // } + postEvent(parent(),e); + } else aux++; +// debug("PASSING CHAR %c",*aux); + } + // now aux == end + if(bCritical) + { + // need to flush everything... + if(data->iLen > 0) + { + // in the last part there are no NULL and \n chars + KviThreadDataEvent<KviStr> * e = new KviThreadDataEvent<KviStr>(KVI_DCC_THREAD_EVENT_DATA); + KviStr * s = new KviStr(data->buffer,data->iLen); + if(s->lastCharIs('\r'))s->cutRight(1); + e->setData(s); + data->iLen = 0; + kvi_free(data->buffer); + data->buffer = 0; + postEvent(parent(),e); + } } - -#endif // !COMPILE_DISABLE_DCC_VOICE -#endif // !COMPILE_DISABLE_DCC_VIDEO return true; } @@ -550,10 +305,6 @@ void KviDccVideoThread::startRecording() #ifndef COMPILE_DISABLE_DCC_VIDEO //debug("Start recording"); if(m_bRecording)return; // already started - - //FIXME this is mandatory only for audio! - openSoundcardForReading(); - { // debug("Posting event"); @@ -581,8 +332,6 @@ void KviDccVideoThread::stopRecording() postEvent(KviDccThread::parent(),e); m_bRecording = false; - //FIXME this is mandatory only if we are using audio - if(!m_bPlaying)closeSoundcard(); #endif } @@ -591,15 +340,12 @@ void KviDccVideoThread::startPlaying() #ifndef COMPILE_DISABLE_DCC_VIDEO //debug("Start playing"); if(m_bPlaying)return; - //FIXME this is mandatory only if we are using audio - openSoundcardForWriting(); - //if() - { - KviThreadDataEvent<int> * e = new KviThreadDataEvent<int>(KVI_DCC_THREAD_EVENT_ACTION); - e->setData(new int(KVI_DCC_VIDEO_THREAD_ACTION_START_PLAYING)); - postEvent(KviDccThread::parent(),e); - m_bPlaying = true; - } + + KviThreadDataEvent<int> * e = new KviThreadDataEvent<int>(KVI_DCC_THREAD_EVENT_ACTION); + e->setData(new int(KVI_DCC_VIDEO_THREAD_ACTION_START_PLAYING)); + postEvent(KviDccThread::parent(),e); + m_bPlaying = true; + #endif } @@ -612,10 +358,7 @@ void KviDccVideoThread::stopPlaying() KviThreadDataEvent<int> * e = new KviThreadDataEvent<int>(KVI_DCC_THREAD_EVENT_ACTION); e->setData(new int(KVI_DCC_VIDEO_THREAD_ACTION_STOP_PLAYING)); postEvent(KviDccThread::parent(),e); - m_bPlaying = false; - //FIXME this is mandatory only if we are using audio - if(!m_bRecording)closeSoundcard(); #endif } @@ -647,14 +390,14 @@ void KviDccVideoThread::run() } if(!readWriteStep())goto exit_dcc; - //if(!videoStep() || !audioStep())goto exit_dcc; if(!videoStep())goto exit_dcc; - + if(!textStep())goto exit_dcc; + m_pInfoMutex->lock(); m_pInfoMutex->unlock(); //if(m_uSleepTime)usleep(m_uSleepTime); -// qDebug("in %d out %d in_sig %d out_sig %d", m_inFrameBuffer.size(), m_outFrameBuffer.size(), m_inSignalBuffer.size(), m_videoOutSignalBuffer.size()); +// qDebug("in %d out %d in_sig %d out_sig %d", m_inFrameBuffer.size(), m_outFrameBuffer.size(), m_videoInSignalBuffer.size(), m_videoOutSignalBuffer.size()); usleep(200); // Start recording if the request was not fulfilled yet @@ -670,13 +413,31 @@ exit_dcc: } KviDccVideo::KviDccVideo(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name) -: KviDccWindow(KVI_WINDOW_TYPE_DCCVOICE,pFrm,name,dcc) +: KviDccWindow(KVI_WINDOW_TYPE_DCCVIDEO,pFrm,name,dcc) { m_pDescriptor = dcc; m_pSlaveThread = 0; m_pszTarget = 0; - m_pLayout = new QGridLayout(); + m_pLayout = new QGridLayout(this); + + m_pButtonBox = new KviTalHBox(this); + + m_pLabel = new KviThemedLabel(m_pButtonBox, this, "dcc_chat_label"); + m_pLabel->setText(name); + m_pButtonBox->setStretchFactor(m_pLabel,1); + + m_pButtonContainer= new KviTalHBox(m_pButtonBox); + createTextEncodingButton(m_pButtonContainer); + +#ifdef COMPILE_CRYPT_SUPPORT + createCryptControllerButton(m_pButtonContainer); +#endif + m_pLayout->addWidget(m_pButtonBox, 0, 0, 1, 3); + + m_pIrcView = new KviIrcView(this,pFrm,this); + connect(m_pIrcView,SIGNAL(rightClicked()),this,SLOT(textViewRightClicked())); + m_pInput = new KviInput(this); //remote video m_pInVideoLabel = new QLabel(); @@ -685,7 +446,7 @@ KviDccVideo::KviDccVideo(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name m_pInVideoLabel->setFrameShape(QFrame::Box); m_pInVideoLabel->setScaledContents(true); m_pInVideoLabel->setAlignment(Qt::AlignCenter); - m_pLayout->addWidget(m_pInVideoLabel, 0, 0, 10, 1); + m_pLayout->addWidget(m_pInVideoLabel, 1, 0, 6, 1); //local video m_pOutVideoLabel = new QLabel(); @@ -694,32 +455,38 @@ KviDccVideo::KviDccVideo(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name m_pOutVideoLabel->setFrameShape(QFrame::Box); m_pOutVideoLabel->setScaledContents(true); m_pOutVideoLabel->setAlignment(Qt::AlignCenter); - m_pLayout->addWidget(m_pOutVideoLabel, 11, 0, 10, 1); + m_pLayout->addWidget(m_pOutVideoLabel, 1, 1, 6, 1); //local video input: config - m_pLabel[0] = new QLabel(); - m_pLabel[0]->setText(__tr2qs_ctx("Video device:","dcc")); - m_pLayout->addWidget(m_pLabel[0], 11, 1, 1, 1); + m_pVideoLabel[0] = new QLabel(); + m_pVideoLabel[0]->setText(__tr2qs_ctx("Video device:","dcc")); + m_pLayout->addWidget(m_pVideoLabel[0], 1, 2, 1, 1); m_pCDevices = new QComboBox(); - m_pLayout->addWidget(m_pCDevices, 12, 1, 1, 1); + m_pLayout->addWidget(m_pCDevices, 2, 2, 1, 1); - m_pLabel[1] = new QLabel(); - m_pLabel[1]->setText(__tr2qs_ctx("Input:","dcc")); - m_pLayout->addWidget(m_pLabel[1], 13, 1, 1, 1); + m_pVideoLabel[1] = new QLabel(); + m_pVideoLabel[1]->setText(__tr2qs_ctx("Input:","dcc")); + m_pLayout->addWidget(m_pVideoLabel[1], 3, 2, 1, 1); m_pCInputs = new QComboBox(); - m_pLayout->addWidget(m_pCInputs, 14, 1, 1, 1); + m_pLayout->addWidget(m_pCInputs, 4, 2, 1, 1); - m_pLabel[2] = new QLabel(); - m_pLabel[2]->setText(__tr2qs_ctx("Standard:","dcc")); - m_pLayout->addWidget(m_pLabel[2], 15, 1, 1, 1); + m_pVideoLabel[2] = new QLabel(); + m_pVideoLabel[2]->setText(__tr2qs_ctx("Standard:","dcc")); + m_pLayout->addWidget(m_pVideoLabel[2], 5, 2, 1, 1); m_pCStandards = new QComboBox(); - m_pLayout->addWidget(m_pCStandards, 16, 1, 1, 1); + m_pLayout->addWidget(m_pCStandards, 6, 2, 1, 1); + + m_pLayout->addWidget(m_pIrcView, 7, 0, 1, 3); + m_pLayout->addWidget(m_pInput, 8, 0, 1, 3); + m_pLayout->setRowStretch(7,1); setLayout(m_pLayout); + if(KVI_OPTION_BOOL(KviOption_boolAutoLogDccChat))m_pIrcView->startLogging(); + connect(&m_Timer, SIGNAL(timeout()), this, SLOT(slotUpdateImage()) ); m_Timer.start(200); //5fps @@ -759,14 +526,14 @@ KviDccVideo::~KviDccVideo() delete m_pCStandards; m_pCStandards=0; } - if(m_pLabel[0]) + if(m_pVideoLabel[0]) { - delete m_pLabel[2]; - delete m_pLabel[1]; - delete m_pLabel[0]; - m_pLabel[2] = 0; - m_pLabel[1] = 0; - m_pLabel[0] = 0; + delete m_pVideoLabel[2]; + delete m_pVideoLabel[1]; + delete m_pVideoLabel[0]; + m_pVideoLabel[2] = 0; + m_pVideoLabel[1] = 0; + m_pVideoLabel[0] = 0; } if(m_pLayout) { @@ -792,6 +559,21 @@ KviDccVideo::~KviDccVideo() } } +void KviDccVideo::textViewRightClicked() +{ + KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatPopupRequest,this,m_pDescriptor->idString()); +} + +void KviDccVideo::triggerCreationEvents() +{ + KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatWindowCreated,this,m_pDescriptor->idString()); +} + +void KviDccVideo::triggerDestructionEvents() +{ + KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatWindowClosing,this,m_pDescriptor->idString()); +} + void KviDccVideo::startConnection() { if(!(m_pDescriptor->bActive)) @@ -867,6 +649,109 @@ QPixmap * KviDccVideo::myIconPtr() return g_pIconManager->getSmallIcon(KVI_SMALLICON_DCCVOICE); } +void KviDccVideo::ownMessage(const QString &text) +{ + if(!m_pSlaveThread) + { + output(KVI_OUT_SYSTEMWARNING,__tr2qs_ctx("Cannot send data: No active connection","dcc")); + return; + } + + QByteArray szData = encodeText(text); + const char * d = szData.data(); + if(!d)return; + +#ifdef COMPILE_CRYPT_SUPPORT + if(cryptSessionInfo()) + { + if(cryptSessionInfo()->bDoEncrypt) + { + if(*d != KVI_TEXT_CRYPTESCAPE) + { + KviStr encrypted; + cryptSessionInfo()->pEngine->setMaxEncryptLen(-1); + switch(cryptSessionInfo()->pEngine->encrypt(d,encrypted)) + { + case KviCryptEngine::Encrypted: + { + KviStr buf(KviStr::Format,"%s\r\n",encrypted.ptr()); + m_tmpTextDataOut.append(buf.ptr(), buf.len()); + m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSGCRYPTED, + m_pDescriptor->szLocalNick.toUtf8().data(),m_pDescriptor->szLocalUser.toUtf8().data(), + m_pDescriptor->szLocalHost.toUtf8().data(),text,KviConsole::NoNotifications); + } + break; + case KviCryptEngine::Encoded: + { + KviStr buf(KviStr::Format,"%s\r\n",encrypted.ptr()); + m_tmpTextDataOut.append(buf.ptr(), buf.len()); + QString encr = decodeText(encrypted.ptr()); + m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSG, + m_pDescriptor->szLocalNick.toUtf8().data(),m_pDescriptor->szLocalUser.toUtf8().data(), + m_pDescriptor->szLocalHost.toUtf8().data(),encr,KviConsole::NoNotifications); + } + break; + default: // also case KviCryptEngine::EncryptError + { + QString szErr = cryptSessionInfo()->pEngine->lastError(); + output(KVI_OUT_SYSTEMERROR, + __tr2qs_ctx("The crypto engine was not able to encrypt the current message (%Q): %Q, no data was sent to the remote end","dcc"), + &text,&szErr); + } + break; + } + return; + } else { + d++; //eat the escape code + KviStr buf(KviStr::Format,"%s\r\n",d); + QString tmp = text.right(text.length() - 1); + m_tmpTextDataOut.append(buf.ptr(), buf.len()); + m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSG, + m_pDescriptor->szLocalNick.toUtf8().data(),m_pDescriptor->szLocalUser.toUtf8().data(), + m_pDescriptor->szLocalHost.toUtf8().data(),tmp,KviConsole::NoNotifications); + return; + } + } + } +#endif + KviStr buf(KviStr::Format,"%s\r\n",d); + m_tmpTextDataOut.append(buf.ptr(), buf.len()); + m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSG, + m_pDescriptor->szLocalNick.toUtf8().data(),m_pDescriptor->szLocalUser.toUtf8().data(), + m_pDescriptor->szLocalHost.toUtf8().data(),text,KviConsole::NoNotifications); +} + +const QString & KviDccVideo::localNick() +{ + // FIXME: This is just a complete HACK + m_szLocalNick = m_pDescriptor->szLocalNick; + return m_szLocalNick; +} + +void KviDccVideo::ownAction(const QString &text) +{ + if(m_pSlaveThread) + { + QString szTmpBuffer; + //see bug ticket #220 + if(KVI_OPTION_BOOL(KviOption_boolStripMircColorsInUserMessages)) + { + szTmpBuffer = KviMircCntrl::stripControlBytes(text); + } else { + szTmpBuffer = text; + } + + QByteArray szData = encodeText(szTmpBuffer); + const char * d = szData.data(); + if(!d)return; + KviStr buf(KviStr::Format,"%cACTION %s%c\r\n",0x01,d,0x01); + m_tmpTextDataOut.append(buf.ptr(), buf.len()); + output(KVI_OUT_ACTION,"%Q %Q",&(m_pDescriptor->szLocalNick),&szTmpBuffer); + } else { + output(KVI_OUT_SYSTEMWARNING,__tr2qs_ctx("Cannot send data: No active connection","dcc")); + } +} + bool KviDccVideo::event(QEvent *e) { if(e->type() == KVI_THREAD_EVENT) @@ -879,12 +764,98 @@ bool KviDccVideo::event(QEvent *e) QString ssss = KviError::getDescription(*err); output(KVI_OUT_DCCERROR,__tr2qs_ctx("ERROR: %Q","dcc"),&(ssss)); delete err; -/* m_pTalkButton->setEnabled(false); - m_pRecordingLabel->setEnabled(false); - m_pPlayingLabel->setEnabled(false);*/ return true; } break; + case KVI_DCC_THREAD_EVENT_DATA: + { + KviStr * encoded = ((KviThreadDataEvent<KviStr> *)e)->getData(); + KviStr d=KviStr(decodeText(encoded->ptr())); + if(d.firstCharIs(0x01)) + { + d.cutLeft(1); + if(d.lastCharIs(0x01))d.cutRight(1); + if(kvi_strEqualCIN("ACTION",d.ptr(),6))d.cutLeft(6); + d.stripLeftWhiteSpace(); + output(KVI_OUT_ACTION,"%Q %s",&(m_pDescriptor->szNick),d.ptr()); + if(!hasAttention()) + { + if(KVI_OPTION_BOOL(KviOption_boolFlashDccChatWindowOnNewMessages)) + { + demandAttention(); + } + if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewDccChatMessages)) + { + QString szMsg = "<b>"; + szMsg += m_pDescriptor->szNick; + szMsg += "</b> "; + szMsg += Qt::escape(QString(d.ptr())); + //debug("kvi_sp_ctcp.cpp:975 debug: %s",szMsg.data()); + g_pApp->notifierMessage(this,KVI_OPTION_MSGTYPE(KVI_OUT_ACTION).pixId(),szMsg,KVI_OPTION_UINT(KviOption_uintNotifierAutoHideTime)); + } + } + } else { + +#ifdef COMPILE_CRYPT_SUPPORT + if(KviCryptSessionInfo * cinf = cryptSessionInfo()) + { + if(cinf->bDoDecrypt) + { + KviStr decryptedStuff; + switch(cinf->pEngine->decrypt(d.ptr(),decryptedStuff)) + { + case KviCryptEngine::DecryptOkWasEncrypted: + case KviCryptEngine::DecryptOkWasEncoded: + case KviCryptEngine::DecryptOkWasPlainText: + if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatMessage,this,QString(decryptedStuff.ptr()),m_pDescriptor->idString())) + { + m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_DCCCHATMSG, + m_pDescriptor->szNick.toUtf8().data(),m_pDescriptor->szUser.toUtf8().data(), + m_pDescriptor->szHost.toUtf8().data(),decryptedStuff.ptr()); + } + delete encoded; + return true; + break; + + default: // also case KviCryptEngine::DecryptError + { + QString szErr = cinf->pEngine->lastError(); + output(KVI_OUT_SYSTEMERROR, + __tr2qs_ctx("The following message appears to be encrypted, but the crypto engine failed to decode it: %Q","dcc"), + &szErr); + } + break; + } + } + } else { +#endif + // FIXME! + if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatMessage,this,QString(d.ptr()),m_pDescriptor->idString())) + { + m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_DCCCHATMSG, + m_pDescriptor->szNick.toUtf8().data(),m_pDescriptor->szUser.toUtf8().data(), + m_pDescriptor->szHost.toUtf8().data(),d.ptr()); + if(!hasAttention()) + { + if(KVI_OPTION_BOOL(KviOption_boolFlashDccChatWindowOnNewMessages)) + { + demandAttention(); + } + if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewDccChatMessages)) + { + QString szMsg = Qt::escape(QString(d.ptr())); + g_pApp->notifierMessage(this,KVI_SMALLICON_DCCCHATMSG,szMsg,KVI_OPTION_UINT(KviOption_uintNotifierAutoHideTime)); + } + } + } +#ifdef COMPILE_CRYPT_SUPPORT + } +#endif + } + delete encoded; + return true; + } + case KVI_DCC_THREAD_EVENT_MESSAGE: { KviStr * str = ((KviThreadDataEvent<KviStr> *)e)->getData(); @@ -929,10 +900,6 @@ void KviDccVideo::handleMarshalError(int err) { QString ssss = KviError::getDescription(err); output(KVI_OUT_DCCERROR,__tr2qs_ctx("DCC Failed: %Q","dcc"),&ssss); -/* m_pTalkButton->setEnabled(false); - m_pTalkButton->setChecked(false); - m_pRecordingLabel->setEnabled(false); - m_pPlayingLabel->setEnabled(false);*/ } void KviDccVideo::connected() @@ -952,10 +919,6 @@ void KviDccVideo::connected() KviDccVideoThreadOptions * opt = new KviDccVideoThreadOptions; opt->pCodec = kvi_dcc_video_get_codec(m_pDescriptor->szCodec.ptr()); - opt->bForceHalfDuplex = KVI_OPTION_BOOL(KviOption_boolDccVoiceForceHalfDuplex); - opt->iPreBufferSize = KVI_OPTION_UINT(KviOption_uintDccVoicePreBufferSize); - opt->szSoundDevice = KVI_OPTION_STRING(KviOption_stringDccVoiceSoundDevice).toUtf8().data(); - opt->iSampleRate = 8000; output(KVI_OUT_DCCMSG,__tr2qs_ctx("Actual codec used is '%s'","dcc"),opt->pCodec->name()); @@ -974,8 +937,6 @@ void KviDccVideo::connected() #endif m_pSlaveThread->start(); - -// m_pTalkButton->setEnabled(true); } void KviDccVideo::stopTalking() @@ -1011,7 +972,7 @@ void KviDccVideo::slotUpdateImage() void KviDccVideo::deviceRegistered(const QString &) { - /* + #ifndef COMPILE_DISABLE_DCC_VIDEO g_pVideoDevicePool->fillDeviceQComboBox(m_pCDevices); g_pVideoDevicePool->fillInputQComboBox(m_pCInputs); @@ -1029,19 +990,16 @@ void KviDccVideo::deviceRegistered(const QString &) // m_Timer.start(200); //5fps // } #endif -#*/ } void KviDccVideo::deviceUnregistered(const QString & ) { -/* #ifndef COMPILE_DISABLE_DCC_VIDEO g_pVideoDevicePool->fillDeviceQComboBox(m_pCDevices); g_pVideoDevicePool->fillInputQComboBox(m_pCInputs); g_pVideoDevicePool->fillStandardQComboBox(m_pCStandards); #endif -*/ } #ifndef COMPILE_USE_STANDALONE_MOC_SOURCES diff --git a/src/modules/dcc/video.h b/src/modules/dcc/video.h index 02f93cd77..628c319d1 100644 --- a/src/modules/dcc/video.h +++ b/src/modules/dcc/video.h @@ -33,6 +33,7 @@ #include "kvi_databuffer.h" #include "kvi_sockettype.h" #include "kvi_tal_hbox.h" +#include "kvi_themedlabel.h" #include <QLabel> #include <QToolButton> @@ -62,10 +63,6 @@ typedef struct _KviDccVideoThreadOptions { QString szVideoDevice; KviDccVideoCodec * pCodec; - bool bForceHalfDuplex; - int iPreBufferSize; - int iSampleRate; - KviStr szSoundDevice; } KviDccVideoThreadOptions; class KviDccVideoThread : public KviDccThread @@ -78,9 +75,10 @@ protected: KviDccVideoThreadOptions * m_pOpt; KviDataBuffer m_outFrameBuffer; KviDataBuffer m_inFrameBuffer; - KviDataBuffer m_inSignalBuffer; + KviDataBuffer m_videoInSignalBuffer; + KviDataBuffer m_textInSignalBuffer; KviDataBuffer m_videoOutSignalBuffer; - KviDataBuffer m_audioOutSignalBuffer; + KviDataBuffer m_textOutSignalBuffer; bool m_bPlaying; bool m_bRecording; bool m_bRecordingRequestPending; @@ -89,30 +87,20 @@ protected: // stuff protected by the mutex: int m_iInputBufferSize; int m_iOutputBufferSize; - //audio - int m_soundFd; - int m_soundFdMode; - bool m_bSoundcardChecked; protected: QImage m_inImage; QImage m_outImage; protected: bool readWriteStep(); bool videoStep(); - bool audioStep(); + bool textStep(); void startRecording(); void stopRecording(); void startPlaying(); void stopPlaying(); inline bool isPlaying() { return m_bPlaying; }; virtual void run(); - //audio - bool checkSoundcard(); - bool openSoundcardWithDuplexOption(int openMode,int failMode); - bool openSoundcard(int mode); - bool openSoundcardForWriting(); - bool openSoundcardForReading(); - void closeSoundcard(); + virtual bool handleIncomingData(KviDccThreadIncomingData *data,bool bCritical); }; @@ -124,6 +112,10 @@ public: KviDccVideo(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name); ~KviDccVideo(); protected: + KviThemedLabel * m_pLabel; + KviTalHBox * m_pButtonBox; + KviTalHBox * m_pButtonContainer; + QLabel *m_pInVideoLabel; QLabel *m_pOutVideoLabel; QComboBox *m_pCDevices; @@ -131,35 +123,34 @@ protected: QComboBox *m_pCStandards; QGridLayout *m_pLayout; QTimer m_Timer; - QLabel * m_pLabel[2]; + QLabel * m_pVideoLabel[2]; QString * m_pszTarget; KviDccVideoThread * m_pSlaveThread; + QByteArray m_tmpTextDataOut; + QString m_szLocalNick; protected: -// virtual void focusInEvent(QFocusEvent *); + virtual void triggerCreationEvents(); + virtual void triggerDestructionEvents(); virtual const QString & target(); virtual void fillCaptionBuffers(); virtual QPixmap * myIconPtr(); -/* - virtual void resizeEvent(QResizeEvent *e); - virtual QSize sizeHint() const; -*/ virtual bool event(QEvent *e); virtual void getBaseLogFileName(QString &buffer); void startTalking(); void stopTalking(); void startConnection(); -// int getMixerVolume(void) const; + virtual const QString & localNick(); + virtual void ownMessage(const QString &text); + virtual void ownAction(const QString &text); protected slots: void handleMarshalError(int err); void connected(); void startOrStopTalking(bool bStart); -/* - void setMixerVolume(int); -*/ void connectionInProgress(); void slotUpdateImage(); void deviceRegistered(const QString &); void deviceUnregistered(const QString &); + void textViewRightClicked(); }; |
