MythTV  master
programs/mythbackend/filetransfer.cpp
Go to the documentation of this file.
1 #include <QCoreApplication>
2 #include <QDateTime>
3 #include <QFileInfo>
4 #include <utility>
5 
6 #include "filetransfer.h"
7 #include "ringbuffer.h"
8 #include "mythdate.h"
9 #include "mythsocket.h"
10 #include "programinfo.h"
11 #include "mythlogging.h"
12 
13 FileTransfer::FileTransfer(QString &filename, MythSocket *remote,
14  bool usereadahead, int timeout_ms) :
15  ReferenceCounter(QString("FileTransfer:%1").arg(filename)),
16  m_rbuffer(RingBuffer::Create(filename, false, usereadahead, timeout_ms, true)),
17  m_sock(remote)
18 {
19  m_pginfo = new ProgramInfo(filename);
21  if (m_rbuffer && m_rbuffer->IsOpen())
22  m_rbuffer->Start();
23 }
24 
25 FileTransfer::FileTransfer(QString &filename, MythSocket *remote, bool write) :
26  ReferenceCounter(QString("FileTransfer:%1").arg(filename)),
27  m_rbuffer(RingBuffer::Create(filename, write)),
28  m_sock(remote), m_writemode(write)
29 {
30  m_pginfo = new ProgramInfo(filename);
32 
33  if (write)
34  remote->SetReadyReadCallbackEnabled(false);
35  if (m_rbuffer)
36  m_rbuffer->Start();
37 }
38 
40 {
41  Stop();
42 
43  if (m_sock) // FileTransfer becomes responsible for deleting the socket
44  m_sock->DecrRef();
45 
46  if (m_rbuffer)
47  {
48  delete m_rbuffer;
49  m_rbuffer = nullptr;
50  }
51 
52  if (m_pginfo)
53  {
55  delete m_pginfo;
56  }
57 }
58 
59 bool FileTransfer::isOpen(void)
60 {
61  return m_rbuffer && m_rbuffer->IsOpen();
62 }
63 
64 bool FileTransfer::ReOpen(QString newFilename)
65 {
66  if (!m_writemode)
67  return false;
68 
69  if (m_rbuffer)
70  return m_rbuffer->ReOpen(std::move(newFilename));
71 
72  return false;
73 }
74 
75 void FileTransfer::Stop(void)
76 {
77  if (m_readthreadlive)
78  {
79  m_readthreadlive = false;
80  LOG(VB_FILE, LOG_INFO, "calling StopReads()");
81  if (m_rbuffer)
83  QMutexLocker locker(&m_lock);
84  m_readsLocked = true;
85  }
86 
87  if (m_writemode)
88  if (m_rbuffer)
90 
91  if (m_pginfo)
93 }
94 
95 void FileTransfer::Pause(void)
96 {
97  LOG(VB_FILE, LOG_INFO, "calling StopReads()");
98  if (m_rbuffer)
100  QMutexLocker locker(&m_lock);
101  m_readsLocked = true;
102 
103  if (m_pginfo)
105 }
106 
107 void FileTransfer::Unpause(void)
108 {
109  LOG(VB_FILE, LOG_INFO, "calling StartReads()");
110  if (m_rbuffer)
112  {
113  QMutexLocker locker(&m_lock);
114  m_readsLocked = false;
115  }
116  m_readsUnlockedCond.wakeAll();
117 
118  if (m_pginfo)
120 }
121 
122 int FileTransfer::RequestBlock(int size)
123 {
124  if (!m_readthreadlive || !m_rbuffer)
125  return -1;
126 
127  int tot = 0;
128  int ret = 0;
129 
130  QMutexLocker locker(&m_lock);
131  while (m_readsLocked)
132  m_readsUnlockedCond.wait(&m_lock, 100 /*ms*/);
133 
134  m_requestBuffer.resize(max((size_t)max(size,0) + 128, m_requestBuffer.size()));
135  char *buf = &m_requestBuffer[0];
136  while (tot < size && !m_rbuffer->GetStopReads() && m_readthreadlive)
137  {
138  int request = size - tot;
139 
140  ret = m_rbuffer->Read(buf, request);
141 
142  if (m_rbuffer->GetStopReads() || ret <= 0)
143  break;
144 
145  if (m_sock->Write(buf, (uint)ret) != ret)
146  {
147  tot = -1;
148  break;
149  }
150 
151  tot += ret;
152  if (ret < request)
153  break; // we hit eof
154  }
155 
156  if (m_pginfo)
158 
159  return (ret < 0) ? -1 : tot;
160 }
161 
162 int FileTransfer::WriteBlock(int size)
163 {
164  if (!m_writemode || !m_rbuffer)
165  return -1;
166 
167  int tot = 0;
168  int ret = 0;
169 
170  QMutexLocker locker(&m_lock);
171 
172  m_requestBuffer.resize(max((size_t)max(size,0) + 128, m_requestBuffer.size()));
173  char *buf = &m_requestBuffer[0];
174  int attempts = 0;
175 
176  while (tot < size)
177  {
178  int request = size - tot;
179  int received = m_sock->Read(buf, (uint)request, 200 /*ms */);
180 
181  if (received != request)
182  {
183  LOG(VB_FILE, LOG_DEBUG,
184  QString("WriteBlock(): Read failed. Requested %1 got %2")
185  .arg(request).arg(received));
186  if (received < 0)
187  {
188  // An error occurred, abort immediately
189  break;
190  }
191  if (received == 0)
192  {
193  attempts++;
194  if (attempts > 3)
195  {
196  LOG(VB_FILE, LOG_ERR,
197  "WriteBlock(): Read tried too many times, aborting "
198  "(client or network too slow?)");
199  break;
200  }
201  continue;
202  }
203  }
204  attempts = 0;
205  ret = m_rbuffer->Write(buf, received);
206  if (ret <= 0)
207  {
208  LOG(VB_FILE, LOG_DEBUG,
209  QString("WriteBlock(): Write failed. Requested %1 got %2")
210  .arg(received).arg(ret));
211  break;
212  }
213 
214  tot += received;
215  }
216 
217  if (m_pginfo)
219 
220  return (ret < 0) ? -1 : tot;
221 }
222 
223 long long FileTransfer::Seek(long long curpos, long long pos, int whence)
224 {
225  if (m_pginfo)
227 
228  if (!m_rbuffer)
229  return -1;
230  if (!m_readthreadlive)
231  return -1;
232 
233  m_ateof = false;
234 
235  Pause();
236 
237  if (whence == SEEK_CUR)
238  {
239  long long desired = curpos + pos;
240  long long realpos = m_rbuffer->GetReadPosition();
241 
242  pos = desired - realpos;
243  }
244 
245  long long ret = m_rbuffer->Seek(pos, whence);
246 
247  Unpause();
248 
249  if (m_pginfo)
251 
252  return ret;
253 }
254 
255 uint64_t FileTransfer::GetFileSize(void)
256 {
257  if (m_pginfo)
259 
260  return m_rbuffer ? m_rbuffer->GetRealFileSize() : 0;
261 }
262 
263 QString FileTransfer::GetFileName(void)
264 {
265  if (!m_rbuffer)
266  return QString();
267 
268  return m_rbuffer->GetFilename();
269 }
270 
271 void FileTransfer::SetTimeout(bool fast)
272 {
273  if (m_pginfo)
275 
276  if (m_rbuffer)
277  m_rbuffer->SetOldFile(fast);
278 }
279 
280 /* vim: set expandtab tabstop=4 shiftwidth=4: */
def write(text, progress=True)
Definition: mythburn.py:279
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
void WriterFlush(void)
Calls ThreadedFileWriter::Flush(void)
General purpose reference counter.
void StartReads(void)
????
Definition: ringbuffer.cpp:722
void SetReadyReadCallbackEnabled(bool enabled)
Definition: mythsocket.h:49
QString GetFilename(void) const
Returns name of file used by this RingBuffer.
int Read(char *, int size, int max_wait_ms)
Definition: mythsocket.cpp:537
unsigned int uint
Definition: compat.h:140
void UpdateInUseMark(bool force=false)
void MarkAsInUse(bool inuse, const QString &usedFor="")
Tracks a recording's in use status, to prevent deletion and to allow the storage scheduler to perform...
bool GetStopReads(void) const
Returns value of stopreads.
Holds information on recordings and videos.
Definition: programinfo.h:66
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
int Write(const char *, int size)
Definition: mythsocket.cpp:524
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
void SetOldFile(bool is_old)
Tell RingBuffer if this is an old file or not.
virtual bool IsOpen(void) const =0
Returns true if open for either reading or writing.
FileTransfer(QString &filename, MythSocket *remote, MythSocketManager *parent, bool usereadahead, int timeout_ms)
virtual long long GetReadPosition(void) const =0
Returns how far into the file we have read.
void Start(void)
Starts the read-ahead thread.
Definition: ringbuffer.cpp:653
void StopReads(void)
????
Definition: ringbuffer.cpp:711
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
long long Seek(long long curpos, long long pos, int whence)
long long GetRealFileSize(void) const
Returns the size of the file we are reading/writing, or -1 if the query fails.
Definition: ringbuffer.cpp:497
const char * kFileTransferInUseID
int Read(void *buf, int count)
This is the public method for reading from a file, it calls the appropriate read method if the file i...
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:26
int Write(const void *buf, uint count)
Writes buffer to ThreadedFileWriter::Write(const void*,uint)
Implements a file/stream reader/writer.
long long Seek(long long pos, int whence, bool has_lock=false)
Seeks to a particular position in the file.
Definition: ringbuffer.cpp:510
virtual bool ReOpen(QString="")