OSDN Git Service

tunerec: include <unistd.h> for usleep
[rec10/rec10-git.git] / tunerec / tunerec.c
index 0a1d1fe..bce1a50 100755 (executable)
@@ -1,3 +1,5 @@
+#define _FILE_OFFSET_BITS 64
+
 #include <linux/dvb/frontend.h>
 #include <linux/dvb/dmx.h>
 #include <linux/dvb/audio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
 
 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#ifndef DTV_STREAM_ID
+#define DTV_STREAM_ID DTV_ISDBS_TS_ID
+#endif
+#define DVB_READ_BUFFER_SIZE 188 * 128
+#define DVB_WRITE_BUFFER_SIZE 1024 * 1024
+#define DMX_BUFFER_SIZE 1024 * 1024
+#define RING_BUFFER_SIZE 1024 * 1024 * 16
+
+typedef struct {
+       void* rb_ptr;
+       int rb_wt;
+       int rb_rt;
+       int rb_ab;
+       pthread_mutex_t rb_mtx;
+} mrb;
+
+typedef struct {
+       int fin;
+       int fout;
+       int rectime;
+       mrb rb;
+       bool done;
+} my_thread_arg;
+
+void * record_read(void * priv);
+void * record_write(void * priv);
+
+// TODO: switchable between VRB(virtual ring buffer) and MRB(My Ring Buffer)
+
+void mrb_init(mrb* rb, size_t size) {
+       rb->rb_ptr = malloc(size);
+       rb->rb_wt = rb->rb_rt = rb->rb_ab = 0;
+       pthread_mutex_init(&rb->rb_mtx, NULL);
+}
+
+void mrb_destroy(mrb* rb) {
+       free(rb->rb_ptr);
+       pthread_mutex_destroy(&rb->rb_mtx);
+}
+
+size_t mrb_data_len(mrb* rb) {
+       return rb->rb_ab;
+}
+
+bool mrb_put(mrb* rb, char *source, size_t size) {
+       int partial;
+
+       if ( rb->rb_ab + size > RING_BUFFER_SIZE ) {
+               // RingBuffer overflow.
+               return false;
+       }
+       if ( rb->rb_wt + size <= RING_BUFFER_SIZE ) {
+               // written bytes + newly added bytes <= buffer max size
+               memcpy(rb->rb_ptr + rb->rb_wt, source, size);
+               rb->rb_wt = rb->rb_wt + size;
+       }
+       else {
+               // written bytes + newly added bytes > buffer max size
+               // wrap around.
+               partial = RING_BUFFER_SIZE - rb->rb_wt;
+               memcpy(rb->rb_ptr + rb->rb_wt, source, partial);
+               memcpy(rb->rb_ptr, source + partial, size - partial);
+               rb->rb_wt = rb->rb_wt + size - RING_BUFFER_SIZE;
+       }
+       pthread_mutex_lock(&rb->rb_mtx);
+       rb->rb_ab += size;
+       pthread_mutex_unlock(&rb->rb_mtx);
+
+       return true;
+}
+
+bool mrb_get(mrb* rb, char *target, size_t size) {
+       int partial;
+
+       if ( rb->rb_ab < size ) {
+               // RingBuffer underflow.
+               return false;
+       }
+       if ( rb->rb_rt + size <= RING_BUFFER_SIZE ) {
+               // read bytes + newly taken bytes <= buffer max size
+               memcpy(target, rb->rb_ptr + rb->rb_rt, size);
+               rb->rb_rt = rb->rb_rt + size;
+       }
+       else {
+               // read bytes + newly taken bytes > buffer max size
+               // wrap around.
+               partial = RING_BUFFER_SIZE - rb->rb_rt;
+               memcpy(target, rb->rb_ptr + rb->rb_rt, partial);
+               memcpy(target + partial, rb->rb_ptr, size - partial);
+               rb->rb_rt = rb->rb_rt + size - RING_BUFFER_SIZE;
+       }
+       pthread_mutex_lock(&rb->rb_mtx);
+       rb->rb_ab -= size;
+       pthread_mutex_unlock(&rb->rb_mtx);
+
+       return true;
+}
 
 static int search(int adapter_nr, unsigned int frequency, unsigned int ts_id)
 {
@@ -101,9 +207,8 @@ static int track(int adapter_nr)
 void record(int adapter_nr, char* output, int rectime) {
        int fin, fout;
        char input[256];
-       time_t start_time, current_time;
-       char buf[1316];
-       ssize_t rt, wt, shift;
+       pthread_t read_thread, write_thread;
+       my_thread_arg arg;
 
        fout = open(output, (O_WRONLY | O_CREAT | O_TRUNC), 0666);
        if ( fout < 0 ) {
@@ -111,54 +216,137 @@ void record(int adapter_nr, char* output, int rectime) {
                return;
        }
        sprintf(input, "/dev/dvb/adapter%d/dvr0", adapter_nr);
-       start_time = time(NULL);
        fin = open(input, (O_RDONLY));
-       while ( rt = read(fin, buf, 1316) > 0 ) {
+       ioctl(fin, DMX_SET_BUFFER_SIZE, DMX_BUFFER_SIZE);
+
+       mrb_init(&arg.rb, RING_BUFFER_SIZE);
+       arg.done = false;
+       arg.rectime = rectime;
+       arg.fin  = fin;
+       arg.fout = fout;
+       pthread_create(&read_thread , NULL, record_read , &arg);
+       pthread_create(&write_thread, NULL, record_write, &arg);
+       pthread_join(write_thread, NULL);
+       pthread_join(read_thread, NULL);
+
+       mrb_destroy(&arg.rb);
+       close(fin);
+       close(fout);
+}
+
+void * record_read(void * priv) {
+       ssize_t rt;
+       time_t start_time, current_time;
+       char* buf = malloc(DVB_READ_BUFFER_SIZE);
+       my_thread_arg* p = (my_thread_arg*)priv;
+
+       start_time = time(NULL);
+       while ( 1 ) {
+               rt = read(p->fin, buf, DVB_READ_BUFFER_SIZE);
+               //printf("read %d %d\n", p->fin, rt);
+               if ( rt == 0 ) {
+                       printf("dvr0 file read failed\n");
+                       goto error;
+               }
+               if ( rt < 0 ) {
+                       switch (errno) {
+                       case EOVERFLOW:
+                               printf("read overflow, continue\n");
+                               continue;
+                       default:
+                               printf("read finished. err=%d\n", errno);
+                               goto error;
+                       }
+               }
+
+               mrb_put(&p->rb, buf, rt);
+
+               // TODO: time() at each DVB_READ_BUFFER_SIZE bytes read is not efficient, reduce frequency
+               current_time = time(NULL);
+               if ( current_time - start_time > p->rectime || p->done ) {
+                       break;
+               }
+       }
+       error:
+       p->done = true;
+       return NULL;
+}
+
+void * record_write(void * priv) {
+       ssize_t rt = 0, wt, shift;
+       char* buf = malloc(DVB_WRITE_BUFFER_SIZE);
+       my_thread_arg* p = (my_thread_arg*)priv;
+
+       while ( 1 ) {
+               if ( mrb_get(&p->rb, buf, DVB_WRITE_BUFFER_SIZE) ) {
+                       // sufficient buffer
+                       rt = DVB_WRITE_BUFFER_SIZE;
+               }
+               else if ( p->done && mrb_data_len(&p->rb) > 0) {
+                       // insufficient buffer, but no more buffer fill
+                       rt = mrb_data_len(&p->rb);
+                       mrb_get(&p->rb, buf, mrb_data_len(&p->rb));
+               }
+               else if ( p->done ) {
+                       // normal exit
+                       break;
+               }
+               else {
+                       usleep(100);
+               }
+
                shift = 0;
-               while ( wt = write(fout, buf + shift, rt) > 0) {
+               while ( ( wt = write(p->fout, buf + shift, rt) ) > 0) {
+                       //printf("write %d %d\n", p->fout, rt);
                        rt -= wt;
                        if ( rt == 0 ) break;
                        shift += wt;
                }
                if ( rt > 0 ) {
                        // [buf] is not correctly written to [fout]
-                       printf("output file write failed\n");
+                       printf("output file write failed. err=%d\n", errno);
                        goto error;
                }
-
-               // TODO: time() at each 1316 bytes read is not efficient, reduce frequency
-               current_time = time(NULL);
-               if ( current_time - start_time > rectime ) {
-                       break;
-               }
        }
-
        error:
-       close(fin);
-       close(fout);
+       p->done = true;
+       return NULL;
 }
 
 int main(int argc, char *argv[]) {
        int adapter_nr;
        int channel_freq;
+       int channel_phys;
        int channel_id;
        int fd;
        int ret;
        int rectime;
 
-       if (argc <= 2) {
-               fprintf(stderr, "Usage: %s adapter_nr freq [tsid]\n", argv[0]);
+       if (argc != 6) {
+               fprintf(stderr, "Usage  : %s adapter_nr channel tsid rectime output\n", argv[0]);
+               fprintf(stderr, "    channel can be freqency or channel(TE1/BS1/CS1)\n");
+               fprintf(stderr, "Version: 0.0.2\n");
                return 1;
        }
        adapter_nr = strtol(argv[1], NULL, 0);
        channel_freq = strtol(argv[2], NULL, 10);
-       channel_id = rectime = 0;
-       if ( argc >= 4 ){
-               channel_id = strtol(argv[3], NULL, 10);
-       }
-       if ( argc >= 5 ) {
-               rectime = atoi(argv[4]);
+       channel_id = strtol(argv[3], NULL, 10);
+       rectime = atoi(argv[4]);
+
+       if ( channel_freq == 0 ) {
+               channel_phys = atoi(argv[2] + 2);
+               if ( toupper(*argv[2]) == 'T' && toupper(*(argv[2] + 1)) == 'E' && channel_phys != 0 ) {
+                       channel_freq = ( 473 + (channel_phys - 13) * 6 ) * 1000000 + 142857;
+               }
+               else if ( toupper(*argv[2]) == 'B' && toupper(*(argv[2] + 1)) == 'S' && channel_phys != 0 ) {
+                       channel_freq = (channel_phys - 1) * 38360 / 2 + 1049480;
+               }
+               else if ( toupper(*argv[2]) == 'C' && toupper(*(argv[2] + 1)) == 'S' && channel_phys != 0 ) {
+                       channel_freq = (channel_phys - 2) * 40000 / 2 + 1613000;
+               }
+               fprintf(stderr, "channel_freq = %d\n", channel_freq);
        }
+
        fd = search(adapter_nr, channel_freq, channel_id);
        if (fd < 0)
                return 1;