OSDN Git Service

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