LoginSignup
1
3

More than 5 years have passed since last update.

Ubuntu16.04(kernel4.4)でPT2動作

Last updated at Posted at 2016-11-27

ic card readerのinstall

$ apt-get install pcscd pcsc-tools

$ pcsc_scan
    :
Japanese Chijou Digital B-CAS Card (pay TV)

dvb版PT2ドライバを削除

$ lsmod | grep pt1
dvb_core              122880  1 earth_pt1

$ rmmod earth_pt1

自動ロードを禁止

/etc/modprobe.d/blacklist.conf
blacklist earth_pt1

mercurial (hg) のinstall

$ apt-get install mercurial

driverの取得とビルド

$ hg clone http://hg.honeyplanet.jp/pt1.oyama/ pt1_driver_oyama
# あるいは $ wget http://hg.honeyplanet.jp/pt1.oyama/archive/tip.tar.bz2

arib25フォルダを復活させる

$ hg revert -r c44e16dbb0e2 --all
$ hg revert -r tip --all

kernel4.2以降はvmalloc.hがio.hから呼ばれないので,パッチをあてる.

driver/pt1_pci.c
--- pt1_pci.c.org       2016-11-27 19:07:47.986658497 +0900
+++ pt1_pci.c   2016-11-27 19:13:06.821699691 +0900
@@ -15,4 +15,7 @@
 #include <asm/uaccess.h>
 #include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,2,0)
+  #include <linux/vmalloc.h>
+#endif
 #include <linux/mutex.h>
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)

ドライバ/arib25をインストール

$ cd driver
$ make
$ make install
$ modprobe pt1_drv
$ lsmod | grep pt1_drv

$ apt-get install libpcsclite-dev

$ cd ../arib25/
$ make
$ make install

※DKMSを使ってkernel version upの際に自動ビルドする方法は こちら参照

recpt1をリンク時にinlineの参照がないと怒られるので,inlineを削除.

src/ushare.c
-inline void
+/*inline*/ void
 display_headers (void)
src/trace.c
-inline void
+/*inline*/ void
 start_log (void)

関連モジュールインストールして,recpt1をビルド

$ apt-get install ffmpeg libavformat-dev libdlna-dev libupnp-dev
$ cd ..
$ ./configure  --with-b25-dir=/usr/local --enable-b25
$ cd src
$ make
$ make install

信号をチェックして録画をチェック
(channelは src/channels/sample.recpt1-channels-* あたりを参考に)

$ checksignal 21
device = /dev/pt1video2
C/N = 31.414479dB^C

$ recpt1 --b25 --strip 21 10 test.mpg
enable B25 strip
pid = 27975
device = /dev/pt1video2
Recording...
Recorded 10sec

channel割り当て (@千葉)

BTN CH 放送局名
1 27 NHK総合
2 26 NHK教育
3 30 千葉テレビ
4 25 日テレ
5 24 TV Asahi
6 22 TBS
7 23 TV Tokyo
8 21 Fuji TV
9 16 Tokyo MX1
11 28 放送大学

http patchをあてる

(参考) recpt1のhttp版パッチを適用してみた

$ recpt1 --b25 --strip --sid hd --http 8888

VLCからは http://サーバのIPアドレス:ポート番号/チャネル番号 を指定して接続する.

systemdに登録するためサービスを記述して登録 (参考)
※recpt1はforkするので, type=forking にしないとうまく動作しないので注意
((参考)Systemd入門(4) - serviceタイプUnitの設定ファイル)

/etc/systemd/system/recpt1-http.service
[Unit]
Description = recpt1-http server
After = network.target

[Service]
ExecStart = /usr/local/bin/recpt1 --b25 --strip --sid hd --http 8888
ExecStop = /usr/bin/pkill recpt1
Restart = no
Type = forking

[Install]
WantedBy = multi-user.target
$ sytemctl list-unit-files --type=services | grep recpt1
kuma-recpt1-http.service                   disabled

# 自動起動on
$ sudo systemctl enable hello
# 起動
$ sudo systemctl start hello

パッチはこちら

src/recpt1.c
--- recpt1.c.org    2016-11-27 21:52:51.502723260 +0900
+++ recpt1.c    2016-11-27 21:51:17.520258656 +0900
@@ -22,6 +22,8 @@
 #include <netinet/in.h>

 #include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
 #include <pt1_ioctl.h>

 #include "config.h"
