OSDN Git Service

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