OSDN Git Service

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