@@ -869,7 +871,7 @@
 show_usage(char *cmd)
 {
 #ifdef HAVE_LIBARIB25
-    fprintf(stderr, "Usage: \n%s [--b25 [--round N] [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] [--es filename_suffix] [--start_time YYYYMMDDHHMISS] [--dlna] channel rectime destfile\n", cmd);
+    fprintf(stderr, "Usage: \n%s [--b25 [--round N] [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--http portnumber] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] [--es filename_suffix] [--start_time YYYYMMDDHHMISS] [--dlna] channel rectime destfile\n", cmd);
 #else
     fprintf(stderr, "Usage: \n%s [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] [--es filename_suffix] [--start_time YYYYMMDDHHMISS] [--dlna] channel rectime destfile\n", cmd);
 #endif
@@ -892,6 +894,7 @@
     fprintf(stderr, "--udp:               Turn on udp broadcasting\n");
     fprintf(stderr, "  --addr hostname:   Hostname or address to connect\n");
     fprintf(stderr, "  --port portnumber: Port number to connect\n");
+    fprintf(stderr, "--http portnumber:   Turn on http broadcasting (run as a daemon)\n");
     fprintf(stderr, "--device devicefile: Specify devicefile to use\n");
     fprintf(stderr, "--lnb voltage:       Specify LNB voltage (0, 11, 15)\n");
     fprintf(stderr, "--sid SID1,SID2,...: Specify SID number in CSV format (101,102,...)\n");
@@ -1033,6 +1036,34 @@
     return 0; /* success */
 }

+//read 1st line from socket
+int read_line(int socket, char *p){
+    int len = 0;
+    while (1){
+        int ret;
+        ret = read(socket, p, 1);
+        if ( ret == -1 ){
+            perror("read");
+            // Connection reset by peer
+            if ( errno == 104 ){
+                return -1;
+            }
+            exit(1);
+        } else if ( ret == 0 ){
+            break;
+        }
+        if ( *p == '\n' ){
+            p++;
+            len++;
+            break;
+        }
+        p++;
+        len++;
+    }
+    *p = '\0';
+    return len;
+}
+

 int
 main(int argc, char **argv)
@@ -1074,6 +1105,7 @@
         { "udp",       0, NULL, 'u'},
         { "addr",      1, NULL, 'a'},
         { "port",      1, NULL, 'p'},
+        { "http",      1, NULL, 'H'},
         { "device",    1, NULL, 'd'},
         { "help",      0, NULL, 'h'},
         { "version",   0, NULL, 'v'},
@@ -1090,6 +1122,7 @@

     boolean use_b25 = FALSE;
     boolean use_udp = FALSE;
+    boolean use_http = FALSE;
     boolean fileless = FALSE;
     boolean use_stdout = FALSE;
     boolean use_splitter = FALSE;
@@ -1097,6 +1130,7 @@
     boolean use_dlna = FALSE;
     char *host_to = NULL;
     int port_to = 1234;
+    int port_http = 12345;
     sock_data *sockdata = NULL;
     char *device = NULL;
     int val;
@@ -1105,7 +1139,7 @@
     char *es_name_prefix = NULL;
     char *start_time = NULL;

