diff options
-rw-r--r-- | clientcontrol.c | 51 | ||||
-rw-r--r-- | clientcontrol.h | 11 | ||||
-rw-r--r-- | ffnetdev.c | 5 | ||||
-rw-r--r-- | pes2ts.c | 155 | ||||
-rw-r--r-- | pes2ts.h | 34 | ||||
-rw-r--r-- | streamdevice.c | 33 | ||||
-rw-r--r-- | tsworker.c | 129 |
7 files changed, 306 insertions, 112 deletions
diff --git a/clientcontrol.c b/clientcontrol.c index ca5fd05..c3a3b05 100644 --- a/clientcontrol.c +++ b/clientcontrol.c @@ -166,6 +166,7 @@ void cClientControl::Action(void) if ( (ret = m_ClientSocket->Read(&data, sizeof(data))) == sizeof(data)) { + dsyslog("pakType %d, dataLen %d", data.pakType, data.dataLen); switch (data.pakType) { case ptInfo: @@ -201,8 +202,6 @@ bool cClientControl::SendPlayState(ePlayMode PlayMode, bool bPlay, bool bForward SClientControl data; SClientControlPlayState state; - int ret; - if ((m_Instance == NULL) || (m_Instance->m_ClientSocket == NULL)) return false; @@ -215,9 +214,10 @@ bool cClientControl::SendPlayState(ePlayMode PlayMode, bool bPlay, bool bForward data.pakType = ptPlayState; data.dataLen = sizeof(state); - if ((ret = m_Instance->m_ClientSocket->Write(&data, sizeof(data))) == sizeof(data)) + dsyslog("dataLen %d, dataSize %d", data.dataLen, sizeof(data)); + if (m_Instance->m_ClientSocket->Write(&data, sizeof(data)) == sizeof(data)) { - if ((ret = m_Instance->m_ClientSocket->Write(&state, sizeof(state))) == sizeof(state)) + if (m_Instance->m_ClientSocket->Write(&state, sizeof(state)) == sizeof(state)) return true; else return false; @@ -225,3 +225,46 @@ bool cClientControl::SendPlayState(ePlayMode PlayMode, bool bPlay, bool bForward else return false; } + + +bool cClientControl::SendStillPicture(const uchar *Data, int Length) +{ + SClientControl data; + int written, available, done; + + if ((m_Instance == NULL) || (m_Instance->m_ClientSocket == NULL)) + return false; + + data.pakType = ptStillPicture; + data.dataLen = Length; + if (m_Instance->m_ClientSocket->Write(&data, sizeof(data)) == sizeof(data)) + { + available = Length; + done = 0; + while ((available > 0) && (m_Instance->m_bHaveClient) && + (!m_Instance->m_bCloseClientRequest)) + { + + if (((written=m_Instance->m_ClientSocket->Write(Data + done, available)) < 0) && + (errno != EAGAIN)) + { + CloseStreamClient(); + return false; + } + + if (written > 0) + { + available -= written; + done += written; + } + else + { + cCondWait::SleepMs(5); + } + } + + return true; + } + else + return false; +} diff --git a/clientcontrol.h b/clientcontrol.h index 38c16dc..599dc5d 100644 --- a/clientcontrol.h +++ b/clientcontrol.h @@ -13,12 +13,12 @@ #include "tools/socket.h" #include "ffnetdev.h" -enum CCPakType{ ptInfo=0, ptPlayState, ptPlayStateReq }; +enum CCPakType{ ptInfo=0, ptPlayState, ptPlayStateReq, ptStillPicture }; struct SClientControl { char pakType; - char dataLen; + int dataLen; char data[0]; }; @@ -35,6 +35,12 @@ struct SClientControlPlayState char Speed; }; +struct SClientControlStillPicture +{ + uchar *Data; + int Length; +}; + // --- cClientControl ------------------------------------------------------------- @@ -65,6 +71,7 @@ public: static bool SendPlayState(ePlayMode PlayMode, bool bPlay, bool bForward, int iSpeed); static bool PlayStateReq(void) { return m_Instance->m_bPlayStateReq; }; + static bool SendStillPicture(const uchar *Data, int Length); }; inline bool cClientControl::Active(void) { @@ -238,6 +238,10 @@ void cPluginFFNetDev::SetPrimaryDevice() { if (m_Remote == NULL) { + i = 0; + while ((strlen(m_ClientName) == 0) && (i++ < 2)) + sleep(1); + char str[30]; if (strlen(m_ClientName) > 0) sprintf(str, "ffnetdev-%s", m_ClientName); @@ -280,7 +284,6 @@ void cPluginFFNetDev::RestorePrimaryDevice() Remotes.Del(m_Remote); m_Remote = NULL; } - dsyslog("[ffnetdev] -------------------\n"); } void cPluginFFNetDev::SetClientName(char* ClientName) @@ -14,8 +14,8 @@ #include "pes2ts.h" ////////////////////////////////////////////////////////////////////////////// -cPESRemux::cPESRemux(int inputBufferSize, int outputBufferSize): - m_InputBuffer(new cRingBufferLinear(inputBufferSize, outputBufferSize)) +cPESRemux::cPESRemux(int inputBufferSize): + m_InputBuffer(new cRingBufferLinear(inputBufferSize, IPACKS)) { OutputLocked = false; m_InputBuffer->SetTimeouts(0, 1000); // IMPORTANT to avoid busy wait in threads main loop and thus a high CPU load @@ -36,7 +36,7 @@ int cPESRemux::Put(const uchar *Data, int Count) ////////////////////////////////////////////////////////////////////////////// -cPES2TSRemux::cPES2TSRemux(int VPid, int APid): cPESRemux(INPUTBUFSIZE, IPACKS), +cPES2TSRemux::cPES2TSRemux(int VPid, int APid): cPESRemux(INPUTBUFSIZE), cThread("[ffnetdev] PES2TS remux"), m_OutputBuffer(new cRingBufferLinear(OUTPUTBUFSIZE, TS_SIZE * 2)), m_Active(false) @@ -96,10 +96,10 @@ void cPES2TSRemux::Action(void) // m_OutputBuffer->Available(), m_InputBuffer->Available()); if ( count < (int)minNeededPacketlen ) { - fprintf(stderr, "[ffnetdev] Remuxer: not enought bytes for PacketLen-Analysis, have only: %d\n", count); + dsyslog("[ffnetdev] Remuxer: not enought bytes for PacketLen-Analysis, have only: %d\n", count); InputMutex.Unlock(); cCondWait::SleepMs(2); - continue; + continue; } //DEBUG @@ -115,12 +115,12 @@ void cPES2TSRemux::Action(void) packetlen = ((data[4]<<8) | data[5]) + 6 ; if ( packetlen>IPACKS) { - fprintf(stderr, "[ffnetdev] Remuxer: IPACKS changed? packet length was %d, maximum: %d\n" + dsyslog("[ffnetdev] Remuxer: IPACKS changed? packet length was %d, maximum: %d\n" "This should not happen! Please report!\n", packetlen, IPACKS); } if ( count < (int)packetlen) { - fprintf(stderr, "[ffnetdev] Remuxer: not enought bytes for whole packet, have only: %d but LenShoud be %d\n", count, packetlen); + dsyslog("[ffnetdev] Remuxer: not enought bytes for whole packet, have only: %d but LenShoud be %d\n", count, packetlen); InputMutex.Unlock(); cCondWait::SleepMs(1); continue; @@ -138,7 +138,7 @@ void cPES2TSRemux::Action(void) cc=&vcc; } else { - fprintf(stderr, "[ffnetdev] Remuxer: Unknown stream id: neither video nor audio type.\n"); + dsyslog("[ffnetdev] Remuxer: Unknown stream id: neither video nor audio type.\n"); // throw away whole PES packet m_InputBuffer->Del(packetlen); InputMutex.Unlock(); @@ -156,19 +156,20 @@ void cPES2TSRemux::Action(void) // no valid PES signature was found, so delete this stuff from ring buffer // normally we should always receive a whole PES packet, since VDR always gives us a whole packet and not less or more // with each call in streamdevice.c (PlayVideo, PlayAudio) - fprintf(stderr, "[ffnetdev] Remuxer: No valid PES signature found. This should not happen.\n"); + dsyslog("[ffnetdev] Remuxer: No valid PES signature found. This should not happen.\n"); m_InputBuffer->Del(1); // Probably it is better to delete 1 byte only to get in sync again!? InputMutex.Unlock(); continue; } - int tspacketlen = ((int)packetlen/184) * 188 + ((packetlen % 184 > 0) ? 188 : 0); - while (m_OutputBuffer->Free() < tspacketlen) { - if (!m_Active) - continue; - cCondWait::SleepMs(10); - //fprintf(stderr, "[ffnetdev] Remuxer: sleep %d %d\n", m_OutputBuffer->Free(), tspacketlen); + int tspacketlen = (packetlen/184) * 188 + ((packetlen % 184 > 0) ? 188 : 0); + while (m_OutputBuffer->Free() < tspacketlen) + { + if (!m_Active) + continue; + cCondWait::SleepMs(10); + //dsyslog("[ffnetdev] Remuxer: sleep %d %d\n", m_OutputBuffer->Free(), tspacketlen); } LockOutput(); @@ -211,10 +212,132 @@ void cPES2TSRemux::Action(void) ////////////////////////////////////////////////////////////////////////////// -cPES2PESRemux::cPES2PESRemux(): cPESRemux(INPUTBUFSIZE + OUTPUTBUFSIZE, IPACKS) +cPES2PESRemux::cPES2PESRemux(): cPESRemux(INPUTBUFSIZE), + cThread("[ffnetdev] PES2PES remux"), + m_OutputBuffer(new cRingBufferLinear(OUTPUTBUFSIZE, IPACKS)), + m_Active(false) { + m_OutputBuffer->SetTimeouts(0, 1000); + Start(); } cPES2PESRemux::~cPES2PESRemux() { + m_Active = false; + delete m_OutputBuffer; +} + +void cPES2PESRemux::Action(void) +{ + unsigned int packetlen; + unsigned int minNeededPacketlen = 10; // needed for read packet len: 6 Should be enought ... but makes no sense + + m_Active = true; + + while (m_Active) + { + int count=0; + // fprintf(stderr, "[ffnetdev] Remuxer: Inputbuffersize: %d, Outputbuffersize: %d\n", + // m_InputBuffer->Available(), m_OutputBuffer->Available()); + + if (m_InputBuffer->Available() < (int)IPACKS*10) + { + cCondWait::SleepMs(5); + continue; + } + + if (!cTSWorker::HaveStreamClient()) + { + ClearOutput(); + cCondWait::SleepMs(10); + continue; + } + + + InputMutex.Lock(); + uchar *data = m_InputBuffer->Get(count); + if (data==NULL) + { + InputMutex.Unlock(); + cCondWait::SleepMs(3); + continue; + } + + if ( count < (int)minNeededPacketlen ) + { + dsyslog("[ffnetdev] Remuxer: not enought bytes for PacketLen-Analysis, have only: %d\n", count); + InputMutex.Unlock(); + cCondWait::SleepMs(2); + continue; + } + + // check for valid PES signature in PES header + if ( (data[0]==0x00) && (data[1]==0x00) && (data[2]==0x01) ) + { + packetlen = ((data[4]<<8) | data[5]) + 6 ; + + if ( packetlen>IPACKS) { + dsyslog("[ffnetdev] Remuxer: IPACKS changed? packet length was %d, maximum: %d\n" + "This should not happen! Please report!\n", packetlen, IPACKS); + } + + if ( count < (int)packetlen) + { + dsyslog("[ffnetdev] Remuxer: not enought bytes for whole packet, have only: %d but LenShoud be %d\n", count, packetlen); + InputMutex.Unlock(); + cCondWait::SleepMs(1); + continue; + } + + + // check for valid stream id type: is it video or audio or unknown? + if ( ((data[3]>=0xC0) && (data[3]<=0xDF)) || + (data[3] == 0xBD) || + ((data[3]>=0xE0) && (data[3]<=0xEF))) + { + while (m_OutputBuffer->Free() < (int)packetlen) + { + if (!m_Active) + continue; + cCondWait::SleepMs(10); + //dsyslog("[ffnetdev] Remuxer: sleep %d %d\n", m_OutputBuffer->Free(), tspacketlen); + } + + LockOutput(); + m_OutputBuffer->Put(data, packetlen); + // we are now finished with the PES packet, delete it from ring buffer + m_InputBuffer->Del(packetlen); + UnlockOutput(); + } + else if (data[3]>=0xBE) + { + dsyslog("[ffnetdev] Remuxer: Padding stream removed.\n"); + m_InputBuffer->Del(packetlen); + InputMutex.Unlock(); + continue; + } + else + { + dsyslog("[ffnetdev] Remuxer: Unknown stream id: neither video nor audio type.\n"); + // throw away whole PES packet + m_InputBuffer->Del(packetlen); + InputMutex.Unlock(); + continue; + } + + InputMutex.Unlock(); + } + else + { + // no valid PES signature was found, so delete this stuff from ring buffer + // normally we should always receive a whole PES packet, since VDR always gives us a whole packet and not less or more + // with each call in streamdevice.c (PlayVideo, PlayAudio) + dsyslog("[ffnetdev] Remuxer: No valid PES signature found. This should not happen.\n"); + + m_InputBuffer->Del(1); // Probably it is better to delete 1 byte only to get in sync again!? + InputMutex.Unlock(); + continue; + } + } + m_Active = false; } @@ -16,7 +16,8 @@ #define TS_SIZE 188 #define IPACKS 2048 -class cPESRemux { +class cPESRemux +{ private: bool OutputLocked; @@ -25,7 +26,7 @@ protected: cMutex InputMutex; public: - cPESRemux(int inputBufferSize, int outputBufferSize); + cPESRemux(int inputBufferSize); virtual ~cPESRemux(); int Put(const uchar *Data, int Count); @@ -33,6 +34,7 @@ public: void ClearInput () { InputMutex.Lock(); m_InputBuffer ->Clear(); InputMutex.Unlock(); } virtual int Available(void) = 0; + virtual int Fill(void) = 0; virtual int Free(void) = 0; virtual int InputFree(void) = 0; virtual uchar *Get(int &Count) = 0; @@ -42,7 +44,8 @@ public: void UnlockOutput() { OutputLocked = false; } }; -class cPES2TSRemux: public cPESRemux, cThread { +class cPES2TSRemux: public cPESRemux, cThread +{ private: cRingBufferLinear *m_OutputBuffer; bool m_Active; @@ -57,6 +60,7 @@ public: virtual ~cPES2TSRemux(); int Available(void) { return m_OutputBuffer->Available(); } + int Fill(void) { return m_InputBuffer->Available() + m_OutputBuffer->Available(); } int Free(void) { return m_OutputBuffer->Free(); } int InputFree(void) { return m_InputBuffer->Free(); } uchar *Get(int &Count) { return m_OutputBuffer->Get(Count); } @@ -64,18 +68,26 @@ public: void ClearOutput() { LockOutput(); m_OutputBuffer->Clear(); UnlockOutput(); } }; -class cPES2PESRemux: public cPESRemux { +class cPES2PESRemux: public cPESRemux, cThread +{ +private: + cRingBufferLinear *m_OutputBuffer; + bool m_Active; + +protected: + virtual void Action(void); + public: cPES2PESRemux(); virtual ~cPES2PESRemux(); - int Available(void) { return m_InputBuffer->Available(); } - int Free(void) { return m_InputBuffer->Free(); } - int InputFree(void) { return m_InputBuffer->Free(); } - uchar *Get(int &Count) { return m_InputBuffer->Get(Count); } - void DelOutput(int Count) { m_InputBuffer->Del(Count); } - void ClearOutput() { LockOutput(); m_InputBuffer->Clear(); UnlockOutput(); } - + int Available(void) { return m_OutputBuffer->Available(); } + int Fill(void) { return m_InputBuffer->Available() + m_OutputBuffer->Available(); } + int Free(void) { return m_OutputBuffer->Free(); } + int InputFree(void) { return m_InputBuffer->Free(); } + uchar *Get(int &Count) { return m_OutputBuffer->Get(Count); } + void DelOutput(int Count) { m_OutputBuffer->Del(Count); } + void ClearOutput() { LockOutput(); m_OutputBuffer->Clear(); UnlockOutput(); } }; #endif // PES2TSREMUX_H diff --git a/streamdevice.c b/streamdevice.c index 283229c..d15b66a 100644 --- a/streamdevice.c +++ b/streamdevice.c @@ -25,6 +25,7 @@ cStreamDevice::cStreamDevice(void) cStreamDevice::~cStreamDevice(void) { dsyslog("[ffnetdev] Device: Destructor cStreamDevice \n"); + m_PlayState = psPlay; DELETENULL(m_Remux); } @@ -77,12 +78,14 @@ bool cStreamDevice::SetPlayMode(ePlayMode PlayMode) bool Forward; int Speed; pControl->GetReplayMode(m_Playing, Forward, Speed); - cClientControl::SendPlayState(PlayMode, m_Playing, Forward, Speed); + if (!cClientControl::SendPlayState(PlayMode, m_Playing, Forward, Speed)) + m_PlayState = psPlay; } else { m_Playing = false; - cClientControl::SendPlayState(PlayMode, false, false, 0); + if (!cClientControl::SendPlayState(PlayMode, false, false, 0)) + m_PlayState = psPlay; } return true; } @@ -96,7 +99,8 @@ void cStreamDevice::TrickSpeed(int Speed) bool Forward; int Speed; pControl->GetReplayMode(m_Playing, Forward, Speed); - cClientControl::SendPlayState(m_PlayMode, m_Playing, Forward, Speed); + if (!cClientControl::SendPlayState(m_PlayMode, m_Playing, Forward, Speed)) + m_PlayState = psPlay; } } @@ -115,7 +119,8 @@ void cStreamDevice::Play(void) bool Forward; int Speed; pControl->GetReplayMode(m_Playing, Forward, Speed); - cClientControl::SendPlayState(m_PlayMode, m_Playing, Forward, Speed); + if (!cClientControl::SendPlayState(m_PlayMode, m_Playing, Forward, Speed)) + m_PlayState = psPlay; } // cDevice::Play(); @@ -140,7 +145,8 @@ void cStreamDevice::SetVolumeDevice(int Volume) void cStreamDevice::StillPicture(const uchar *Data, int Length) { - dsyslog("[ffnetdev] Device: StillPicture(not implemented).\n"); + dsyslog("[ffnetdev] Device: StillPicture %d Bytes.\n", Length); + cClientControl::SendStillPicture(Data, Length); } bool cStreamDevice::Poll(cPoller &Poller, int TimeoutMs) @@ -158,12 +164,14 @@ int cStreamDevice::PlayAudio(const uchar *Data, int Length, uchar Id) { if (cTSWorker::HaveStreamClient()) { - while (((m_Remux->InputFree() < Length) && (!m_Playing) || - (m_Remux->Available() > TCP_SEND_SIZE * 10) && (m_Playing)) && cTSWorker::HaveStreamClient()) + while ((((!m_Playing) && (m_Remux->InputFree() < Length)) || + ((m_Playing) && ((m_Remux->InputFree() < Length) || + (m_Remux->Fill() > TCP_SEND_SIZE * 10)))) && + (cTSWorker::HaveStreamClient())) cCondWait::SleepMs(1); int result=m_Remux->Put(Data, Length); if (result!=Length) { - fprintf(stderr,"[ffnetdev] Device: Did not put all in input buffer(audio). result:%d Length: %d Skipping Audio PES packet...\n", result, Length ); + dsyslog("[ffnetdev] Device: Did not put all in input buffer(audio). result:%d Length: %d Skipping Audio PES packet...\n", result, Length ); // Delete part of data already written to buffer m_Remux->DelInput(result); return (0); @@ -188,13 +196,14 @@ int cStreamDevice::PlayVideo(const uchar *Data, int Length) { if (cTSWorker::HaveStreamClient()) { - - while (((m_Remux->InputFree() < Length) && (!m_Playing) || - (m_Remux->Available() > TCP_SEND_SIZE * 10) && (m_Playing)) && cTSWorker::HaveStreamClient()) + while ((((!m_Playing) && (m_Remux->InputFree() < Length)) || + ((m_Playing) && ((m_Remux->InputFree() < Length) || + (m_Remux->Fill() > TCP_SEND_SIZE * 10)))) && + (cTSWorker::HaveStreamClient())) cCondWait::SleepMs(1); int result=m_Remux->Put(Data, Length); if (result!=Length) { - fprintf(stderr,"[ffnetdev] Device: Did not put all in input buffer(video). result:%d Length: %d Skipping Video PES packet...\n", result, Length ); + dsyslog("[ffnetdev] Device: Did not put all in input buffer(video). result:%d Length: %d Skipping Video PES packet...\n", result, Length ); // Delete part of data already written to buffer m_Remux->DelInput(result); return (0); @@ -182,79 +182,76 @@ void cTSWorker::ActionTCP(void) { } } - if ( select.CanWrite(*m_StreamClient) ) { - + if ( select.CanWrite(*m_StreamClient) ) + { if (m_StreamDevice->GetPlayState() == psBufferReset) { cCondWait::SleepMs(10); m_StreamDevice->SetPlayState(psBufferReseted); } - if (m_StreamDevice->GetPlayState() == psBufferReseted) + if (m_StreamDevice->GetPlayState() == psPlay) { - cCondWait::SleepMs(10); - continue; - } - - int count=0; - - m_StreamDevice->LockOutput(); - uchar *buffer = m_StreamDevice->Get(count); - if (buffer!=NULL) { - count = (count > TCP_SEND_SIZE) ? TCP_SEND_SIZE : count; - int available = count; - int done = 0; - int written = 0; - while ((available > 0) && (have_Streamclient == true) && - (!close_Streamclient_request)) - { - - if (((written=m_StreamClient->Write(&buffer[done], available)) < 0) && - (errno != EAGAIN)) - { - CloseStreamClient(); - } - - if (written > 0) - { - available -= written; - done += written; - } - else - { - cCondWait::SleepMs(5); - } - } - m_StreamDevice->Del(count); - - struct timeval curtime; - gettimeofday(&curtime, 0); - if (oldtime.tv_sec == 0) - { - oldtime = curtime; - bytessend = 0; - oldbytessend = 0; - } - - bytessend += count; - if (curtime.tv_sec > oldtime.tv_sec + 10) - { - double secs = (curtime.tv_sec * 1000 + (curtime.tv_usec / 1000.0)) / 1000 - - (oldtime.tv_sec * 1000 + (oldtime.tv_usec / 1000.0)) / 1000; - double rate = (double)((bytessend - oldbytessend) / secs) * 8 / 1024 / 1024; - int bufstat = m_StreamDevice->Available() * 100 / (m_StreamDevice->Available() + m_StreamDevice->Free()); -#ifdef DEBUG - fprintf(stderr, "[ffnetdev] Streamer: current TransferRate %2.3f MBit/Sec, %d Bytes send, %d%% Buffer used\n", - rate, bytessend - oldbytessend, bufstat); -#endif - dsyslog("[ffnetdev] Streamer: Rate %2.3f MBit/Sec, %d Bytes send, %d%% Buffer used\n", - rate, bytessend - oldbytessend, bufstat); - - oldbytessend = bytessend; - oldtime = curtime; - } - } - m_StreamDevice->UnlockOutput(); + int count=0; + + m_StreamDevice->LockOutput(); + uchar *buffer = m_StreamDevice->Get(count); + if (buffer!=NULL) { + count = (count > TCP_SEND_SIZE) ? TCP_SEND_SIZE : count; + int available = count; + int done = 0; + int written = 0; + while ((available > 0) && (have_Streamclient == true) && + (!close_Streamclient_request)) + { + + if (((written=m_StreamClient->Write(&buffer[done], available)) < 0) && + (errno != EAGAIN)) + { + CloseStreamClient(); + } + + if (written > 0) + { + available -= written; + done += written; + } + else + { + cCondWait::SleepMs(5); + } + } + m_StreamDevice->Del(count); + + struct timeval curtime; + gettimeofday(&curtime, 0); + if (oldtime.tv_sec == 0) + { + oldtime = curtime; + bytessend = 0; + oldbytessend = 0; + } + + bytessend += count; + if (curtime.tv_sec > oldtime.tv_sec + 10) + { + double secs = (curtime.tv_sec * 1000 + (curtime.tv_usec / 1000.0)) / 1000 + - (oldtime.tv_sec * 1000 + (oldtime.tv_usec / 1000.0)) / 1000; + double rate = (double)((bytessend - oldbytessend) / secs) * 8 / 1024 / 1024; + int bufstat = m_StreamDevice->Available() * 100 / (m_StreamDevice->Available() + m_StreamDevice->Free()); + #ifdef DEBUG + fprintf(stderr, "[ffnetdev] Streamer: current TransferRate %2.3f MBit/Sec, %d Bytes send, %d%% Buffer used\n", + rate, bytessend - oldbytessend, bufstat); + #endif + dsyslog("[ffnetdev] Streamer: Rate %2.3f MBit/Sec, %d Bytes send, %d%% Buffer used\n", + rate, bytessend - oldbytessend, bufstat); + + oldbytessend = bytessend; + oldtime = curtime; + } + } + m_StreamDevice->UnlockOutput(); + } } if ( select.CanRead(*m_StreamClient) ) |