Exemple #1
0
/* this function will be reader thread */
void *
reader_func(void *p)
{
    thread_data *tdata = (thread_data *)p;
    QUEUE_T *p_queue = tdata->queue;
    decoder *dec = tdata->decoder;
    splitter *splitter = tdata->splitter;
    int wfd = tdata->wfd;
    boolean use_b25 = dec ? TRUE : FALSE;
    boolean use_udp = tdata->sock_data ? TRUE : FALSE;
    boolean fileless = FALSE;
    boolean use_splitter = splitter ? TRUE : FALSE;
    int sfd = -1;
    pthread_t signal_thread = tdata->signal_thread;
//    struct sockaddr_in *addr = NULL;
    BUFSZ *qbuf;
    static splitbuf_t splitbuf;
    ARIB_STD_B25_BUFFER sbuf, dbuf, buf;
    int code;
    int split_select_finish = TSS_ERROR;

    buf.size = 0;
    buf.data = NULL;
    splitbuf.buffer_size = 0;
    splitbuf.buffer = NULL;

    if(wfd == -1)
        fileless = TRUE;

    if(use_udp) {
        sfd = tdata->sock_data->sfd;
//        addr = &tdata->sock_data->addr;
    }

    while(1) {
        ssize_t wc = 0;
        int file_err = 0;
        qbuf = dequeue(p_queue);
        /* no entry in the queue */
        if(qbuf == NULL) {
            break;
        }

        sbuf.data = qbuf->buffer;
        sbuf.size = qbuf->size;

        buf = sbuf; /* default */

        if(use_b25) {
            code = b25_decode(dec, &sbuf, &dbuf);
            if(code < 0) {
                fprintf(stderr, "b25_decode failed (code=%d). fall back to encrypted recording.\n", code);
                use_b25 = FALSE;
            }
            else
                buf = dbuf;
        }


        if(use_splitter) {
            splitbuf.buffer_filled = 0;

            /* allocate split buffer */
            if(splitbuf.buffer_size < buf.size && buf.size > 0) {
                splitbuf.buffer = realloc(splitbuf.buffer, buf.size);
                if(splitbuf.buffer == NULL) {
                    fprintf(stderr, "split buffer allocation failed\n");
                    use_splitter = FALSE;
                    goto fin;
                }
            }

            while(buf.size) {
                /* 分離対象PIDの抽出 */
                if(split_select_finish != TSS_SUCCESS) {
                    split_select_finish = split_select(splitter, &buf);
                    if(split_select_finish == TSS_NULL) {
                        /* mallocエラー発生 */
                        fprintf(stderr, "split_select malloc failed¥n");
                        use_splitter = FALSE;
                        goto fin;
                    }
                    else if(split_select_finish != TSS_SUCCESS) {
                        /* 分離対象PIDが完全に抽出できるまで出力しない
                         * 1秒程度余裕を見るといいかも
                         */
                        time_t cur_time;
                        time(&cur_time);
                        if(cur_time - tdata->start_time > 4) {
                            use_splitter = FALSE;
                            goto fin;
                        }
                        break;
                    }
                }

                /* 分離対象以外をふるい落とす */
                code = split_ts(splitter, &buf, &splitbuf);
                if(code == TSS_NULL) {
                    fprintf(stderr, "PMT reading..¥n");
                }
                else if(code != TSS_SUCCESS) {
                    fprintf(stderr, "split_ts failed¥n");
                    break;
                }

                break;
            } /* while */

            buf.size = splitbuf.buffer_filled;
            buf.data = splitbuf.buffer;
        fin:
            ;
        } /* if */


        if(!fileless) {
            /* write data to output file */
            int size_remain = buf.size;
            int offset = 0;

            while(size_remain > 0) {
                int ws = size_remain < SIZE_CHANK ? size_remain : SIZE_CHANK;

                wc = write(wfd, buf.data + offset, ws);
                if(wc < 0) {
                    perror("write");
                    file_err = 1;
                    pthread_kill(signal_thread,
                                 errno == EPIPE ? SIGPIPE : SIGUSR2);
                    break;
                }
                size_remain -= wc;
                offset += wc;
            }
        }

        if(use_udp && sfd != -1) {
            /* write data to socket */
            int size_remain = buf.size;
            int offset = 0;
            while(size_remain > 0) {
                int ws = size_remain < SIZE_CHANK ? size_remain : SIZE_CHANK;
                wc = write(sfd, buf.data + offset, ws);
                if(wc < 0) {
                    if(errno == EPIPE)
                        pthread_kill(signal_thread, SIGPIPE);
                    break;
                }
                size_remain -= wc;
                offset += wc;
            }
        }

        free(qbuf);
        qbuf = NULL;

        /* normal exit */
        if((f_exit && !p_queue->num_used) || file_err) {

            buf = sbuf; /* default */

            if(use_b25) {
                code = b25_finish(dec, &sbuf, &dbuf);
                if(code < 0)
                    fprintf(stderr, "b25_finish failed¥n");
                else
                    buf = dbuf;
            }

            if(use_splitter) {
                /* 分離対象以外をふるい落とす */
                code = split_ts(splitter, &buf, &splitbuf);
                if(code == TSS_NULL) {
                    split_select_finish = TSS_ERROR;
                    fprintf(stderr, "PMT reading..¥n");
                }
                else if(code != TSS_SUCCESS) {
                    fprintf(stderr, "split_ts failed¥n");
                    break;
                }

                buf.data = splitbuf.buffer;
                buf.size = splitbuf.buffer_size;
            }

            if(!fileless && !file_err) {
                wc = write(wfd, buf.data, buf.size);
                if(wc < 0) {
                    perror("write");
                    file_err = 1;
                    pthread_kill(signal_thread,
                                 errno == EPIPE ? SIGPIPE : SIGUSR2);
                }
            }

            if(use_udp && sfd != -1) {
                wc = write(sfd, buf.data, buf.size);
                if(wc < 0) {
                    if(errno == EPIPE)
                        pthread_kill(signal_thread, SIGPIPE);
                }
            }

            if(use_splitter) {
                free(splitbuf.buffer);
                splitbuf.buffer = NULL;
                splitbuf.buffer_size = 0;
            }

            break;
        }
    }

    time_t cur_time;
    time(&cur_time);
    fprintf(stderr, "Recorded %dsec¥n",
            (int)(cur_time - tdata->start_time));

    return NULL;
}
Exemple #2
0
/** main */
int
main(int argc, char *argv[])
{
	Args args = parseOption(argc, argv);
	// 正常終了時戻り値
	int result = 0;
	boost::scoped_ptr<Recordable> tuner(NULL);
	timeval tv_start;
#ifdef UDP
	Udp udp;
#endif /* defined(UDP) */
#ifdef HTTP
	int dest = 1; // stdout
	int connected_socket = 0;
	int listening_socket = 0;
#endif /* defined(HTTP) */
	
	// 引数確認
	if (!args.forever && args.recsec <= 0) {
		std::cerr << "recsec must be (recsec > 0)." << std::endl;
		exit(1);
	}
	
	// 録画時間の基準開始時間
	time_t time_start = time(NULL);
	
	// ログ出力先設定
	std::ostream& log = args.stdout ? std::cerr : std::cout;

#ifdef HTTP
	if( !args.http_mode ){
		// 出力先ファイルオープン
		if(!args.stdout) {
			dest = open(args.destfile, (O_RDWR | O_CREAT | O_TRUNC), 0666);
			if (0 > dest) {
				std::cerr << "can't open file '" << args.destfile << "' to write." << std::endl;
				exit(1);
			}
		}
	}else{
		struct sockaddr_in	sin;
		int					sock_optval = 1;
		int					ret;

		fprintf(stderr, "run as a daemon..\n");
		if(daemon(1,1)){
			perror("failed to start");
			exit(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(args.http_port);
		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", args.http_port);
	}

	while(1){
		if ( args.http_mode ) {
			struct sockaddr_in	peer_sin;
			int					read_size;
			unsigned int		len;
			char				buffer[256];
			char				s0[256],s1[256],s2[256];
			char				delim[] = "/";
			char				*channel;
			char				*sidflg;

			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);

			read_size = read_line(connected_socket, buffer);
			fprintf(stderr, "request command is %s\n", buffer);
			// ex:GET /C8/333 HTTP/1.1
			sscanf(buffer, "%s%s%s", s0, s1, s2);
			channel = strtok(s1, delim);
			if (channel != NULL) {
				fprintf(stderr, "Channel: %s\n", channel);
				parseChannel(&args, channel);
				sidflg = strtok(NULL, delim);
				if (sidflg != NULL) {
					fprintf(stderr, "SID: %s\n", sidflg);
#ifdef TSSL
					args.splitter = true;
					args.sid_list = sidflg;
				} else {
					args.splitter = false;
					args.sid_list = NULL;
#endif /* defined(TSSL) */
				}
			}
			char header[] =  "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nCache-Control: no-cache\r\n\r\n";
			write(connected_socket, header, strlen(header));
			//set write target to http
			dest = connected_socket;
		}
#endif /* defined(HTTP) */

#ifdef B25
	// B25初期化
	B25Decoder b25dec;
	if (args.b25) {
		try {
			b25dec.setRound(args.round);
			b25dec.setStrip(args.strip);
			b25dec.setEmmProcess(args.emm);
			b25dec.open();
			log << "B25Decoder initialized." << std::endl;
		} catch (b25_error& e) {
			std::cerr << e.what() << std::endl;
			
#ifdef HTTP
			if (!args.http_mode) {
#endif /* defined(HTTP) */
			// エラー時b25を行わず処理続行。終了ステータス1
			std::cerr << "disable b25 decoding." << std::endl;
			args.b25 = false;
			result = 1;
#ifdef HTTP
			}
#endif /* defined(HTTP) */
		}
	}
#endif /* defined(B25) */

#ifdef UDP
	// UDP初期化
	if( ! args.ip.empty() ){
		try{
			udp.setLog(&log);
			udp.init( args.ip, args.port );
		}
		catch( const char* e ){
			log << e << std::endl;
			log << "disable UDP." << std::endl;
		}
	}
#endif /* defined(UDP) */

#ifdef TSSL
	/* initialize splitter */
	splitbuf_t splitbuf;
	splitbuf.size = 0;
	splitter *splitter = NULL;
	int split_select_finish = TSS_ERROR;
	int code;
	if(args.splitter) {
		splitter = split_startup(args.sid_list);
		if(splitter->sid_list == NULL) {
			fprintf(stderr, "Cannot start TS splitter\n");
			return 1;
		}
	}
#endif /* defined(TSSL) */

	// Tuner取得
	tuner.reset(createRecordable(args.type));
#ifdef HDUS
	if( args.type == TUNER_HDUS ) log << "Tuner type is HDUS." << std::endl;
	else if( args.type == TUNER_HDP ) log << "Tuner type is HDP." << std::endl;
#endif /* defined(HDUS) */
	// ログ出力先設定
	tuner->setLog(&log);
	// ロックファイル設定
	if (args.lockfile != NULL) {
		tuner->setDetectLockFile(args.lockfile);
	}
	
	// Tuner初期化
	int retryCount = ERROR_RETRY_MAX;
	while (0 < retryCount) {
		try {
			// チューナopen
			bool r = tuner->open(args.lnb);
			if (!r) {
				std::cerr << "can't open tuner." << std::endl;
				exit(1);
			}
			
			// チャンネル設定
			tuner->setChannel(args.band, args.channel);
			
			// 開始時SignalLevel出力
			float lev_before = 0.0;
			int lev_retry_count = SIGNALLEVEL_RETRY_MAX;
			while (lev_before < SIGNALLEVEL_RETRY_THRESHOLD && 0 < lev_retry_count) {
				lev_before = tuner->getSignalLevel();
				log << "Signal level: " << lev_before << std::endl;
				
				lev_retry_count--;
				usleep(SIGNALLEVEL_RETRY_INTERVAL * 1000);
			}
		} catch (usb_error& e) {
			// リトライ処理
			retryCount--;
			std::cerr << e.what();
			if (retryCount <= 0) {
				std::cerr << " abort." << std::endl;
				exit(1);
			}
			std::cerr << " retry." << std::endl;
			
			tuner->close();
			usleep(ERROR_RETRY_INTERVAL * 1000);
			continue;
		}
		break;
	}
	
#ifndef HTTP
	// 出力先ファイルオープン
	FILE *dest = stdout;
	if (!args.stdout) {
		dest = fopen(args.destfile, "w");
		if (NULL == dest) {
			std::cerr << "can't open file '" << args.destfile << "' to write." << std::endl;
			exit(1);
		}
	}
#endif /* !defined(HTTP) */
	
	// 出力開始/時間計測
	log << "Output ts file." << std::endl;
	if (gettimeofday(&tv_start, NULL) < 0) {
		std::cerr << "gettimeofday failed." << std::endl;
		exit(1);
	}
	
	// SIGINT/SIGTERMキャッチ
	struct sigaction sa;
	memset(&sa, 0, sizeof(struct sigaction));
	sa.sa_handler = sighandler;
	sa.sa_flags = SA_RESTART;
	struct sigaction saDefault;
	memset(&saDefault, 0, sizeof(struct sigaction));
	saDefault.sa_handler = SIG_DFL;
	sigaction(SIGINT,  &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGPIPE, &sa, NULL);
	
	uint8_t		*buf = NULL;
	int			rlen;
	
	// 受信スレッド起動
	tuner->startStream();
	// データ読み出し
	uint32_t urb_error_cnt = 0;
	while (!caughtSignal && (args.forever || time(NULL) <= time_start + args.recsec)) {
		try {
			rlen = tuner->getStream((const uint8_t **)&buf, 200);
			if (0 == rlen) {
				continue;
			}
			
#ifdef B25
			// B25を経由させる。
			if (args.b25) {
				static int f_b25_sync = 0;
				try {
					uint8_t *b25buf;
					b25dec.put(buf, rlen);
					rlen = b25dec.get((const uint8_t **)&b25buf);
					if (0 == rlen) {
						continue;
					}
					f_b25_sync = 1;
					buf = b25buf;
				} catch (b25_error& e) {
					if( f_b25_sync == 0 && args.sync ){
						log << "Wait for B25 sync" << std::endl;
						continue;
					}
					log << "B25 Error: " << e.what() << std::endl;
					log << "Continue recording without B25." << std::endl;
#ifdef HTTP
					if (!args.http_mode) {
#endif /* defined(HTTP) */
					// b25停止、戻り値エラー
					args.b25 = false;
					result = 1;
#ifdef HTTP
					}
#endif /* defined(HTTP) */
				}
			}
#endif /* defined(B25) */

#ifdef TSSL
			if (args.splitter) {
				splitbuf.size = 0;
				while (rlen) {
					/* 分離対象PIDの抽出 */
					if (split_select_finish != TSS_SUCCESS) {
						split_select_finish = split_select(splitter, buf, rlen);
						if (split_select_finish == TSS_NULL) {
							/* mallocエラー発生 */
							log << "split_select malloc failed" << std::endl;
							args.splitter = false;
							result = 1;
							goto fin;
						} else if (split_select_finish != TSS_SUCCESS) {
							// 分離対象PIDが完全に抽出できるまで出力しない
							// 1秒程度余裕を見るといいかも
							time_t cur_time;
							time(&cur_time);
							if (cur_time - time_start > 4) {
								args.splitter = false;
								result = 1;
								goto fin;
							}
							break;
						}
					}
					/* 分離対象以外をふるい落とす */
					code = split_ts(splitter, buf, rlen, &splitbuf);
					if (code != TSS_SUCCESS) {
						log << "split_ts failed" << std::endl;
						break;
					}
					break;
				}

				rlen = splitbuf.size;
				buf = splitbuf.buffer;
			fin:
				;
			}
#endif /* defined(TSSL) */

#ifdef UDP
			// UDP 配信
			udp.send(buf, rlen);
#endif /* defined(UDP) */

#ifdef HTTP
			while(rlen > 0) {
				ssize_t wc;
				int ws = rlen < SIZE_CHUNK ? rlen : SIZE_CHUNK;
				while(ws > 0) {
					wc = write(dest, buf, ws);
					if(wc < 0) {
						log << "write failed." << std::endl;
						rlen = 0;
						buf = NULL;
						break;
					}
					ws -= wc;
					rlen -= wc;
					buf += wc;
				}
			}
#else
			fwrite(buf, 1, rlen, dest);
#endif /* defined(HTTP) */

		} catch (usb_error& e) {
			if (urb_error_cnt <= URB_ERROR_MAX) {
				log << e.what() << std::endl;
				if (urb_error_cnt == URB_ERROR_MAX) {
					log << "Too many URB error." << std::endl;
				}
				urb_error_cnt++;
			}
		}
	}
	if (caughtSignal) {
#ifdef HTTP
		if( args.http_mode )
			caughtSignal = false;
		else
#endif /* defined(HTTP) */
		log << "interrupted." << std::endl;
	}
	// 受信スレッド停止
	tuner->stopStream();
	
	// シグナルハンドラを戻す。
	sigaction(SIGINT,  &saDefault, NULL);
	sigaction(SIGTERM, &saDefault, NULL);
	sigaction(SIGPIPE, &saDefault, NULL);

	rlen = 0;
	buf = NULL;

#ifdef B25
	// B25デコーダ内のデータを出力する。
	if (args.b25) {
		try {
			b25dec.flush();
			rlen = b25dec.get((const uint8_t **)&buf);
		} catch (b25_error& e) {
			log << "B25 Error: " << e.what() << std::endl;
			result = 1;
		}
	}
#endif /* defined(B25) */
#ifdef TSSL
	if (args.splitter) {
		splitbuf.size = 0;
		while (rlen) {
			/* 分離対象PIDの抽出 */
			if (split_select_finish != TSS_SUCCESS) {
				split_select_finish = split_select(splitter, buf, rlen);
				if (split_select_finish == TSS_NULL) {
					/* mallocエラー発生 */
					log << "split_select malloc failed" << std::endl;
					args.splitter = false;
					result = 1;
					break;
				} else if (split_select_finish != TSS_SUCCESS) {
					// 分離対象PIDが完全に抽出できるまで出力しない
					// 1秒程度余裕を見るといいかも
					time_t cur_time;
					time(&cur_time);
					if (cur_time - time_start > 4) {
						args.splitter = false;
						result = 1;
					}
					break;
				}
			}
			/* 分離対象以外をふるい落とす */
			code = split_ts(splitter, buf, rlen, &splitbuf);
			if (code != TSS_SUCCESS) {
				log << "split_ts failed" << std::endl;
				break;
			}
			break;
		}
		rlen = splitbuf.size;
		buf = splitbuf.buffer;
		split_shutdown(splitter);
	}
#endif /* defined(TSSL) */
#ifdef HTTP
		while(rlen > 0) {
			ssize_t wc;
			int ws = rlen < SIZE_CHUNK ? rlen : SIZE_CHUNK;
			while(ws > 0) {
				wc = write(dest, buf, ws);
				if(wc < 0) {
					log << "write failed." << std::endl;
					rlen = 0;
					buf = NULL;
					break;
				}
				ws -= wc;
				rlen -= wc;
				buf += wc;
			}
		}
		if( args.http_mode ){
			/* close http socket */
			close(dest);
			fprintf(stderr,"connection closed. still listening at port %d\n", args.http_port);
		}else
			break;
	}
#else
	if (0 < rlen) {
		fwrite(buf, 1, rlen, dest);
	}
#endif /* defined(HTTP) */

	// 時間計測
	timeval tv_end;
	if (gettimeofday(&tv_end, NULL) < 0) {
		err(1, "gettimeofday failed.");
	}
	
	// 出力先ファイルクローズ
#ifdef HTTP
	if (!args.stdout) {
		close(dest);
	}
#else
	fflush(dest);
	if (!args.stdout) {
		fclose(dest);
	}
#endif /* defined(HTTP) */
	log << "done." << std::endl;
	
#ifdef UDP
	// UDP クローズ
	udp.shutdown();
#endif /* defined(UDP) */
	
	// 録画時間出力
	timeval rec_time;
	timersub(&tv_end, &tv_start, &rec_time);
	log << "Rec time: " << rec_time.tv_sec << "." << std::setfill('0') << std::setw(6) << rec_time.tv_usec << " sec." << std::endl;
	
	// 終了時SignalLevel出力
	try {
		float lev_after = tuner->getSignalLevel();
		log << "Signal level: " << lev_after << std::endl;
	} catch (usb_error& e) {
		log << e.what() << " ignored." << std::endl;
	}
	
	return result;
}