+#define _FILE_OFFSET_BITS 64
+
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/audio.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#ifndef DTV_STREAM_ID
+#define DTV_STREAM_ID DTV_ISDBS_TS_ID
+#endif
+#define DVB_READ_BUFFER_SIZE 188 * 128
+#define DVB_WRITE_BUFFER_SIZE 1024 * 1024
+#define DMX_BUFFER_SIZE 1024 * 1024
+#define RING_BUFFER_SIZE 1024 * 1024 * 16
+
+typedef struct {
+ void* rb_ptr;
+ int rb_wt;
+ int rb_rt;
+ int rb_ab;
+ pthread_mutex_t rb_mtx;
+} mrb;
+
+typedef struct {
+ int fin;
+ int fout;
+ int rectime;
+ mrb rb;
+ bool done;
+} my_thread_arg;
+
+void * record_read(void * priv);
+void * record_write(void * priv);
+
+// TODO: switchable between VRB(virtual ring buffer) and MRB(My Ring Buffer)
+
+void mrb_init(mrb* rb, size_t size) {
+ rb->rb_ptr = malloc(size);
+ rb->rb_wt = rb->rb_rt = rb->rb_ab = 0;
+ pthread_mutex_init(&rb->rb_mtx, NULL);
+}
+
+void mrb_destroy(mrb* rb) {
+ free(rb->rb_ptr);
+ pthread_mutex_destroy(&rb->rb_mtx);
+}
+
+size_t mrb_data_len(mrb* rb) {
+ return rb->rb_ab;
+}
+
+bool mrb_put(mrb* rb, char *source, size_t size) {
+ int partial;
+
+ if ( rb->rb_ab + size > RING_BUFFER_SIZE ) {
+ // RingBuffer overflow.
+ return false;
+ }
+ if ( rb->rb_wt + size <= RING_BUFFER_SIZE ) {
+ // written bytes + newly added bytes <= buffer max size
+ memcpy(rb->rb_ptr + rb->rb_wt, source, size);
+ rb->rb_wt = rb->rb_wt + size;
+ }
+ else {
+ // written bytes + newly added bytes > buffer max size
+ // wrap around.
+ partial = RING_BUFFER_SIZE - rb->rb_wt;
+ memcpy(rb->rb_ptr + rb->rb_wt, source, partial);
+ memcpy(rb->rb_ptr, source + partial, size - partial);
+ rb->rb_wt = rb->rb_wt + size - RING_BUFFER_SIZE;
+ }
+ pthread_mutex_lock(&rb->rb_mtx);
+ rb->rb_ab += size;
+ pthread_mutex_unlock(&rb->rb_mtx);
-struct channel {
- int id;
- const char *name;
- unsigned int frequency;
- unsigned int ts_id;
-};
-static int search(int adapter_nr,struct channel *ch)
+ return true;
+}
+
+bool mrb_get(mrb* rb, char *target, size_t size) {
+ int partial;
+
+ if ( rb->rb_ab < size ) {
+ // RingBuffer underflow.
+ return false;
+ }
+ if ( rb->rb_rt + size <= RING_BUFFER_SIZE ) {
+ // read bytes + newly taken bytes <= buffer max size
+ memcpy(target, rb->rb_ptr + rb->rb_rt, size);
+ rb->rb_rt = rb->rb_rt + size;
+ }
+ else {
+ // read bytes + newly taken bytes > buffer max size
+ // wrap around.
+ partial = RING_BUFFER_SIZE - rb->rb_rt;
+ memcpy(target, rb->rb_ptr + rb->rb_rt, partial);
+ memcpy(target + partial, rb->rb_ptr, size - partial);
+ rb->rb_rt = rb->rb_rt + size - RING_BUFFER_SIZE;
+ }
+ pthread_mutex_lock(&rb->rb_mtx);
+ rb->rb_ab -= size;
+ pthread_mutex_unlock(&rb->rb_mtx);
+
+ return true;
+}
+
+static int search(int adapter_nr, unsigned int frequency, unsigned int ts_id)
{
char file[256];
int fd;
struct dvb_frontend_info info;
- struct channel *channel;
struct dtv_property prop[3];
struct dtv_properties props;
int i;
fe_status_t status;
sprintf(file, "/dev/dvb/adapter%d/frontend0", adapter_nr);
- if ((fd = open(file, O_RDWR)) < 0) {
+ if ((fd = open(file, (O_RDWR | O_CREAT | O_TRUNC), 0666)) < 0) {
perror("open");
return -1;
}
goto out;
}
- if (info.type == FE_QPSK) {
- channel = ch;
- //channel = lookup_channel(channel_id, isdbs_channels,
- // ARRAY_SIZE(isdbs_channels));
- } else if (info.type == FE_OFDM) {
- channel = ch;
- //channel = lookup_channel(channel_id, isdbt_channels,
- // ARRAY_SIZE(isdbt_channels));
+ if (info.type == FE_QPSK || info.type == FE_OFDM) {
} else {
fprintf(stderr, "Unknown type of adapter\n");
goto out;
}
- if (channel == NULL) {
- fprintf(stderr, "Unknown id of channel\n");
- goto out;
- }
prop[0].cmd = DTV_FREQUENCY;
- prop[0].u.data = channel->frequency;
- prop[1].cmd = DTV_ISDBS_TS_ID;
- prop[1].u.data = channel->ts_id;
+ prop[0].u.data = frequency;
+ prop[1].cmd = DTV_STREAM_ID;
+ prop[1].u.data = ts_id;
prop[2].cmd = DTV_TUNE;
props.props = prop;
goto out;
}
if (status & FE_HAS_LOCK) {
- fprintf(stderr, "Successfully tuned to %d(%d) .\n",
- channel->frequency,channel->ts_id);
+ fprintf(stderr, "Successfully tuned to %d(%d).\n",
+ frequency, ts_id);
return 0;
}
usleep(250 * 1000);
}
- fprintf(stderr, "Failed to tune to %s (status %02x).\n",
- channel->name, status);
+ fprintf(stderr, "Failed to tune (status %02x).\n", status);
out:
close(fd);
return -1;
}
-
if (ioctl(fd, DMX_SET_PES_FILTER, &filter) < 0) {
perror("ioctl DMX_SET_PES_FILTER");
close(fd);
return -1;
}
+ return 0;
}
void record(int adapter_nr, char* output, int rectime) {
int fin, fout;
char input[256];
- time_t start_time, current_time;
- char buf[1316];
- ssize_t rt, wt;
- int size_remain;
+ pthread_t read_thread, write_thread;
+ my_thread_arg arg;
- fout = open(output, (O_WRONLY | O_CREAT | O_TRUNC));
+ fout = open(output, (O_WRONLY | O_CREAT | O_TRUNC), 0666);
if ( fout < 0 ) {
printf("output file open failed\n");
return;
}
sprintf(input, "/dev/dvb/adapter%d/dvr0", adapter_nr);
- start_time = time(NULL);
fin = open(input, (O_RDONLY));
- while ( rt = read(fin, buf, 1316) ) {
- while ( wt = write(fout, buf, rt) ) {
- rt -= wt;
- if ( rt == 0 ) break;
- if ( wt == 0 ) {
- printf("output file write failed\n");
+ ioctl(fin, DMX_SET_BUFFER_SIZE, DMX_BUFFER_SIZE);
+
+ mrb_init(&arg.rb, RING_BUFFER_SIZE);
+ arg.done = false;
+ arg.rectime = rectime;
+ arg.fin = fin;
+ arg.fout = fout;
+ pthread_create(&read_thread , NULL, record_read , &arg);
+ pthread_create(&write_thread, NULL, record_write, &arg);
+ pthread_join(write_thread, NULL);
+ pthread_join(read_thread, NULL);
+
+ mrb_destroy(&arg.rb);
+ close(fin);
+ close(fout);
+}
+
+void * record_read(void * priv) {
+ ssize_t rt;
+ time_t start_time, current_time;
+ char* buf = malloc(DVB_READ_BUFFER_SIZE);
+ my_thread_arg* p = (my_thread_arg*)priv;
+
+ start_time = time(NULL);
+ while ( 1 ) {
+ rt = read(p->fin, buf, DVB_READ_BUFFER_SIZE);
+ //printf("read %d %d\n", p->fin, rt);
+ if ( rt == 0 ) {
+ printf("dvr0 file read failed\n");
+ goto error;
+ }
+ if ( rt < 0 ) {
+ switch (errno) {
+ case EOVERFLOW:
+ printf("read overflow, continue\n");
+ continue;
+ default:
+ printf("read finished. err=%d\n", errno);
goto error;
}
}
+
+ mrb_put(&p->rb, buf, rt);
+
+ // TODO: time() at each DVB_READ_BUFFER_SIZE bytes read is not efficient, reduce frequency
current_time = time(NULL);
- if ( current_time - start_time > rectime ) {
+ if ( current_time - start_time > p->rectime || p->done ) {
break;
}
}
+ error:
+ p->done = true;
+ return NULL;
+}
+
+void * record_write(void * priv) {
+ ssize_t rt = 0, wt, shift;
+ char* buf = malloc(DVB_WRITE_BUFFER_SIZE);
+ my_thread_arg* p = (my_thread_arg*)priv;
+
+ while ( 1 ) {
+ if ( mrb_get(&p->rb, buf, DVB_WRITE_BUFFER_SIZE) ) {
+ // sufficient buffer
+ rt = DVB_WRITE_BUFFER_SIZE;
+ }
+ else if ( p->done && mrb_data_len(&p->rb) > 0) {
+ // insufficient buffer, but no more buffer fill
+ rt = mrb_data_len(&p->rb);
+ mrb_get(&p->rb, buf, mrb_data_len(&p->rb));
+ }
+ else if ( p->done ) {
+ // normal exit
+ break;
+ }
+ else {
+ usleep(100);
+ }
+ shift = 0;
+ while ( ( wt = write(p->fout, buf + shift, rt) ) > 0) {
+ //printf("write %d %d\n", p->fout, rt);
+ rt -= wt;
+ if ( rt == 0 ) break;
+ shift += wt;
+ }
+ if ( rt > 0 ) {
+ // [buf] is not correctly written to [fout]
+ printf("output file write failed. err=%d\n", errno);
+ goto error;
+ }
+ }
error:
- close(fin);
- close(fout);
+ p->done = true;
+ return NULL;
}
int main(int argc, char *argv[]) {
int adapter_nr;
- int channel_id;
int channel_freq;
+ int channel_phys;
+ int channel_id;
int fd;
int ret;
int rectime;
- if (argc <= 2) {
- fprintf(stderr, "Usage: %s adapter_nr freq [tsid]\n", argv[0]);
+ if (argc != 6) {
+ fprintf(stderr, "Usage : %s adapter_nr channel tsid rectime output\n", argv[0]);
+ fprintf(stderr, " channel can be freqency or channel(TE1/BS1/CS1)\n");
+ fprintf(stderr, "Version: 0.0.2\n");
return 1;
}
- struct channel *ch;
adapter_nr = strtol(argv[1], NULL, 0);
channel_freq = strtol(argv[2], NULL, 10);
- channel_id=0;
- if (argc >= 4){
- channel_id = strtol(argv[3], NULL, 10);
- }
- if ( argc == 5 ) {
- rectime = atoi(argv[4]);
- }
- struct channel ch1 ={0,"",channel_freq,channel_id};
- ch = &ch1;
- //struct channel ch;//{ 1, "NHK BS-1", 1318000, 0x40f1 }
- //ch={0,"",argv}
- fd = search(adapter_nr, ch);
+ channel_id = strtol(argv[3], NULL, 10);
+ rectime = atoi(argv[4]);
+
+ if ( channel_freq == 0 ) {
+ channel_phys = atoi(argv[2] + 2);
+ if ( toupper(*argv[2]) == 'T' && toupper(*(argv[2] + 1)) == 'E' && channel_phys != 0 ) {
+ channel_freq = ( 473 + (channel_phys - 13) * 6 ) * 1000000 + 142857;
+ }
+ else if ( toupper(*argv[2]) == 'B' && toupper(*(argv[2] + 1)) == 'S' && channel_phys != 0 ) {
+ channel_freq = (channel_phys - 1) * 38360 / 2 + 1049480;
+ }
+ else if ( toupper(*argv[2]) == 'C' && toupper(*(argv[2] + 1)) == 'S' && channel_phys != 0 ) {
+ channel_freq = (channel_phys - 2) * 40000 / 2 + 1613000;
+ }
+ fprintf(stderr, "channel_freq = %d\n", channel_freq);
+ }
+
+ fd = search(adapter_nr, channel_freq, channel_id);
if (fd < 0)
return 1;
ret = track(adapter_nr);
- record(adapter_nr, argv[3], rectime);
+ record(adapter_nr, argv[5], rectime);
close(fd);
return ret < 0;
}
+