2 Copyright (c) 2005 by Olivier Goffart <ogoffart@kde.org>
4 Kopete (c) 2002-2007 by the Kopete developers <kopete-devel@kde.org>
6 *************************************************************************
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
13 *************************************************************************
26 #include <QTimerEvent>
29 #include <kbufferedsocket.h>
31 #include <kserversocket.h>
32 #include <kmessagebox.h>
37 #include <qdatetime.h>
39 #include "dispatcher.h"
41 #include "mimicwrapper.h"
42 #include "msnwebcamdialog.h"
45 #include "avdevice/videodevicepool.h"
47 using namespace KNetwork
;
51 Webcam::Webcam(Who who
, const QString
& to
, Dispatcher
*parent
, quint32 sessionId
)
52 : TransferContext(to
,parent
,sessionId
) , m_who(who
) , m_timerId(0)
54 setType(P2P::WebcamType
);
55 m_direction
= Incoming
;
58 m_webcamState
=wsNegotiating
;
63 KSharedConfig::Ptr config
= KGlobal::config();
64 config
->setGroup( "MSN" );
66 // Read the configuration to get the number of frame per second to send
67 int webCamFps
=config
->readNumEntry("WebcamFPS", 25);
68 m_timerFps
= 1000 / webCamFps
;
73 kDebug(14140) << "################################################";
76 delete m_webcamSocket
;
79 if(m_timerId
!= 0) //if we were sending
81 Kopete::AV::VideoDevicePool
*videoDevice
= Kopete::AV::VideoDevicePool::self();
82 videoDevice
->stopCapturing();
88 void Webcam::askIncommingInvitation()
90 m_direction
= Incoming
;
91 //protect, in case this is deleted when the messagebox is active
92 QPointer
<Webcam
> _this
= this;
93 QString message
= (m_who
==wProducer
) ?
94 i18n("<qt>The contact %1 wants to see <b>your</b> webcam, do you want them to see it?</qt>", m_recipient
) :
95 i18n("The contact %1 wants to show you his/her webcam, do you want to see it?", m_recipient
) ;
96 int result
=KMessageBox::questionYesNo( 0L , message
,
97 i18n("Webcam invitation - Kopete MSN Plugin") , i18n("Accept") , i18n("Decline"));
101 QString content
= QString("SessionID: %1\r\n\r\n").arg(m_sessionId
);
102 if(result
==KMessageBox::Yes
)
104 //Send two message, an OK, and an invite.
105 //Normaly, the user should decline the invite (i hope)
107 // Send a 200 OK message to the recipient.
108 sendMessage(OK
, content
);
111 //send an INVITE message we want the user decline
112 //need to change the branch of the second message
113 m_branch
=Uid::createUid();
114 m_state
= Negotiation
; //set type to application/x-msnmsgr-transreqbody
116 content
=QString("Bridges: TRUDPv1 TCPv1\r\n"
117 "NetID: -1280904111\r\n"
118 "Conn-Type: Firewall\r\n"
120 "ICF: false\r\n\r\n");
122 sendMessage(INVITE
, content
);
127 //Decline the invitation
128 sendMessage(DECLINE
, content
);
133 void Webcam::sendBYEMessage()
136 QString content
="Context: dAMAgQ==\r\n";
137 sendMessage(BYE
,content
);
139 //If ever the opposite client was dead or something, we'll ack anyway, so everything get cleaned
140 QTimer::singleShot(60*1000 , this, SLOT(acknowledged()));
145 void Webcam::acknowledged()
153 // m_state=Negotiation;
160 if(m_type == UserDisplayIcon)
162 <<< Data preparation acknowledge message.
163 m_state = DataTransfer;
172 NOTE <<< Data acknowledged message.
173 <<< Bye message should follow.
176 if(m_handshake == 0x01)
178 Data handshake acknowledge message.
182 else if(m_handshake == 0x02)
184 Data acknowledge message.
185 Send the recipient a BYE message.
187 sendMessage(BYE, "\r\n");
194 //BYE or DECLINE acknowledge message.
195 m_dispatcher
->detach(this);
205 void Webcam::processMessage(const Message
& message
)
207 if(message
.header
.dataOffset
+message
.header
.dataSize
>= message
.header
.totalDataSize
)
208 acknowledge( message
); //aknowledge if needed
210 if(message
.applicationIdentifier
!= 4l)
212 QString body
= QByteArray(message
.body
.data(), message
.header
.dataSize
);
213 kDebug(14141) << "received, " << body
;
215 if(body
.startsWith("MSNSLP/1.0 200 OK"))
217 m_direction
= Outgoing
;
219 if(body
.startsWith("INVITE"))
221 if(m_direction
== Outgoing
)
223 QRegExp
regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n");
225 m_branch
=regex
.cap(1);
227 sendMessage(DECLINE
);
228 makeSIPMessage("syn",0x17,0x2a,0x01);
231 else if(body
.startsWith("MSNSLP/1.0 603 DECLINE"))
233 //if it is the declinaison of the second invite message, we have to don't care
234 //TODO anyway, if it's the declinaison of our invitation, we have to something
236 else if(body
.startsWith("BYE"))
240 // Dispose of this transfer context.
241 m_dispatcher
->detach(this);
248 //Let's take the fun, we entering into the delicious webcam negotiation binary protocol
250 //well, there is maybe better to take utf16, but it's ascii, so no problem.
251 QByteArray dataMessage
=message
.body
;
256 while(f
<dataMessage
.size())
259 for(unsigned int q
=0; q
<16 ; q
++)
261 if(q
+f
<dataMessage
.size())
263 unsigned int N
=(unsigned int) (dataMessage
[q
+f
]);
266 echoS
+=QString::number( N
,16)+' ';
273 for(unsigned int q
=0; (q
<16 && (q
+f
)<dataMessage
.size()) ; q
++)
275 unsigned char X
=dataMessage
[q
+f
];
276 char C
=((char)(( X
<128 && X
>31 ) ? X
: '.'));
277 echoS
+=QString::fromLatin1(&C
,1);
281 kDebug(14141) << dataMessage
.size() << echoS
;
288 for(uint pos
=m_content
.isNull() ? 10 : 0; pos
<dataMessage
.size(); pos
+=2)
291 m_content
+=dataMessage
[pos
];
294 if(message
.header
.dataOffset
+message
.header
.dataSize
< message
.header
.totalDataSize
)
297 kDebug(14141) << "Message contents: " << m_content
<< '\n';
298 if(m_content
.startsWith("syn"))
300 if(m_direction
== Incoming
)
301 makeSIPMessage("syn",0x17,0x2a,0x01);
303 makeSIPMessage("ack",0xea,0x00,0x00);
305 else if(m_content
.startsWith("ack"))
307 if(m_direction
== Incoming
)
308 makeSIPMessage("ack",0xea,0x00,0x00);
312 uint sess
=rand()%1000+5000;
313 uint rid
=rand()%100+50;
314 m_myAuth
=QString("recipientid=%1&sessionid=%2\r\n\r\n").arg(rid
).arg(sess
);
315 kDebug(14140) << "m_myAuth= " << m_myAuth
;
316 QString producerxml
=xml(sess
, rid
);
317 kDebug(14140) << "producerxml= " << producerxml
;
318 makeSIPMessage(producerxml
);
321 else if(m_content
.contains("<producer>") || m_content
.contains("<viewer>"))
323 QRegExp
rx("<rid>([0-9]*)</rid>.*<session>([0-9]*)</session>");
324 rx
.search(m_content
);
325 QString rid
=rx
.cap(1);
326 QString sess
=rx
.cap(2);
327 if(m_content
.contains("<producer>"))
330 QString viewerxml
=xml(sess
.toUInt() , rid
.toUInt());
331 kDebug(14140) << "vewerxml= " << viewerxml
;
332 makeSIPMessage( viewerxml
,0x00,0x09,0x00 );
333 m_peerAuth
=m_myAuth
=QString("recipientid=%1&sessionid=%2\r\n\r\n").arg(rid
,sess
);
334 kDebug(14140) << "m_auth= " << m_myAuth
;
338 m_peerAuth
=QString("recipientid=%1&sessionid=%2\r\n\r\n").arg(rid
,sess
);
340 makeSIPMessage("receivedViewerData", 0xec , 0xda , 0x03);
343 m_listener
= new KServerSocket("7786",this);
344 //m_listener->setResolutionEnabled(true);
345 // Create the callback that will try to accept incoming connections.
346 QObject::connect(m_listener
, SIGNAL(readyAccept()), this, SLOT(slotAccept()));
347 QObject::connect(m_listener
, SIGNAL(gotError(int)), this, SLOT(slotListenError(int)));
348 // Listen for incoming connections.
349 bool isListening
= m_listener
->listen();
350 kDebug(14140) << (isListening
? QString("listening %1").arg(m_listener
->localAddress().toString()) : QString("not listening"));
352 rx
=QRegExp("<tcpport>([^<]*)</tcpport>");
353 rx
.search(m_content
);
354 QString port1
=rx
.cap(1);
358 rx
=QRegExp("<tcplocalport>([^<]*)</tcplocalport>");
359 rx
.search(m_content
);
360 QString port2
=rx
.cap(1);
361 if(port2
==port1
|| port2
=="0")
364 rx
=QRegExp("<tcpexternalport>([^<]*)</tcpexternalport>");
365 rx
.search(m_content
);
366 QString port3
=rx
.cap(1);
367 if(port3
==port1
|| port3
==port2
|| port3
=="0")
374 if(!m_content
.contains( QString("<tcpipaddress%1>").arg(an
) ))
376 rx
=QRegExp(QString("<tcpipaddress%1>([^<]*)</tcpipaddress%2>").arg(an
).arg(an
));
377 rx
.search(m_content
);
378 QString ip
=rx
.cap(1);
384 kDebug(14140) << "trying to connect on " << ip
<<':' << port1
;
385 KBufferedSocket
*sock
=new KBufferedSocket( ip
, port1
, this );
386 m_allSockets
.append(sock
);
387 QObject::connect( sock
, SIGNAL( connected( const KResolverEntry
&) ), this, SLOT( slotSocketConnected() ) );
388 QObject::connect( sock
, SIGNAL( gotError(int)), this, SLOT(slotSocketError(int)));
389 sock
->connect(ip
, port1
);
390 kDebug(14140) << "okok " << sock
<< " - " << sock
->peerAddress().toString() << " ; " << sock
->localAddress().toString();
394 kDebug(14140) << "trying to connect on " << ip
<<':' << port2
;
395 KBufferedSocket
*sock
=new KBufferedSocket( ip
, port2
, this );
396 m_allSockets
.append(sock
);
397 QObject::connect( sock
, SIGNAL( connected( const KResolverEntry
&) ), this, SLOT( slotSocketConnected() ) );
398 QObject::connect( sock
, SIGNAL( gotError(int)), this, SLOT(slotSocketError(int)));
399 sock
->connect(ip
, port2
);
403 kDebug(14140) << "trying to connect on " << ip
<<':' << port3
;
404 KBufferedSocket
*sock
=new KBufferedSocket( ip
, port3
, this );
405 m_allSockets
.append(sock
);
406 QObject::connect( sock
, SIGNAL( connected( const KResolverEntry
&) ), this, SLOT( slotSocketConnected() ) );
407 QObject::connect( sock
, SIGNAL( gotError(int)), this, SLOT(slotSocketError(int)));
408 sock
->connect(ip
, port3
);
411 QList
<KBufferedSocket
*>::iterator it
;
412 for ( it
= m_allSockets
.begin(); it
!= m_allSockets
.end(); ++it
)
414 KBufferedSocket
*sock
=(*it
);
416 //sock->enableRead( false );
417 kDebug(14140) << "connect to " << sock
<< " - "<< sock
->peerAddress().toString() << " ; " << sock
->localAddress().toString();
420 else if(m_content
.contains("receivedViewerData"))
422 //I'm happy you received the xml i sent, really.
429 void Webcam::makeSIPMessage(const QString
&message
, quint8 XX
, quint8 YY
, quint8 ZZ
)
431 QByteArray dataMessage
; //(12+message.length()*2);
432 QDataStream
writer( &dataMessage
,QIODevice::WriteOnly
);
433 writer
.setVersion(QDataStream::Qt_3_3
);
434 writer
.setByteOrder(QDataStream::LittleEndian
);
435 writer
<< (quint8
)0x80;
436 writer
<< (quint8
)XX
;
437 writer
<< (quint8
)YY
;
438 writer
<< (quint8
)ZZ
;
439 writer
<< (quint8
)0x08;
440 writer
<< (quint8
)0x00;
441 writer
<< message
+'\0';
442 //writer << (quint16)0x0000;
446 while(f<dataMessage.size())
449 for(unsigned int q=0; q<16 ; q++)
451 if(q+f<dataMessage.size())
453 unsigned int N=(unsigned int) (dataMessage[q+f]);
456 echoS+=QString::number( N ,16)+' ';
463 for(unsigned int q=0; (q<16 && (q+f)<dataMessage.size()) ; q++)
465 unsigned char X=dataMessage[q+f];
466 char C=((char)(( X<128 && X>31 ) ? X : '.'));
467 echoS+=QString::fromLatin1(&C,1);
471 kDebug(14141) << dataMessage.size() << echoS;*/
474 sendBigP2PMessage(dataMessage
);
477 void Webcam::sendBigP2PMessage( const QByteArray
& dataMessage
)
479 unsigned int size
=m_totalDataSize
=dataMessage
.size();
483 for(unsigned int f
=0;f
<size
;f
+=1200)
487 unsigned int tempValue1
, tempValue2
;
489 tempValue2
= m_totalDataSize
-m_offset
;
490 dm2
.duplicate(dataMessage
.data()+m_offset
, qMin(tempValue1
,tempValue2
));
492 m_offset
+=dm2
.size();
500 QString
Webcam::xml(uint session
, uint rid
)
502 QString who
= ( m_who
== wProducer
) ? QString("producer") : QString("viewer");
507 QStringList::iterator it
;
508 QStringList ips
=m_dispatcher
->localIp();
509 for ( it
= ips
.begin(); it
!= ips
.end(); ++it
)
511 ip
+=QString("<tcpipaddress%1>%2</tcpipaddress%3>").arg(ip_number
).arg(*it
).arg(ip_number
);
515 QString port
= QString::number(getAvailablePort());
517 m_listener
= new KServerSocket(port
, this) ;
519 return '<' + who
+ "><version>2.0</version><rid>"+QString::number(rid
)+"</rid><udprid>"+QString::number(rid
+1)+"</udprid><session>"+QString::number(session
)+"</session><ctypes>0</ctypes><cpu>2931</cpu>" +
520 "<tcp><tcpport>7786</tcpport>\t\t\t\t\t\t\t\t <tcplocalport>7786</tcplocalport>\t\t\t\t\t\t\t\t <tcpexternalport>7786</tcpexternalport>"+ip
+"</tcp>"+
521 "<udp><udplocalport>7786</udplocalport><udpexternalport>31863</udpexternalport><udpexternalip>"+ ip
+"</udpexternalip><a1_port>31859</a1_port><b1_port>31860</b1_port><b2_port>31861</b2_port><b3_port>31862</b3_port><symmetricallocation>1</symmetricallocation><symmetricallocationincrement>1</symmetricallocationincrement><udpversion>1</udpversion><udpinternalipaddress1>127.0.0.1</udpinternalipaddress1></udp>"+
522 "<codec></codec><channelmode>1</channelmode></"+who
+">\r\n\r\n";
525 int Webcam::getAvailablePort()
527 KConfigGroup config
= KGlobal::config()->group("MSN");
528 QString basePort
=config
.readEntry("WebcamPort");
529 if(basePort
.isEmpty() || basePort
== "0" )
532 uint firstport
= basePort
.toInt();
533 uint maxOffset
=config
.readEntry("WebcamMaxPortOffset", 10);
534 uint lastport
= firstport
+ maxOffset
;
536 // try to find an available port
538 KServerSocket
*ss
= new KServerSocket();
539 ss
->setFamily(KResolver::InetFamily
);
541 unsigned int port
= firstport
;
542 for( ; port
<= lastport
; ++port
) {
543 ss
->setAddress( QString::number( port
) );
544 bool success
= ss
->listen();
545 if( found
= ( success
&& ss
->error() == KSocketBase::NoError
) )
552 kDebug(14140) << "found available port : " << port
;
558 /* ---------- Now functions about the dirrect connection --------- */
560 void Webcam::slotSocketConnected()
562 kDebug(14140) <<"##########################";
564 m_webcamSocket
=const_cast<KBufferedSocket
*>(static_cast<const KBufferedSocket
*>(sender()));
571 QList
<KBufferedSocket
*>::iterator it
;
572 for ( it
= m_allSockets
.begin(); it
!= m_allSockets
.end(); ++it
)
574 KBufferedSocket
*sock
=(*it
);
575 if(sock
!=m_webcamSocket
)
578 m_allSockets
.clear();
580 kDebug(14140) << "Connection established on " << m_webcamSocket
->peerAddress().toString() << " ; " << m_webcamSocket
->localAddress().toString();
582 m_webcamSocket
->setBlocking(false);
583 m_webcamSocket
->enableRead(true);
584 m_webcamSocket
->enableWrite(false);
586 // Create the callback that will try to read bytes from the accepted socket.
587 QObject::connect(m_webcamSocket
, SIGNAL(readyRead()), this, SLOT(slotSocketRead()));
588 // Create the callback that will try to handle the socket close event.
589 QObject::connect(m_webcamSocket
, SIGNAL(closed()), this, SLOT(slotSocketClosed()));
590 // Create the callback that will try to handle the socket error event.
591 // QObject::connect(m_webcamSocket, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
593 m_webcamState
=wsConnected
;
594 QByteArray to_send
=m_peerAuth
.toUtf8();
595 m_webcamSocket
->write(to_send
.data(), to_send
.length());
596 kDebug(14140) << "sending "<< m_peerAuth
;
601 void Webcam::slotAccept()
603 // Try to accept an incoming connection from the sending client.
604 m_webcamSocket
= static_cast<KBufferedSocket
*>(m_listener
->accept());
607 // NOTE If direct connection fails, the sending
608 // client wil transfer the file data through the
610 kDebug(14140) << "Direct connection failed.";
611 // Close the listening endpoint.
612 // m_listener->close();
616 kDebug(14140) << "Direct connection established.";
618 // Set the socket to non blocking,
619 // enable the ready read signal and disable
620 // ready write signal.
621 // NOTE readyWrite consumes too much cpu usage.
622 m_webcamSocket
->setBlocking(false);
623 m_webcamSocket
->enableRead(true);
624 m_webcamSocket
->enableWrite(false);
626 // Create the callback that will try to read bytes from the accepted socket.
627 QObject::connect(m_webcamSocket
, SIGNAL(readyRead()), this, SLOT(slotSocketRead()));
628 // Create the callback that will try to handle the socket close event.
629 QObject::connect(m_webcamSocket
, SIGNAL(closed()), this, SLOT(slotSocketClosed()));
630 // Create the callback that will try to handle the socket error event.
631 QObject::connect(m_webcamSocket
, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
633 m_allSockets
.append(m_webcamSocket
);
636 void Webcam::slotSocketRead()
638 m_webcamSocket
=const_cast<KBufferedSocket
*>(static_cast<const KBufferedSocket
*>(sender()));
640 uint available
= m_webcamSocket
->bytesAvailable();
641 kDebug(14140) << m_webcamSocket
<< "############# " << available
<< " bytes available.";
642 static const QString
connected_str("connected\r\n\r\n");
643 switch(m_webcamState
)
647 if(available
< m_myAuth
.length())
649 kDebug(14140) << "waiting more data ( " << available
<< " of " <<m_myAuth
.length()<< " )";
652 QByteArray
buffer(available
);
653 m_webcamSocket
->read(buffer
.data(), buffer
.size());
655 kDebug(14140) << buffer
.data();
657 if(QString(buffer
) == m_myAuth
)
659 closeAllOtherSockets();
660 kDebug(14140) << "Sending " << connected_str
;
661 QByteArray conne
=connected_str
.toUtf8();
662 m_webcamSocket
->write(conne
.data(), conne
.length());
663 m_webcamState
=wsConnecting
;
665 //SHOULD NOT BE THERE
666 m_mimic
=new MimicWrapper();
669 Kopete::AV::VideoDevicePool
*videoDevice
= Kopete::AV::VideoDevicePool::self();
671 videoDevice
->setSize(320, 240);
672 videoDevice
->startCapturing();
674 m_timerId
=startTimer(1000);
675 kDebug(14140) << "new timer" << m_timerId
;
677 m_widget
=new MSNWebcamDialog(m_recipient
);
678 connect(m_widget
, SIGNAL( closingWebcamDialog() ) , this , SLOT(sendBYEMessage()));
683 kWarning(14140) << "Auth failed";
684 m_webcamSocket
->disconnect();
685 m_webcamSocket
->deleteLater();
686 m_allSockets
.remove(m_webcamSocket
);
695 if(available
< connected_str
.length())
697 kDebug(14140) << "waiting more data ( " << available
<< " of " <<connected_str
.length()<< " )";
700 QByteArray
buffer(connected_str
.length());
701 m_webcamSocket
->read(buffer
.data(), buffer
.size());
703 // kDebug(14140) << "state " << m_webcamState << " received :" << QCString(buffer);
706 if(QString(buffer
) == connected_str
)
708 if(m_webcamState
==wsConnected
)
710 closeAllOtherSockets();
711 kDebug(14140) << "Sending " << connected_str
;
712 QByteArray conne
=connected_str
.toUtf8();
713 m_webcamSocket
->write(conne
.data(), conne
.length());
715 //SHOULD BE DONE IN ALL CASE
716 m_mimic
=new MimicWrapper();
719 Kopete::AV::VideoDevicePool
*videoDevice
= Kopete::AV::VideoDevicePool::self();
721 videoDevice
->setSize(320, 240);
722 videoDevice
->startCapturing();
724 m_timerId
=startTimer(1000);
725 kDebug(14140) << "new timer" << m_timerId
;
727 m_widget
=new MSNWebcamDialog(m_recipient
);
728 connect(m_widget
, SIGNAL( closingWebcamDialog() ) , this , SLOT(sendBYEMessage()));
731 m_webcamState
=wsTransfer
;
736 kWarning(14140) << "Connecting failed";
737 m_webcamSocket
->disconnect();
738 m_webcamSocket
->deleteLater();
739 m_allSockets
.remove(m_webcamSocket
);
748 kWarning(14140) << "data received when we are producer";
753 kDebug(14140) << "waiting more data ( " << available
<< " of " <<24<< " )";
756 QByteArray
buffer(available
);
757 m_webcamSocket
->peek(buffer
.data(), buffer
.size());
759 quint32 paysize
=(uchar
)buffer
[8] + ((uchar
)buffer
[9]<<8) + ((uchar
)buffer
[10]<<16) + ((uchar
)buffer
[11]<<24);
761 if(available
< (paysize
+24))
763 kDebug(14140) << "waiting more data ( " << available
<< " of " <<paysize
<< " )";
766 m_webcamSocket
->read(buffer
.data(), 24); //flush
767 buffer
.resize(paysize
);
768 m_webcamSocket
->read(buffer
.data(), buffer
.size());
770 QPixmap pix
=m_mimic
->decode(buffer
);
773 kWarning(14140) << "incorrect pixmap returned, better to stop everything";
774 m_webcamSocket
->disconnect();
777 m_widget
->newImage(pix
);
786 void Webcam::slotListenError(int errorCode
)
788 kWarning(14140) << "Error " << errorCode
<< " : " << m_listener
->errorString();
791 void Webcam::slotSocketClosed()
793 if(!m_dispatcher
) //we are in this destructor
799 void Webcam::slotSocketError(int errorCode
)
801 kDebug(14140) << errorCode
;
805 void Webcam::closeAllOtherSockets()
807 //m_lisener->close();
811 QList
<KBufferedSocket
*>::iterator it
;
812 for ( it
= m_allSockets
.begin(); it
!= m_allSockets
.end(); ++it
)
814 KBufferedSocket
*sock
=(*it
);
817 m_allSockets
.clear();
821 void Webcam::timerEvent( QTimerEvent
*e
)
823 if(e
->timerId() != m_timerId
)
824 return TransferContext::timerEvent(e
);
828 Kopete::AV::VideoDevicePool
*videoDevice
= Kopete::AV::VideoDevicePool::self();
829 videoDevice
->getFrame();
831 videoDevice
->getImage(&img
);
834 m_widget
->newImage(QPixmap::fromImage(img
.mirrored(VideoDevice
->getImageAsMirror(),false)));
836 if(img
.width()!=320 || img
.height()!=240)
838 kWarning(14140) << "Bad image size " <<img
.width() << 'x' << img
.height();
842 uchar
*bits
=img
.bits();
843 QByteArray
image_data(img
.width()*img
.height()*3);
845 uint imgsize
=img
.width()*img
.height()*4;
846 for(uint f
=0; f
< imgsize
; f
+=4)
848 image_data
[b2
+0]=bits
[f
+2];
849 image_data
[b2
+1]=bits
[f
+1];
850 image_data
[b2
+2]=bits
[f
+0];
854 QByteArray frame
=m_mimic
->encode(image_data
);
857 kDebug(14140) << "Sendinf frame of size " << frame
.size();
861 QDataStream
writer( &header
,QIODevice::WriteOnly
);
862 writer
.setVersion(QDataStream::Qt_3_3
);
863 writer
.setByteOrder(QDataStream::LittleEndian
);
864 writer
<< (quint16
)24; // header size
865 writer
<< (quint16
)img
.width();
866 writer
<< (quint16
)img
.height();
867 writer
<< (quint16
)0x0000; //wtf .?
868 writer
<< (quint32
)frame
.size();
869 writer
<< (quint8
)('M') << (quint8
)('L') << (quint8
)('2') << (quint8
)('0');
870 writer
<< (quint32
)0x00000000; //wtf .?
871 writer
<< QTime::currentTime(); //FIXME: possible midnight bug ?
873 m_webcamSocket
->write(header
.data(), header
.size());
874 m_webcamSocket
->write(frame
.data(), frame
.size());
881 #include "webcam.moc"