-    while((result = getopt_long(argc, argv, "br:smn:ua:p:d:hvli:y:c",
+    while((result = getopt_long(argc, argv, "br:smn:ua:H:p:d:hvli:y:c",
                                 long_options, &option_index)) != -1) {
         switch(result) {
         case 'b':
@@ -1125,6 +1159,11 @@
             host_to = "localhost";
             fprintf(stderr, "enable UDP broadcasting\n");
             break;
+        case 'H':
+            use_http = TRUE;
+            port_http = atoi(optarg);
+            fprintf(stderr, "creating a http daemon\n");
+            break;
         case 'h':
             fprintf(stderr, "\n");
             show_usage(argv[0]);
@@ -1195,6 +1234,234 @@
         }
     }

+    //http broadcasting
+    if(use_http){
+        fprintf(stderr, "run as a daemon..\n");
+        if(daemon(1,1)){
+            perror("failed to start");
+            exit(1);
+        }
+        fprintf(stderr, "pid = %d\n", getpid());
+
+        int connected_socket, listening_socket;
+        struct sockaddr_in sin;
+        unsigned int len;
+        int ret;
+        int sock_optval = 1;
+
+        listening_socket = socket(AF_INET, SOCK_STREAM, 0);
+        if ( listening_socket == -1 ){
+            perror("socket");
+            exit(1);
+        }
+
+        if ( setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR,
+            &sock_optval, sizeof(sock_optval)) == -1 ){
+            perror("setsockopt");
+            exit(1);
+        }
+
+        sin.sin_family = AF_INET;
+        sin.sin_port = htons(port_http);
+        sin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+        if ( bind(listening_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0 ){
+            perror("bind");
+            exit(1);
+        }
+
+        ret = listen(listening_socket, SOMAXCONN);
+        if ( ret == -1 ){
+            perror("listen");
+            exit(1);
+        }
+        fprintf(stderr,"listening at port %d\n", port_http);
+
+        /* initialize decoder */
+        if(use_b25) {
+            dec = b25_startup(&dopt);
+            if(!dec) {
+                fprintf(stderr, "Cannot start b25 decoder\n");
+                fprintf(stderr, "Fall back to encrypted recording\n");
+                use_b25 = FALSE;
+            }
+        }
+        //set rectime to the infinite
+        if(parse_time("-",&tdata) != 0){
+            return 1;
+        }
+
+        while(1){
+            struct hostent *peer_host;
+            struct sockaddr_in peer_sin;
+
+            len = sizeof(peer_sin);
+
+            connected_socket = accept(listening_socket, (struct sockaddr *)&peer_sin, &len);
+            if ( connected_socket == -1 ){
+                perror("accept");
+                exit(1);
+            }
+
+            int error;
+            char hbuf[NI_MAXHOST], nhbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+            error =  getnameinfo((struct sockaddr *)&peer_sin, sizeof(peer_sin), hbuf, sizeof(hbuf), NULL, 0, 0);
+            if(error) {
+                fprintf(stderr, "getnameinfo(): %s\n", gai_strerror(error));
+                exit(1);
+            }
+            error =  getnameinfo((struct sockaddr *)&peer_sin, sizeof(peer_sin), nhbuf, sizeof(nhbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+            if(error) {
+                fprintf(stderr, "getnameinfo(): %s\n", gai_strerror(error));
+                exit(1);
+            }
+            fprintf(stderr,"connect from: %s [%s] port %s\n", hbuf, nhbuf, sbuf);
+
+            int read_size;
+
+            char buf[256];
+            read_size = read_line(connected_socket, buf);
+        if ( read_size != -1 ){
+            fprintf(stderr,"request command is %s\n",buf);
+            char s0[256],s1[256],s2[256];
+            sscanf(buf,"%s%s%s",s0,s1,s2);
+            char delim[] = "/";
+            char *channel = strtok(s1,delim);
+            char *sidflg = strtok(NULL,delim);
+            if(sidflg)
+                sid_list = sidflg;
+            fprintf(stderr,"channel is %s\n",channel);
+
+            /* initialize splitter */
+            if(use_splitter) {
+                splitter = split_startup(sid_list);
+                if(splitter->sid_list == NULL) {
+                    fprintf(stderr, "Cannot start TS splitter\n");
+                    return 1;
+                }
+            }
+            char header[] =  "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nCache-Control: no-cache\r\n\r\n";
+            ret = write(connected_socket, header, strlen(header));
+
+            //set write target to http
+            tdata.wfd = connected_socket;
+
+            //tune
+            if(tune(channel, &tdata, device) != 0){
+                fprintf(stderr, "Tuner cannot start recording\n");
+                continue;
+            }
+            /* prepare thread data */
+            tdata.queue = p_queue;
+            tdata.decoder = dec;
+            tdata.splitter = splitter;
+            tdata.sock_data = sockdata;
+
+            /* spawn signal handler thread */
+            init_signal_handlers(&signal_thread, &tdata);
+
+            /* spawn reader thread */
+            tdata.signal_thread = signal_thread;
+            pthread_create(&reader_thread, NULL, reader_func, &tdata);
+
+            /* spawn ipc thread */
+            key_t key;
+            key = (key_t)(getpid());
+
+            if ((tdata.msqid = msgget(key, IPC_CREAT | 0666)) < 0) {
+                perror("msgget");
+            }
+            pthread_create(&ipc_thread, NULL, mq_recv, &tdata);
+
+            /* start recording */
+            if(ioctl(tdata.tfd, START_REC, 0) < 0) {
+                fprintf(stderr, "Tuner cannot start recording\n");
+                exit(1);
+            }
+
+            fprintf(stderr, "Broadcasting...\n");
+
+            time(&tdata.start_time);
+
+            /* read from tuner */
+            while(1) {
+                if(f_exit)
+                    break;
+
+                time(&cur_time);
+                bufptr = malloc(sizeof(BUFSZ));
+                if(!bufptr) {
+                    f_exit = TRUE;
+                    break;
+                }
+                bufptr->size = read(tdata.tfd, bufptr->buffer, MAX_READ_SIZE);
+                if(bufptr->size <= 0) {
+                    if((cur_time - tdata.start_time) >= tdata.recsec && !tdata.indefinite) {
+                        f_exit = TRUE;
+                        enqueue(p_queue, NULL);
+                        break;
+                    }
+                    else {
+                        continue;
+                    }
+                }
+                enqueue(p_queue, bufptr);
+
+                /* stop recording */
+                time(&cur_time);
+                if((cur_time - tdata.start_time) >= tdata.recsec && !tdata.indefinite) {
+                    ioctl(tdata.tfd, STOP_REC, 0);
+                    /* read remaining data */
+                    while(1) {
+                        bufptr = malloc(sizeof(BUFSZ));
+                        if(!bufptr) {
+                            f_exit = TRUE;
+                            break;
+                        }
+                        bufptr->size = read(tdata.tfd, bufptr->buffer, MAX_READ_SIZE);
+                        if(bufptr->size <= 0) {
+                            f_exit = TRUE;
+                            enqueue(p_queue, NULL);
+                            break;
+                        }
+                        enqueue(p_queue, bufptr);
+                    }
+                    break;
+                }
+            }
+
+            /* delete message queue*/
+            msgctl(tdata.msqid, IPC_RMID, NULL);
+
+            pthread_kill(signal_thread, SIGUSR1);
+
+            /* wait for threads */
+            pthread_join(reader_thread, NULL);
+            pthread_join(signal_thread, NULL);
+            pthread_join(ipc_thread, NULL);
+
+            /* close tuner */
+            if(close_tuner(&tdata) != 0)
+                return 1;
+
+            //reset queue
+            destroy_queue(p_queue);
+            p_queue = create_queue(MAX_QUEUE);
+
+            /* close splitter */
+            if(use_splitter) {
+                split_shutdown(splitter);
+            }
+ 
+        }
+            /* close http socket */
+            close(tdata.wfd);
+
+            fprintf(stderr,"connection closed. still listening at port %d\n",port_http);
+            f_exit = FALSE;
+        }
+    }
+
     if(argc - optind < 3) {
         if(argc - optind == 2 &&
               (use_udp|use_dlna|use_esout) ) {

さらにhttpの際のpidを /tmp/recpt1.pid に記録するように修正して,PIDFileを設定し,Restart=always にすると,落ちた時自動でsystemdで再起動される.

src/recpt1.c
        fprintf(stderr, "pid = %d\n", getpid());
+       FILE* fp = fopen("/tmp/recpt1.pid", "w");
+       fprintf(fp, "%d\n", getpid());
+       fclose(fp);
/etc/systemd/system/recpt1-http.service
[Unit]
Description = recpt1-http server
After = network.target

[Service]
ExecStart = /usr/local/bin/recpt1 --b25 --strip --sid hd --http 8888
ExecStop = /usr/bin/pkill recpt1
ExeStopPost = /bin/rm /tmp/recpt1.pid
Restart = always
Type = forking
PIDFile = /tmp/recpt1.pid
#PrivateTmp = true

[Install]
WantedBy = multi-user.target

PrivateTmpを有効にすると,外部からアクセスできないので無効にする.
(PIDFileで指定しても見つからない)

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3