OSDN Git Service

a30bfe952739245be241d30552f02a09ae2c8c5a
[rec10/rec10-git.git] / tunerec / tunerec.c
1 #define _FILE_OFFSET_BITS 64
2
3 #include <linux/dvb/frontend.h>
4 #include <linux/dvb/dmx.h>
5 #include <linux/dvb/audio.h>
6 #include <linux/dvb/version.h>
7 #include <sys/ioctl.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <time.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <pthread.h>
16 #include <stdbool.h>
17 #include <string.h>
18 #include <ctype.h>
19
20 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
21 #ifndef DTV_STREAM_ID
22 #define DTV_STREAM_ID DTV_ISDBS_TS_ID
23 #endif
24 #define DVB_READ_BUFFER_SIZE 188 * 128
25 #define DVB_WRITE_BUFFER_SIZE 1024 * 1024
26 #define DMX_BUFFER_SIZE 1024 * 1024
27 #define RING_BUFFER_SIZE 1024 * 1024 * 16
28
29 typedef struct {
30         void* rb_ptr;
31         int rb_wt;
32         int rb_rt;
33         int rb_ab;
34         pthread_mutex_t rb_mtx;
35 } mrb;
36
37 typedef struct {
38         int fin;
39         int fout;
40         int rectime;
41         mrb rb;
42         bool done;
43 } my_thread_arg;
44
45 void * record_read(void * priv);
46 void * record_write(void * priv);
47
48 // TODO: switchable between VRB(virtual ring buffer) and MRB(My Ring Buffer)
49
50 void mrb_init(mrb* rb, size_t size) {
51         rb->rb_ptr = malloc(size);
52         rb->rb_wt = rb->rb_rt = rb->rb_ab = 0;
53         pthread_mutex_init(&rb->rb_mtx, NULL);
54 }
55
56 void mrb_destroy(mrb* rb) {
57         free(rb->rb_ptr);
58         pthread_mutex_destroy(&rb->rb_mtx);
59 }
60
61 size_t mrb_data_len(mrb* rb) {
62         return rb->rb_ab;
63 }
64
65 bool mrb_put(mrb* rb, char *source, size_t size) {
66         int partial;
67
68         if ( rb->rb_ab + size > RING_BUFFER_SIZE ) {
69                 // RingBuffer overflow.
70                 return false;
71         }
72         if ( rb->rb_wt + size <= RING_BUFFER_SIZE ) {
73                 // written bytes + newly added bytes <= buffer max size
74                 memcpy(rb->rb_ptr + rb->rb_wt, source, size);
75                 rb->rb_wt = rb->rb_wt + size;
76         }
77         else {
78                 // written bytes + newly added bytes > buffer max size
79                 // wrap around.
80                 partial = RING_BUFFER_SIZE - rb->rb_wt;
81                 memcpy(rb->rb_ptr + rb->rb_wt, source, partial);
82                 memcpy(rb->rb_ptr, source + partial, size - partial);
83                 rb->rb_wt = rb->rb_wt + size - RING_BUFFER_SIZE;
84         }
85         pthread_mutex_lock(&rb->rb_mtx);
86         rb->rb_ab += size;
87         pthread_mutex_unlock(&rb->rb_mtx);
88
89         return true;
90 }
91
92 bool mrb_get(mrb* rb, char *target, size_t size) {
93         int partial;
94
95         if ( rb->rb_ab < size ) {
96                 // RingBuffer underflow.
97                 return false;
98         }
99         if ( rb->rb_rt + size <= RING_BUFFER_SIZE ) {
100                 // read bytes + newly taken bytes <= buffer max size
101                 memcpy(target, rb->rb_ptr + rb->rb_rt, size);
102                 rb->rb_rt = rb->rb_rt + size;
103         }
104         else {
105                 // read bytes + newly taken bytes > buffer max size
106                 // wrap around.
107                 partial = RING_BUFFER_SIZE - rb->rb_rt;
108                 memcpy(target, rb->rb_ptr + rb->rb_rt, partial);
109                 memcpy(target + partial, rb->rb_ptr, size - partial);
110                 rb->rb_rt = rb->rb_rt + size - RING_BUFFER_SIZE;
111         }
112         pthread_mutex_lock(&rb->rb_mtx);
113         rb->rb_ab -= size;
114         pthread_mutex_unlock(&rb->rb_mtx);
115
116         return true;
117 }
118
119 static int search(int adapter_nr, unsigned int frequency, unsigned int ts_id)
120 {
121         char file[256];
122         int fd;
123         struct dvb_frontend_info info;
124         struct dtv_property prop[3];
125         struct dtv_properties props;
126         int i;
127         fe_status_t status;
128
129         sprintf(file, "/dev/dvb/adapter%d/frontend0", adapter_nr);
130         if ((fd = open(file, (O_RDWR | O_CREAT | O_TRUNC), 0666)) < 0) {
131                 perror("open");
132                 return -1;
133         }
134
135         if (ioctl(fd, FE_GET_INFO, &info) < 0) {
136                 perror("ioctl FE_GET_INFO");
137                 goto out;
138         }
139
140         if (info.type == FE_QPSK || info.type == FE_OFDM) {
141         } else {
142                 fprintf(stderr, "Unknown type of adapter\n");
143                 goto out;
144         }
145
146         prop[0].cmd = DTV_FREQUENCY;
147         prop[0].u.data = frequency;
148         prop[1].cmd = DTV_STREAM_ID;
149         prop[1].u.data = ts_id;
150         prop[2].cmd = DTV_TUNE;
151
152         props.props = prop;
153         props.num = 3;
154
155         if ((ioctl(fd, FE_SET_PROPERTY, &props)) < 0) {
156                 perror("ioctl FE_SET_PROPERTY");
157                 goto out;
158         }
159
160         for (i = 0; i < 4; i++) {
161                 if (ioctl(fd, FE_READ_STATUS, &status) < 0) {
162                         perror("ioctl FE_READ_STATUS");
163                         goto out;
164                 }
165                 if (status & FE_HAS_LOCK) {
166                         fprintf(stderr, "Successfully tuned to %d(%d).\n",
167                                 frequency, ts_id);
168                         return 0;
169                 }
170                 usleep(250 * 1000);
171         }
172
173         fprintf(stderr, "Failed to tune (status %02x).\n", status);
174
175 out:
176         close(fd);
177         return -1;
178 }
179
180 static int track(int adapter_nr)
181 {
182         char file[256];
183         int fd;
184         struct dmx_pes_filter_params filter;
185
186         filter.pid = 0x2000;
187         filter.input = DMX_IN_FRONTEND;
188         filter.output = DMX_OUT_TS_TAP;
189         filter.pes_type =  DMX_PES_VIDEO;
190         filter.flags = DMX_IMMEDIATE_START;
191
192         sprintf(file, "/dev/dvb/adapter%d/demux0", adapter_nr);
193         if ((fd = open(file, O_RDWR)) < 0) {
194                 perror("open");
195                 return -1;
196         }
197
198         if (ioctl(fd, DMX_SET_PES_FILTER, &filter) < 0) {
199                 perror("ioctl DMX_SET_PES_FILTER");
200                 close(fd);
201                 return -1;
202         }
203         return 0;
204 }
205
206 void record(int adapter_nr, char* output, int rectime) {
207         int fin, fout;
208         char input[256];
209         pthread_t read_thread, write_thread;
210         my_thread_arg arg;
211
212         fout = open(output, (O_WRONLY | O_CREAT | O_TRUNC), 0666);
213         if ( fout < 0 ) {
214                 printf("output file open failed\n");
215                 return;
216         }
217         sprintf(input, "/dev/dvb/adapter%d/dvr0", adapter_nr);
218         fin = open(input, (O_RDONLY));
219         ioctl(fin, DMX_SET_BUFFER_SIZE, DMX_BUFFER_SIZE);
220
221         mrb_init(&arg.rb, RING_BUFFER_SIZE);
222         arg.done = false;
223         arg.rectime = rectime;
224         arg.fin  = fin;
225         arg.fout = fout;
226         pthread_create(&read_thread , NULL, record_read , &arg);
227         pthread_create(&write_thread, NULL, record_write, &arg);
228         pthread_join(write_thread, NULL);
229         pthread_join(read_thread, NULL);
230
231         mrb_destroy(&arg.rb);
232         close(fin);
233         close(fout);
234 }
235
236 void * record_read(void * priv) {
237         ssize_t rt;
238         time_t start_time, current_time;
239         char* buf = malloc(DVB_READ_BUFFER_SIZE);
240         my_thread_arg* p = (my_thread_arg*)priv;
241
242         start_time = time(NULL);
243         while ( 1 ) {
244                 rt = read(p->fin, buf, DVB_READ_BUFFER_SIZE);
245                 //printf("read %d %d\n", p->fin, rt);
246                 if ( rt == 0 ) {
247                         printf("dvr0 file read failed\n");
248                         goto error;
249                 }
250                 if ( rt < 0 ) {
251                         switch (errno) {
252                         case EOVERFLOW:
253                                 printf("read overflow, continue\n");
254                                 continue;
255                         default:
256                                 printf("read finished. err=%d\n", errno);
257                                 goto error;
258                         }
259                 }
260
261                 mrb_put(&p->rb, buf, rt);
262
263                 // TODO: time() at each DVB_READ_BUFFER_SIZE bytes read is not efficient, reduce frequency
264                 current_time = time(NULL);
265                 if ( current_time - start_time > p->rectime || p->done ) {
266                         break;
267                 }
268         }
269         error:
270         p->done = true;
271         return NULL;
272 }
273
274 void * record_write(void * priv) {
275         ssize_t rt = 0, wt, shift;
276         char* buf = malloc(DVB_WRITE_BUFFER_SIZE);
277         my_thread_arg* p = (my_thread_arg*)priv;
278
279         while ( 1 ) {
280                 if ( mrb_get(&p->rb, buf, DVB_WRITE_BUFFER_SIZE) ) {
281                         // sufficient buffer
282                         rt = DVB_WRITE_BUFFER_SIZE;
283                 }
284                 else if ( p->done && mrb_data_len(&p->rb) > 0) {
285                         // insufficient buffer, but no more buffer fill
286                         rt = mrb_data_len(&p->rb);
287                         mrb_get(&p->rb, buf, mrb_data_len(&p->rb));
288                 }
289                 else if ( p->done ) {
290                         // normal exit
291                         break;
292                 }
293                 else {
294                         usleep(100);
295                 }
296
297                 shift = 0;
298                 while ( ( wt = write(p->fout, buf + shift, rt) ) > 0) {
299                         //printf("write %d %d\n", p->fout, rt);
300                         rt -= wt;
301                         if ( rt == 0 ) break;
302                         shift += wt;
303                 }
304                 if ( rt > 0 ) {
305                         // [buf] is not correctly written to [fout]
306                         printf("output file write failed. err=%d\n", errno);
307                         goto error;
308                 }
309         }
310         error:
311         p->done = true;
312         return NULL;
313 }
314
315 int main(int argc, char *argv[]) {
316         int adapter_nr;
317         int channel_freq;
318         int channel_phys;
319         int channel_id;
320         int fd;
321         int ret;
322         int rectime;
323
324         if (argc != 6) {
325                 fprintf(stderr, "Usage  : %s adapter_nr channel tsid rectime output\n", argv[0]);
326                 fprintf(stderr, "    channel can be freqency or channel(TE1/BS1/CS1)\n");
327                 fprintf(stderr, "Version: 0.0.2\n");
328                 return 1;
329         }
330         adapter_nr = strtol(argv[1], NULL, 0);
331         channel_freq = strtol(argv[2], NULL, 10);
332         channel_id = strtol(argv[3], NULL, 10);
333         rectime = atoi(argv[4]);
334
335         if ( channel_freq == 0 ) {
336                 channel_phys = atoi(argv[2] + 2);
337                 if ( toupper(*argv[2]) == 'T' && toupper(*(argv[2] + 1)) == 'E' && channel_phys != 0 ) {
338                         channel_freq = ( 473 + (channel_phys - 13) * 6 ) * 1000000 + 142857;
339                 }
340                 else if ( toupper(*argv[2]) == 'B' && toupper(*(argv[2] + 1)) == 'S' && channel_phys != 0 ) {
341                         channel_freq = (channel_phys - 1) * 38360 / 2 + 1049480;
342                 }
343                 else if ( toupper(*argv[2]) == 'C' && toupper(*(argv[2] + 1)) == 'S' && channel_phys != 0 ) {
344                         channel_freq = (channel_phys - 2) * 40000 / 2 + 1613000;
345                 }
346                 fprintf(stderr, "channel_freq = %d\n", channel_freq);
347         }
348
349         fd = search(adapter_nr, channel_freq, channel_id);
350         if (fd < 0)
351                 return 1;
352
353         ret = track(adapter_nr);
354         record(adapter_nr, argv[5], rectime);
355         close(fd);
356
357         return ret < 0;
358 }
359