bool cRecord::Start(int fd, unsigned short vpid, unsigned short * apids, int numpids) { fprintf(stderr, "%s:%s: fd %d, vpid 0x%02x\n", FILENAME, __FUNCTION__, fd, vpid); fprintf(stderr, "apids: "); for (int i = 0; i < numpids; i++) fprintf(stderr, "0x%02x ", apids[i]); fprintf(stderr, "\n"); file_fd = fd; demuxfd_count = 1 + numpids; //fixme: currently we only deal which what is called write_ts in earlier versions //not splitting is possible, because we dont have the filename here... for (unsigned int i = 0; i < demuxfd_count; i++) { unsigned short pid; if (i == 0) pid = vpid; else pid = apids[i-1]; if ((demuxfd[i] = setPesFilter(pid, DMX_OUT_TS_TAP)) < 0) { for (unsigned int j = 0; j < i; j++) unsetPesFilter(demuxfd[j]); fprintf(stderr, "error setting pes filter\n"); return false; } } if ((dvrfd = open(DVRDEV, O_RDONLY|O_NONBLOCK)) < 0) { while (demuxfd_count > 0) unsetPesFilter(demuxfd[--demuxfd_count]); fprintf(stderr, "error opening dvr device\n"); return false; } fprintf(stderr, "dvrfd %d\n", dvrfd); exit_flag = STREAM2FILE_STATUS_RUNNING; if (pthread_create(&demux_thread[0], 0, execute_demux_thread, this) != 0) { exit_flag = STREAM2FILE_STATUS_WRITE_OPEN_FAILURE; fprintf(stderr, "[stream2file]: error creating thread! (out of memory?)\n"); return false; } time(&record_start_time); fprintf(stderr, "record start time: %lu \n", record_start_time); return true; }
stream2file_error_msg_t start_recording(const char * const filename, const char * const info, const bool with_o_sync, const bool with_fdatasync, const unsigned long long splitsize, const unsigned int numpids, const unsigned short * const pids, const bool write_ts, const unsigned int ringbuffers) { int fd; char buf[FILENAMEBUFFERSIZE]; if (busy_count != 0) { if (exit_flag == STREAM2FILE_STATUS_RUNNING) return STREAM2FILE_BUSY; /* give threads a second to exit */ sleep(1); puts("[stream2file] recording attempt 2"); if (busy_count != 0) return STREAM2FILE_BUSY; } INC_BUSY_COUNT; strcpy(myfilename, filename); // write stream information (should wakeup the disk from standby, too) sprintf(buf, "%s.xml", filename); if ((fd = open(buf, O_SYNC | O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) { write(fd, info, strlen(info)); fdatasync(fd); close(fd); } else { DEC_BUSY_COUNT; return STREAM2FILE_INVALID_DIRECTORY; } if (splitsize < TS_SIZE) { limit = 1099511627776ULL; // 1024GB, virtually no splitting } else limit = splitsize; use_o_sync = with_o_sync; use_fdatasync = with_fdatasync; if (ringbuffers < 20) ringbuffersize = IN_SIZE * 20; else ringbuffersize = IN_SIZE * ringbuffers; for (unsigned int i = 0; i < numpids; i++) { if (pids[i] > 0x1fff) { DEC_BUSY_COUNT; return STREAM2FILE_INVALID_PID; } if ((demuxfd[i] = setPesFilter(pids[i], write_ts ? DMX_OUT_TS_TAP : DMX_OUT_TAP)) < 0) { for (unsigned int j = 0; j < i; j++) unsetPesFilter(demuxfd[j]); DEC_BUSY_COUNT; return STREAM2FILE_PES_FILTER_FAILURE; } } demuxfd_count = numpids; if (write_ts) { if ((dvrfd = open(DVRDEV, O_RDONLY)) < 0) { while (demuxfd_count > 0) unsetPesFilter(demuxfd[--demuxfd_count]); DEC_BUSY_COUNT; return STREAM2FILE_DVR_OPEN_FAILURE; } exit_flag = STREAM2FILE_STATUS_RUNNING; pthread_create(&demux_thread[0], 0, DMXThread, &dvrfd); } else { exit_flag = STREAM2FILE_STATUS_RUNNING; for (unsigned int i = 0; i < numpids; i++) { INC_BUSY_COUNT; pthread_create(&demux_thread[i], 0, DMXThread, &demuxfd[i]); } DEC_BUSY_COUNT; } return STREAM2FILE_OK; }
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); }
int main (int argc, char ** argv) { int pid; int pids[MAXPIDS]; char *fname; ssize_t written; int i; pthread_t rcst; int fd; if (argc < 4 ) { dprintf("Usage: streamfile file vpid apid [ pid3 pid4 ... ] (HEX-values without leading 0x!)\n"); dprintf("file: filename without trailing '.ts'\n"); return EXIT_FAILURE; } // set signal handler for clean termination signal(SIGTERM, clean_exit); buf = (unsigned char *) malloc(IN_SIZE); memset(buf, 0x00, IN_SIZE); if (buf == NULL) { perror("malloc buf"); return EXIT_FAILURE; } i = 1; while (argv[i][0] == '-') { if (!strcmp(argv[i], "-s")) silent = 1; if (!strcmp(argv[i], "-l")) sscanf(argv[++i], "%d", &limit); i++; } if (limit <= 0) limit=2; fname = argv[i++]; for (; i < argc; i++) { sscanf(argv[i], "%x", &pid); if (pid>0x1fff){ printf ("invalid pid 0x%04x specified\n",pid); return EXIT_FAILURE; } pids[demuxfd_count] = pid; if ((demuxfd[demuxfd_count] = setPesFilter(pid)) < 0) break; dprintf("set filter for pid 0x%x\n", pid); demuxfd_count++; } // create and delete temp-file to wakeup the disk from standby sprintf(buf, "%s.tmp", fname); fd = open(buf, O_SYNC | O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, S_IRUSR | S_IWUSR); write(fd, buf, IN_SIZE); fdatasync(fd); close(fd); unlink(buf); if ((dvrfd = open(DVRDEV, O_RDONLY)) < 0) { free(buf); perror ("[streamfile]: open dvr"); return EXIT_FAILURE; } ringbuf = ringbuffer_create (RINGBUFFERSIZE); pthread_create (&rcst, 0, FileThread, fname); /* write raw transport stream */ int offset=0; ringbuffer_data_t vec[2]; ssize_t r=0; ssize_t todo; ssize_t todo2; while (!exit_flag) { r = read(dvrfd, buf, IN_SIZE); if (r > 0) { offset = sync_byte_offset(buf, r); if (offset != -1) break; } } written = ringbuffer_write(ringbuf, buf + offset, r - offset); // TODO: Retry if (written != r - offset) { dprintf("PANIC: wrote less than requested to ringbuffer, written %d, requested %d\n", written, r - offset); exit_flag = 1; } todo = IN_SIZE - (r - offset); if (todo == 0) todo = IN_SIZE; while (!exit_flag) { ringbuffer_get_write_vector(ringbuf, &(vec[0])); todo2 = todo - vec[0].len; if (todo2 < 0) { todo2 = 0; } else { if (todo2 > vec[1].len) { dprintf("PANIC: not enough space in ringbuffer, available %d, needed %d\n", vec[0].len + vec[1].len, todo + todo2); exit_flag = 1; } todo = vec[0].len; } while (!exit_flag) { 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; } } } next: todo = IN_SIZE; } //sleep(1); // give FileThread some time to write remaining content of ringbuffer to file // pthread_kill(rcst, SIGKILL); while (demuxfd_count > 0) unsetPesFilter(demuxfd[--demuxfd_count]); close(dvrfd); pthread_join(rcst,NULL); free(buf); ringbuffer_free(ringbuf); dprintf("End of main(). All filters are unset now.\n"); return EXIT_SUCCESS; }
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); }
stream2file_error_msg_t start_recording(const char * const filename, const char * const info, int mode, const bool with_o_sync, const bool with_fdatasync, const unsigned long long splitsize, const unsigned int numpids, const unsigned short * const pids, const bool write_ts, const unsigned int ringbuffers, const bool with_gen_psi ) { int fd; char buf[FILENAMEBUFFERSIZE]; if (busy_count != 0) { if (exit_flag == STREAM2FILE_STATUS_RUNNING) return STREAM2FILE_BUSY; /* give threads a second to exit */ sleep(1); puts("[stream2file]: recording attempt 2"); if (busy_count != 0) return STREAM2FILE_BUSY; } INC_BUSY_COUNT; strcpy(myfilename, filename); mymode = mode; // printf("start_recording: myfilename '%s' filename '%s'\n",myfilename,filename); // write stream information (should wakeup the disk from standby, too) sprintf(buf, "%s.xml", filename); if ((fd = open(buf, O_SYNC|O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, REC_FILE_PERMISSIONS)) >= 0) { write(fd, info, strlen(info)); fdatasync(fd); close(fd); } else { if (errno == EEXIST) printf("[stream2file] INFO: %s already exists, not overwriting\n", buf); else { fprintf(stderr, "[stream2file] trying to create %s, error %d (%m)\n", buf, errno); DEC_BUSY_COUNT; return STREAM2FILE_INVALID_DIRECTORY; } } if (splitsize < TS_SIZE) { limit = 1099511627776ULL; // 1024GB, virtually no splitting } else limit = splitsize; use_o_sync = with_o_sync; use_fdatasync = with_fdatasync; gen_psi = with_gen_psi; if (ringbuffers > 4) ringbuffersize = ((1 << 19) << 4); else ringbuffersize = ((1 << 19) << ringbuffers); printf("[stream2file]: ringbuffersize %d write_ts %d numpids %d\n", ringbuffersize, write_ts, numpids); for (unsigned int i = 0; i < numpids; i++) { if (pids[i] > 0x1fff) { DEC_BUSY_COUNT; return STREAM2FILE_INVALID_PID; } #ifndef HAVE_TRIPLEDRAGON demuxfd[i] = setPesFilter(pids[i], write_ts ? DMX_OUT_TS_TAP : DMX_OUT_TAP); #else demuxfd[i] = setPesFilter(pids[i], OUT_MEMORY); #endif if (demuxfd[i] < 0) { for (unsigned int j = 0; j < i; j++) unsetPesFilter(demuxfd[j]); DEC_BUSY_COUNT; return STREAM2FILE_PES_FILTER_FAILURE; } } demuxfd_count = numpids; if (write_ts) { #ifdef HAVE_TRIPLEDRAGON if ((dvrfd = open(DMXDEV, O_RDWR|O_NONBLOCK)) != -1) { ioctl(dvrfd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); ioctl(dvrfd, DEMUX_SET_BUFFER_SIZE, 230400); struct demux_bucket_para dbp; dbp.unloader.unloader_type = UNLOADER_TYPE_TRANSPORT; dbp.unloader.threshold = 128; // one interrupt per 32kB if (ioctl(dvrfd, DEMUX_FILTER_BUCKET_SET, &dbp) < 0) perror("start_recording DEMUX_FILTER_BUCKET_SET"); } else #else if ((dvrfd = open(DVRDEV, O_RDONLY|O_NONBLOCK)) < 0) #endif { while (demuxfd_count > 0) unsetPesFilter(demuxfd[--demuxfd_count]); DEC_BUSY_COUNT; return STREAM2FILE_DVR_OPEN_FAILURE; } exit_flag = STREAM2FILE_STATUS_RUNNING; if (pthread_create(&demux_thread[0], 0, DMXThread, &dvrfd) != 0) { DEC_BUSY_COUNT; exit_flag = STREAM2FILE_STATUS_RECORDING_THREADS_FAILED; puts("[stream2file]: error creating thread! (out of memory?)"); while (demuxfd_count > 0) unsetPesFilter(demuxfd[--demuxfd_count]); close(dvrfd); return STREAM2FILE_RECORDING_THREADS_FAILED; } #ifdef HAVE_TRIPLEDRAGON if (ioctl(dvrfd, DEMUX_START) < 0) perror("start_recording DEMUX_START"); #endif } else { exit_flag = STREAM2FILE_STATUS_RUNNING; for (unsigned int i = 0; i < numpids; i++) { if (pthread_create(&demux_thread[i], 0, DMXThread, &demuxfd[i]) == 0) INC_BUSY_COUNT; else { DEC_BUSY_COUNT; exit_flag = STREAM2FILE_STATUS_RECORDING_THREADS_FAILED; puts("[stream2file]: error creating thread! (out of memory?)"); return STREAM2FILE_RECORDING_THREADS_FAILED; } } DEC_BUSY_COUNT; } /* this is set to 0 on program start and during stop_recording(). done this way, because stop_recording() is only called on a regular stopped recording. */ if (record_start_time == 0) time(&record_start_time); return STREAM2FILE_OK; }