OSDN Git Service

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