void RecData::RecordThread() { lt_info("%s: begin\n", __func__); hal_set_threadname("hal:record"); const int readsize = bufsize / 16; int buf_pos = 0; int queued = 0; struct aiocb a; int val = fcntl(file_fd, F_GETFL); if (fcntl(file_fd, F_SETFL, val|O_APPEND)) lt_info("%s: O_APPEND? (%m)\n", __func__); memset(&a, 0, sizeof(a)); a.aio_fildes = file_fd; a.aio_sigevent.sigev_notify = SIGEV_NONE; dmx->Start(); int overflow_count = 0; bool overflow = false; int r = 0; while (exit_flag == RECORD_RUNNING) { if (buf_pos < bufsize) { if (overflow_count) { lt_info("%s: Overflow cleared after %d iterations\n", __func__, overflow_count); overflow_count = 0; } int toread = bufsize - buf_pos; if (toread > readsize) toread = readsize; ssize_t s = dmx->Read(buf + buf_pos, toread, 50); lt_debug("%s: buf_pos %6d s %6d / %6d\n", __func__, buf_pos, (int)s, bufsize - buf_pos); if (s < 0) { if (errno != EAGAIN && (errno != EOVERFLOW || !overflow)) { lt_info("%s: read failed: %m\n", __func__); exit_flag = RECORD_FAILED_READ; state = REC_STATUS_OVERFLOW; break; } } else { overflow = false; buf_pos += s; } } else { if (!overflow) overflow_count = 0; overflow = true; if (!(overflow_count % 10)) lt_info("%s: buffer full! Overflow? (%d)\n", __func__, ++overflow_count); state = REC_STATUS_SLOW; } r = aio_error(&a); if (r == EINPROGRESS) { lt_debug("%s: aio in progress, free: %d\n", __func__, bufsize - buf_pos); continue; } // not calling aio_return causes a memory leak --martii r = aio_return(&a); if (r < 0) { exit_flag = RECORD_FAILED_FILE; lt_debug("%s: aio_return = %d (%m)\n", __func__, r); break; } else lt_debug("%s: aio_return = %d, free: %d\n", __func__, r, bufsize - buf_pos); if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) perror("posix_fadvise"); if (queued) { memmove(buf, buf + queued, buf_pos - queued); buf_pos -= queued; } queued = buf_pos; a.aio_buf = buf; a.aio_nbytes = queued; r = aio_write(&a); if (r) { lt_info("%s: aio_write %d (%m)\n", __func__, r); exit_flag = RECORD_FAILED_FILE; break; } } dmx->Stop(); while (true) /* write out the unwritten buffer content */ { lt_debug("%s: run-out write, buf_pos %d\n", __func__, buf_pos); r = aio_error(&a); if (r == EINPROGRESS) { usleep(50000); continue; } r = aio_return(&a); if (r < 0) { exit_flag = RECORD_FAILED_FILE; lt_info("%s: aio_result: %d (%m)\n", __func__, r); break; } if (!queued) break; memmove(buf, buf + queued, buf_pos - queued); buf_pos -= queued; queued = buf_pos; a.aio_buf = buf; a.aio_nbytes = queued; r = aio_write(&a); } #if 0 // TODO: do we need to notify neutrino about failing recording? CEventServer eventServer; eventServer.registerEvent2(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, "/tmp/neutrino.sock"); stream2file_status2_t s; s.status = exit_flag; strncpy(s.filename,basename(myfilename),512); s.filename[511] = '\0'; strncpy(s.dir,dirname(myfilename),100); s.dir[99] = '\0'; eventServer.sendEvent(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, &s, sizeof(s)); printf("[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename); #endif lt_info("%s: end\n", __func__); pthread_exit(NULL); }
void cRecord::RecordThread() { lt_info("%s: begin\n", __func__); #define BUFSIZE (1 << 19) /* 512 kB */ ssize_t r = 0; int buf_pos = 0; uint8_t *buf; buf = (uint8_t *)malloc(BUFSIZE); if (!buf) { exit_flag = RECORD_FAILED_MEMORY; lt_info("%s: unable to allocate buffer! (out of memory)\n", __func__); } dmx->Start(); while (exit_flag == RECORD_RUNNING) { if (buf_pos < BUFSIZE) { r = dmx->Read(buf + buf_pos, BUFSIZE - 1 - buf_pos, 100); lt_debug("%s: buf_pos %6d r %6d / %6d\n", __func__, buf_pos, (int)r, BUFSIZE - 1 - buf_pos); if (r < 0) { if (errno != EAGAIN) { lt_info("%s: read failed: %m\n", __func__); exit_flag = RECORD_FAILED_READ; break; } lt_info("%s: EAGAIN\n", __func__); } else buf_pos += r; } else lt_info("%s: buffer full! Overflow?\n", __func__); if (buf_pos > (BUFSIZE / 3)) /* start writeout */ { size_t towrite = BUFSIZE / 2; if (buf_pos < BUFSIZE / 2) towrite = buf_pos; r = write(file_fd, buf, towrite); if (r < 0) { exit_flag = RECORD_FAILED_FILE; lt_info("%s: write error: %m\n", __func__); break; } buf_pos -= r; memmove(buf, buf + r, buf_pos); lt_debug("%s: buf_pos %6d w %6d / %6d\n", __func__, buf_pos, (int)r, (int)towrite); #if 0 if (fdatasync(file_fd)) perror("cRecord::FileThread() fdatasync"); #endif if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) perror("posix_fadvise"); } } dmx->Stop(); while (buf_pos > 0) /* write out the unwritten buffer content */ { r = write(file_fd, buf, buf_pos); if (r < 0) { exit_flag = RECORD_FAILED_FILE; lt_info("%s: write error: %m\n", __func__); break; } buf_pos -= r; memmove(buf, buf + r, buf_pos); } free(buf); #if 0 // TODO: do we need to notify neutrino about failing recording? CEventServer eventServer; eventServer.registerEvent2(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, "/tmp/neutrino.sock"); stream2file_status2_t s; s.status = exit_flag; strncpy(s.filename,basename(myfilename),512); s.filename[511] = '\0'; strncpy(s.dir,dirname(myfilename),100); s.dir[99] = '\0'; eventServer.sendEvent(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, &s, sizeof(s)); printf("[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename); #endif lt_info("%s: end", __func__); pthread_exit(NULL); }
void * DMXThread(void * v_arg) { pthread_t file_thread; struct filenames_t filename_data; char filename_extension[3]; ringbuffer_data_t vec[2]; ssize_t written; ssize_t todo; ssize_t todo2; unsigned char buf[TS_SIZE]; int offset = 0; ssize_t r = 0; struct pollfd pfd = {*(int*)v_arg, POLLIN|POLLERR,0 }; int pres; ringbuffer_t * ringbuf = ringbuffer_create(ringbuffersize); filename_data.ringbuffer = ringbuf; if (v_arg == &dvrfd) { filename_data.extension = "ts"; } else { for (int i = 0; i < MAXPIDS; i++) if (v_arg == (&(demuxfd[i]))) sprintf(filename_extension, "%u", i); filename_data.extension = filename_extension; } pthread_create(&file_thread, 0, FileThread, &filename_data); if (v_arg == &dvrfd) while (exit_flag == STREAM2FILE_STATUS_RUNNING) { if ((pres=poll (&pfd, 1, 15000))>0) { if (!(pfd.revents&POLLIN)) { printf ("PANIC: error reading from demux, bailing out\n"); exit_flag = STREAM2FILE_STATUS_READ_FAILURE; } r = read(*(int *)v_arg, &(buf[0]), TS_SIZE); if (r > 0) { offset = sync_byte_offset(&(buf[0]), r); if (offset != -1) break; } } else if (!pres) { printf ("[stream2file]: timeout from demux\n"); } } else offset = 0; written = ringbuffer_write(ringbuf, (char *)&(buf[offset]), r - offset); // TODO: Retry if (written != r - offset) { printf("PANIC: wrote less than requested to ringbuffer, written %d, requested %d\n", written, r - offset); exit_flag = STREAM2FILE_STATUS_BUFFER_OVERFLOW; } todo = IN_SIZE - (r - offset); /* IN_SIZE > TS_SIZE => todo > 0 */ while (exit_flag == STREAM2FILE_STATUS_RUNNING) { ringbuffer_get_write_vector(ringbuf, &(vec[0])); todo2 = todo - vec[0].len; if (todo2 < 0) { todo2 = 0; } else { if (((size_t)todo2) > vec[1].len) { printf("PANIC: not enough space in ringbuffer, available %d, needed %d\n", vec[0].len + vec[1].len, todo + todo2); exit_flag = STREAM2FILE_STATUS_BUFFER_OVERFLOW; } todo = vec[0].len; } while (exit_flag == STREAM2FILE_STATUS_RUNNING) { if ((pres=poll (&pfd, 1, 15000))>0) { if (!(pfd.revents&POLLIN)) { printf ("PANIC: error reading from demux, bailing out\n"); exit_flag = STREAM2FILE_STATUS_READ_FAILURE; } r = read(*(int *)v_arg, vec[0].buf, todo); if (r > 0) { ringbuffer_write_advance(ringbuf, r); if (todo == r) { if (todo2 == 0) goto next; todo = todo2; todo2 = 0; vec[0].buf = vec[1].buf; } else { vec[0].buf += r; todo -= r; } } } else if (!pres){ printf ("[stream2file]: timeout reading from demux\n"); goto next; } } next: todo = IN_SIZE; } if (v_arg == &dvrfd) close(*(int *)v_arg); else unsetPesFilter(*(int *)v_arg); pthread_join(file_thread, NULL); ringbuffer_free(ringbuf); if (v_arg == &dvrfd) while (demuxfd_count > 0) unsetPesFilter(demuxfd[--demuxfd_count]); DEC_BUSY_COUNT; if ((v_arg == &dvrfd) || (v_arg == (&(demuxfd[0])))) { CEventServer eventServer; eventServer.registerEvent2(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, "/tmp/neutrino.sock"); stream2file_status2_t s; s.status = exit_flag; strncpy(s.dir,dirname(myfilename),100); eventServer.sendEvent(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, &s, sizeof(s)); printf("[stream2file] pthreads exit code: %u\n", exit_flag); } pthread_exit(NULL); }
void cRecord::DMXThread() { pthread_t file_thread; ringbuffer_data_t vec[2]; ssize_t written; ssize_t todo = 0; ssize_t todo2; unsigned char buf[TS_SIZE]; int offset = 0; ssize_t r = 0; struct pollfd pfd; int pres; fprintf(stderr, "%s:%s >\n", __FILE__, __FUNCTION__); pfd.fd = dvrfd; pfd.events = POLLIN|POLLERR; pfd.revents = 0; ringbuffer_t * ringbuf = ringbuffer_create(ringbuffersize); if (!ringbuf) { exit_flag = STREAM2FILE_STATUS_WRITE_OPEN_FAILURE; fprintf(stderr, "[stream2file]: error allocating ringbuffer! (out of memory?)\n"); } else fprintf(stderr, "[stream2file] allocated ringbuffer size: %ld\n", ringbuffer_write_space(ringbuf)); ringbuffer = ringbuf; if (pthread_create(&file_thread, 0, execute_file_thread, this) != 0) { exit_flag = STREAM2FILE_STATUS_WRITE_OPEN_FAILURE; fprintf(stderr, "[stream2file]: error creating file_thread! (out of memory?)\n"); } while (exit_flag == STREAM2FILE_STATUS_RUNNING) { if ((pres=poll (&pfd, 1, 15000))>0) { if (!(pfd.revents&POLLIN)) { fprintf(stderr, "[stream2file]: PANIC: error reading from demux, bailing out\n"); exit_flag = STREAM2FILE_STATUS_READ_FAILURE; } r = read(dvrfd, &(buf[0]), TS_SIZE); if (r > 0) { offset = sync_byte_offset(&(buf[0]), r); if (offset != -1) break; } } else if (!pres) { fprintf(stderr, "[stream2file]: timeout from demux\n"); } } if (exit_flag == STREAM2FILE_STATUS_RUNNING) { written = ringbuffer_write(ringbuf, (char *)&(buf[offset]), r - offset); // TODO: Retry if (written != r - offset) { fprintf(stderr, "PANIC: wrote less than requested to ringbuffer, written %d, requested %d\n", written, r - offset); exit_flag = STREAM2FILE_STATUS_BUFFER_OVERFLOW; } todo = IN_SIZE - (r - offset); } /* IN_SIZE > TS_SIZE => todo > 0 */ while (exit_flag == STREAM2FILE_STATUS_RUNNING) { ringbuffer_get_write_vector(ringbuf, &(vec[0])); todo2 = todo - vec[0].len; if (todo2 < 0) { todo2 = 0; } else { if (((size_t)todo2) > vec[1].len) { fprintf(stderr, "PANIC: not enough space in ringbuffer, available %d, needed %d\n", vec[0].len + vec[1].len, todo + todo2); exit_flag = STREAM2FILE_STATUS_BUFFER_OVERFLOW; } todo = vec[0].len; } while (exit_flag == STREAM2FILE_STATUS_RUNNING) { if ((pres=poll (&pfd, 1, 5000))>0) { if (!(pfd.revents&POLLIN)) { fprintf(stderr, "PANIC: error reading from demux, bailing out\n"); exit_flag = STREAM2FILE_STATUS_READ_FAILURE; } r = read(dvrfd, vec[0].buf, todo); if (r > 0) { ringbuffer_write_advance(ringbuf, r); if (todo == r) { if (todo2 == 0) goto next; todo = todo2; todo2 = 0; vec[0].buf = vec[1].buf; } else { vec[0].buf += r; todo -= r; } } } else if (!pres){ fprintf(stderr, "[stream2file]: timeout reading from demux\n"); exit_flag = STREAM2FILE_STATUS_READ_FAILURE; } } next: todo = IN_SIZE; } close(dvrfd); pthread_join(file_thread, NULL); if (ringbuf) ringbuffer_free(ringbuf); while (demuxfd_count > 0) unsetPesFilter(demuxfd[--demuxfd_count]); #ifdef needed //fixme: do we need it? CEventServer eventServer; eventServer.registerEvent2(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, "/tmp/neutrino.sock"); stream2file_status2_t s; s.status = exit_flag; strncpy(s.filename,basename(myfilename),512); s.filename[511] = '\0'; strncpy(s.dir,dirname(myfilename),100); s.dir[99] = '\0'; eventServer.sendEvent(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, &s, sizeof(s)); fprintf(stderr, "[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename); #endif fprintf(stderr, "%s:%s <\n", __FILE__, __FUNCTION__); pthread_exit(NULL); }