Digital broadcast recording system for Japan. This middleware is a tiny and minimal implementation.
Recording, EPG acquisition, keyword search recording reservation are the main functions.
(+ Real-time viewing function (in browser by HLS).)
Simple to build and use.
For self studying MPEG-2 TS and ARIB.
- Tuning
- Terrestrial digital only.
- Simultaneous selection by multiple tuners.
- Channel scan
- Record and schedule recording
- Simultaneous recording with multiple tuners.
- Event tracking.
- After obtaining the EPG, search for the registered keyword and schedule recording.
- Introduced tssplitter_lite.
- EPG
- Perform scheduled EPG acquisition.
- Viewing (*Platform dependent)
- View broadcasts in real time on your browser using ffmpeg HLS.
- Simultaneous viewing with multiple tuners.
- Introduced tssplitter_lite.
- Broadcaster logo download
- Save what fell during song selection in PNG format.
- Since palette information (CLUT common fixed color) is added, it can be viewed as it is. (It should be possible.)
- Command Line Interface (CLI)
- Connect to the
command serverwithtelnetornetcat, etc., and execute commands that access each function.
- Connect to the
flowchart TB
cmdline(["command line"]):::ext
kwjson(["keyword json"]):::ext
webcli(["web UI (browser)"]):::ext
subgraph PROC["auto_rec_mini process"]
direction TB
CS["Command Server"]:::mod
HTTP["http server<br/>(non-module thread)"]:::thread
subgraph APP["application modules"]
direction LR
ES["Event Search"]:::mod
ESM["Event Schedule Manager<br/>(EPG)"]:::mod
CM["Channel Manager<br/>(channel scan)"]:::mod
RM["Rec Manager<br/>(recording)"]:::mod
VM["Viewing Manager<br/>(viewing)"]:::mod
end
REC["Rec Instance x N<br/>libarib25 / libpcsclite*"]:::lib
SH["stream_handler<br/>(viewing)"]:::lib
TS["Tuner Service<br/>(tuning / tuner-resource manager)"]:::mod
subgraph TUN["tuner instance x4 (group0-3)"]
direction LR
PSI["PSI/SI Manager"]:::mod
TC["Tuner Control"]:::mod
TT["Tune Thread"]:::mod
end
end
subgraph FLEX["flexible (forked tuner processes)"]
direction LR
RECDVB["recdvb"]:::proc2
RECFS["recfsusb2i"]:::proc2
end
PCSCD["pcscd"]:::proc2
BCAS["B-CAS"]:::hw
DEV1["PX-S1UD V2.0"]:::hw
DEV2["KTV-FSUSB2/V3"]:::hw
cmdline -->|TCP/IP| CS
kwjson --> ES
webcli -->|HTTP| HTTP
HTTP --> VM
HTTP --> CM
ESM --> ES
ES --> RM
ESM --> RM
CM --> TS
ESM --> TS
RM --> TS
VM --> TS
RM --> REC
VM --> SH
REC -->|PC/SC| PCSCD
PCSCD --> BCAS
TS --> PSI
TS --> TC
TC --> TT
TT -.fork.-> RECDVB
TT -.fork.-> RECFS
RECDVB --> DEV1
RECFS --> DEV2
subgraph LEGEND["legend"]
direction LR
L1["process"]:::proc2
L2["coseq-managed module (fiber)"]:::mod
L3["non-module thread"]:::thread
end
classDef mod fill:#f8cbad,stroke:#d00,stroke-width:2px,color:#000;
classDef lib fill:#2e75b6,stroke:#1f4e79,color:#fff;
classDef ext fill:#ffff00,stroke:#b59f00,color:#000;
classDef proc2 fill:#d9d9d9,stroke:#808080,color:#000;
classDef hw fill:#bdd7ee,stroke:#2e75b6,color:#000;
classDef thread fill:#fff2cc,stroke:#bf9000,stroke-dasharray:4 2,color:#000;
Tuner Service sits at the center of this diagram. It looks after the tuner groups (0-3) and their allocation, and modules that need tuning go through it.
The Command Server accepts CLI connections (
command line) and sends operation / debug commands to every module. The per-module edges are omitted here to keep the diagram readable — see How to use CLI for the full command set.
Stream (m2ts) data flow
flowchart LR
DEV["tuner device<br/>(PX-S1UD / KTV-FSUSB2)"]:::hw
RECPROC["recdvb / recfsusb2i<br/>(forked)"]:::proc2
TT["Tune Thread"]:::mod
subgraph HANDLERS["stream receivers"]
direction TB
PSI["PSI/SI Manager"]:::mod
REC["Rec Instance<br/>libarib25 / libpcsclite*"]:::lib
SH["stream_handler<br/>(viewing)"]:::lib
end
RECFILE["recording file"]:::proc2
VIEWER["ffmpeg (HLS)"]:::proc2
DEV -->|TS| RECPROC
RECPROC -->|TS| TT
TT -->|m2ts| PSI
TT -->|m2ts| REC
TT -->|m2ts| SH
REC --> RECFILE
SH --> VIEWER
classDef mod fill:#f8cbad,stroke:#d00,stroke-width:2px,color:#000;
classDef lib fill:#2e75b6,stroke:#1f4e79,color:#fff;
classDef proc2 fill:#d9d9d9,stroke:#808080,color:#000;
classDef hw fill:#bdd7ee,stroke:#2e75b6,color:#000;
The m2ts stream from Tune Thread is delivered to the three components that receive it — PSI/SI Manager, Rec Instance, and stream_handler (viewing).
- BS/CS compatible.
- BML-related functions for data broadcasting. (Implementation of dsmcc)
- Expand the HTTP API (add more endpoints).
The supported tuners are as follows. only terrestrial digitl.
KTV-FSUSB2/V3(S/N: K1212 later)PX-S1UD V2.0
Anything that works with other PLEX tuners or recdvb should work.
Will work on generic linux destributions. (confirmed worked on Ubuntu, Fedora, Raspberry Pi OS)
I usually use Raspberry Pi3 model B to check the operation,
If the back EPG acquisition runs during recording, or if multiple simultaneous recordings are performed,
packet loss may occur, causing block noise and image skipping..
There was a case that ts could not be taken because of insufficient power at the start of tuning.
As a remedy, using a 5V4A power adapter has improved somewhat.
These will also be improved on Pi4.
Regarding the viewing function, we use ffmpeg to encode in H.264 and view it in a browser using the HLS streaming method.
H.264 assumes HW encoding on Raspberry Pi4 (h264_omx), but encoding could not keep up with the playback speed on Pi3.
It also depends on the version of ffmpeg, and operation has been confirmed with versions below 4.1.
(For versions 4.2 and above, an error will occur due to the subtitle stream.)
(On Raspberry Pi OS, when using Buster, ffmpeg 4.1 series is installed by default.)
Also using B-CAS card with IC card reader.
Depends on the following libraries.
Please install as appropriate.
libpcsclite
$ sudo apt-get install pcscd libpcsclite-dev libccid pcsc-tools
libarib25
$ sudo apt-get install cmake g++
$ git clone https://github.com/stz2012/libarib25.git
$ cd libarib25
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install
Install firmware for PLEX tuners. (isdbt_rio.inp)
$ wget http://plex-net.co.jp/plex/px-s1ud/PX-S1UD_driver_Ver.1.0.1.zip
$ unzip PX-S1UD_driver_Ver.1.0.1.zip
$ sudo cp PX-S1UD_driver_Ver.1.0.1/x64/amd64/isdbt_rio.inp /lib/firmware/
$ sync
$ sudo reboot
The code depends on several git submodules.
You need cloned the repository with --recursive.
$ git clone --recursive https://github.com/ysan/auto_rec_mini.git
$ cd auto_rec_mini
$ make
$ sudo make INSTALLDIR=/opt/auto_rec_mini install
Installation files:
/opt/auto_rec_mini/
├── bin
│ ├── auto_rec_mini
│ ├── recfsusb2i
│ └── recdvb
└── lib
├── libchannel_manager.so
├── libcommand_server.so
├── libcommon.so
├── libdsmccparser.so
├── libevent_schedule_manager.so
├── libevent_search.so
├── libparser.so
├── libpsisi_manager.so
├── libpsisiparser.so
├── librec_manager.so
├── libstream_handler.so
├── libcoseq.so
├── libcoseqpp.so
├── libtuner_control.so
├── libtuner_service.so
├── libtune_thread.so
└── libviewing_manager.so
Copy the configuration file(settings.json) to /opt/auto_rec_mini/ .
$ sudo cp settings.json /opt/auto_rec_mini
Copy the systemd unit file. (auto_rec_mini.service)
$ sudo cp auto_rec_mini.service /etc/systemd/system
To start the service for the first time, do the usual systemctl dance.
$ sudo systemctl daemon-reload
$ sudo systemctl enable auto_rec_mini.service
$ sudo systemctl start auto_rec_mini.service
auto_rec_mini process is up and ready to use the tuner.
command server is listening for connections on port 20001.
First, please execute the channel scan CLI command.
Copy the syslog configuration file. (30-auto_rec_mini.conf) and restart syslog service.
If you set is_syslog_output to true in settings.json,
you can output to /var/log/auto_rec_mini/auto_rec_mini.log.
$ sudo cp 30-auto_rec_mini.conf /etc/rsyslog.d
$ sudo systemctl restart rsyslog.service
Copy the rotation configuration file. (logrotate-auto_rec_mini)
$ sudo cp logrotate-auto_rec_mini /etc/logrotate.d
$ sudo chown root:root /etc/logrotate.d/logrotate-auto_rec_mini
$ sudo systemctl stop auto_rec_mini.service
$ sudo systemctl disable auto_rec_mini.service
$ sudo rm /etc/systemd/system/auto_rec_mini.service
$ sudo systemctl daemon-reload
$ sudo rm /etc/rsyslog.d/30-auto_rec_mini.conf
$ sudo rm /etc/logrotate.d/logrotate-auto_rec_mini
$ sudo systemctl restart rsyslog.service
$ sudo make INSTALLDIR=/opt/auto_rec_mini clean
$ make clean
settings.json is a configuration file.
Read at auto_rec_mini process startup.
There are no default values, and settings are made by reading this file.
Please you set according to the environment.
| items | descriptions |
|---|---|
is_syslog_output |
Switch whether to output log to syslog. |
command_server_port |
Listening port of command server. |
tuner_hal_allocates |
Assign the tuner hal command. |
channels_json_path |
Save/load destination of channel scan result. |
rec_reserves_json_path |
Save/load destination of recording reservation. |
rec_results_json_path |
Save/load destination of recording result. |
rec_ts_path |
Save destination of recording stream. |
rec_disk_space_low_limit_MB |
Threshold of the remaining capacity of the file system for recording. (MBytes) |
rec_use_splitter |
Toggles whether to apply tssplitter_lite to the recording stream. |
dummy_tuner_ts_path |
unused |
event_schedule_cache_is_enable |
Switch to enable EPG. |
event_schedule_cache_start_interval_day |
EPG cache execution interval date. |
event_schedule_cache_start_time |
EPG cache start time. (HH:mm) |
event_schedule_cache_timeout_min |
EPG cache timeout minute. |
event_schedule_cache_retry_interval_min |
EPG cache retry interval minute. |
event_schedule_cache_data_json_path |
Save/load EPG cached json data. |
event_schedule_cache_histories_json_path |
Save/load destination of EPG cache history. |
event_name_keywords_json_path |
Save/load destination of keywords for event name search. |
extended_event_keywords_json_path |
Save/load destination of keywords for extended event search. |
event_name_search_histories_json_path |
Save/load destination of event name search history. |
extended_event_search_histories_json_path |
Save/load destination of extended event search history. |
viewing_stream_command_format |
Write the viewing stream command. |
viewing_stream_data_path |
Path to write/read the viewing stream file. |
viewing_use_splitter |
Toggle whether to apply tssplitter_lite to the viewing stream. |
http_server_port |
listening port of the HTTP server. |
http_server_static_contents_path |
Location where Web UI content is placed and corresponds to the document root. The Web UI (client) is provided by auto_rec_mini_trials; build it there and place the output here. |
logo_path |
save/load destination of broadcaster logo png. |
Assign commands according to the tuner hardware you can use.
"tuner_hal_allocates": [
"./bin/recdvb %d - -",
"./bin/recfsusb2i --np %d - -"
],
By creating json of the following format in event_name_keywords_json_path
Search for programs whose keywords are included in the program name after EPG acquisition and
make a recording reservation.
No limit to the number of keywords.
{
"m_event_name_keywords": [
"XXXニュース",
"XXXスポーツ",
"XXX報道"
]
}
By creating json of the following format in extended_event_keywords_json_path
Search for programs whose keywords are included in the program name after EPG acquisition and
make a recording reservation.
No limit to the number of keywords.
{
"m_extended_event_keywords": [
"ワールドカップ",
"オリンピック",
"野球"
]
}
As an initial value, we have included a command to encode in H264 using ffmpeg's HLS method.
In addition to the HTTP API, auto_rec_mini provides an
interactive command shell over TCP, called the command server. It listens on the
port set by command_server_port in settings.json (default 20001).
Open another terminal and connect with netcat (or telnet):
$ nc -C 127.0.0.1 20001
### command line begin. ###
------ root tables ------
s -- system commands
tc -- tuner control
pm -- psisi manager
ts -- tuner service
rec -- rec manager
cm -- channel manager
em -- event schedule manager
es -- event search
vm -- viewing manager
/ >
| input | action |
|---|---|
<key> (e.g. cm) |
enter a module sub-menu |
<command> [args ...] |
run a command in the current menu (args are space-separated) |
. |
re-print the current menu |
.. |
go up one level (to the parent menu) |
Each command shows its usage: (arguments) in the menu listing. Log level and closing the connection are system commands under s (see the s table below); you can also disconnect with Ctrl-C.
# 1) scan channels first (needed once)
/ > cm
/channel manager > scan
# 2) live view, then stop
/ > vm
/viewing manager > ch 27 0 # ch {physical_channel} {service_idx}; prints the assigned group_id
/viewing manager > stop 0 # stop {group_id} (the id printed by ch)
# 3) record the current program of an already-tuned group
/ > rec
/rec manager > ce 0 # reserve the current event on group 0 (see note below)
/rec manager > g # list reservations
About group_id — a group_id identifies a tuner group (0-3), owned and allocated by the Tuner Service (the hub all modules converge into in the Architecture diagram). How you obtain one differs by use case:
- Viewing (
vm ch/vm svc) allocates a tuner group automatically and prints the assignedgroup_id; use that id forvm stop/vm opt. rec ce {group_id}reserves the current event, so the group must already be tuned — e.g. a running live view, a channel scan, orts op+ts t.- Scheduled reservations (
rec e/eh/m) do not need a pre-tuned group; a tuner is allocated automatically at recording time.
s - system
| command | description |
|---|---|
e |
echo from command server |
cl |
close connection |
lv {0-4} |
set log level (0:debug 1:info 2:warning 3:error 4:perror) |
ts |
status dump (no-op on coseq) |
tc - tuner control
| command | description |
|---|---|
t {freq[kHz]} {tuner_id} |
tune by frequency |
ch {physical_channel} {tuner_id} |
tune by physical channel |
stop {tuner_id} |
tune stop |
s |
get tuner state (printed to the CLI) |
ts - tuner service
| command | description |
|---|---|
op |
open tuner resource |
cl {tuner_id} |
close tuner resource |
t {physical_channel} {tuner_id} |
tune by physical channel |
stop {tuner_id} |
tune stop |
d |
dump tuner resources to the log |
pm - psisi manager
| command | description |
|---|---|
st {tuner_id} |
set target tuner id |
dp |
dump program infos to the log |
ds |
dump service infos to the log |
de |
dump event present/following infos to the log |
dn |
dump network info to the log |
t |
PSI/SI table dump sub-menu (see below) |
The t sub-menu dumps individual PSI/SI tables to the log:
| command | description |
|---|---|
pat |
dump PAT |
pmt |
dump PMT |
eit_pf |
dump EIT (p/f) |
eit_pf_s |
dump EIT (p/f) simple |
eit_sch |
dump EIT (schedule) |
eit_sched_e |
dump EIT (schedule) event |
eit_sched_s |
dump EIT (schedule) simple |
nit |
dump NIT |
sdt |
dump SDT |
cat |
dump CAT |
cdt |
dump CDT |
rst |
dump RST |
bit |
dump BIT |
rec - rec manager
| command | description |
|---|---|
ce {group_id} |
add reserve - current event |
e {tsid} {org_nid} {svcid} {evtid} {repeat} |
add reserve - event (repeat: 0 none, 1 auto) |
eh {index} {repeat} |
add reserve - event by index (helper) |
m {tsid} {org_nid} {svcid} {start} {end} {repeat} |
add reserve - manual (time yyyyMMddHHmmss; repeat 0 none, 1 daily, 2 weekly) |
r {tsid} {org_nid} {svcid} {evtid} {consider_repeat} |
remove reserve (consider_repeat: 0 false, 1 true) |
rx {index} {consider_repeat} |
remove reserve by index |
g |
get reserves (list printed to the CLI) |
stop {group_id} |
force stop recording |
dr |
dump reserves to the log |
drl |
dump recording results to the log |
drec |
dump currently-recording entries to the log |
cm - channel manager
| command | description |
|---|---|
scan |
channel scan |
t |
tune (interactive mode) |
stop {group_id} |
tune stop |
d |
dump channels to the log |
em - event schedule manager (EPG)
| command | description |
|---|---|
cs |
cache schedule (EPG) |
csc {group_id} |
cache schedule - force current service |
stop |
stop cache schedule |
dm |
dump schedule map to the log |
d {tsid} {org_nid} {svcid} |
dump schedule to the log |
di |
dump schedule to the log (interactive mode) |
ded {index} |
dump event detail to the log (from last dumped schedule) |
grep {keyword} |
search events by event name (results to the CLI) |
grepx {keyword} |
search events by event name + extended info (results to the CLI) |
dr |
dump reserves to the log |
h |
dump histories to the log |
es - event search
| command | description |
|---|---|
r |
add rec reserve (event-name keyword search) |
rx |
add rec reserve (extended-event keyword search) |
h |
dump histories to the log (event-name keyword) |
hx |
dump histories to the log (extended-event keyword) |
vm - viewing manager
| command | description |
|---|---|
ch {physical_channel} {service_idx} |
start viewing by physical channel |
svc {tsid} {org_nid} {svcid} |
start viewing by service id |
stop {group_id} |
force stop viewing |
opt {group_id} {auto_stop_grace_period_sec} |
set option |
d |
dump to the log |
The HTTP API is the JSON interface used by the Web UI. The server listens on
http_server_port in settings.json (default 8080); POST requests require the
header Content-Type: application/json.
| method | endpoint | description | request | response |
|---|---|---|---|---|
POST |
/api/ctrl/viewing/start_by_physical_channel |
start live viewing by physical channel number | body: { "physical_channel", "service_idx", "option"?: { "auto_stop_grace_period_sec" } } |
{ "group_id" } |
POST |
/api/ctrl/viewing/start_by_service_id |
start live viewing by service (TS / network / service id) | body: { "transport_stream_id", "original_network_id", "service_id", "option"?: { "auto_stop_grace_period_sec" } } |
{ "group_id" } |
POST |
/api/ctrl/viewing/stop |
stop live viewing for a group | body: { "group_id" } |
200 OK |
POST |
/api/ctrl/viewing/auto_stop_grace_period |
set the auto-stop grace period (sec) for a group | body: { "group_id", "period_sec" } |
200 OK |
GET |
/api/ctrl/channel/channels |
list all scanned channels | - | array of { "physical_channel", "transport_stream_id", "original_network_id", "service_ids": [ ... ] } |
GET |
/api/ctrl/channel/original_network_name/{onid} |
get the network name for an original_network_id | path: onid |
{ "original_network_name" } |
GET |
/api/ctrl/channel/transport_stream_name/{onid}/{tsid} |
get the transport stream name | path: onid, tsid |
{ "transport_stream_name" } |
GET |
/api/ctrl/channel/service_name/{tsid}/{onid}/{svcid} |
get the service (channel) name | path: tsid, onid, svcid |
{ "service_name" } |
GET |
/api/ctrl/psisi/{group_id}/present_event/{tsid}/{onid}/{svcid} |
get the present (now-playing) event of a service | path params | { "transport_stream_id", "original_network_id", "service_id", "event_id", "start_time", "end_time", "event_name_char" } |
GET |
/api/ctrl/psisi/{group_id}/follow_event/{tsid}/{onid}/{svcid} |
get the following (next) event of a service | path params | same fields as present_event |
Notes:
start_time/end_timeare UNIX epoch seconds.- On error the server returns
400(bad request / missing field) or500(internal error). - The HTTP API is still being expanded; more endpoints will be added (TODO).
# start live view by physical channel
curl -X POST -H "Content-Type: application/json" \
-d '{"physical_channel": 27, "service_idx": 0}' \
http://localhost:8080/api/ctrl/viewing/start_by_physical_channel
# start live view by service id (with auto-stop option)
curl -X POST -H "Content-Type: application/json" \
-d '{"transport_stream_id": 32736, "original_network_id": 32736, "service_id": 1024, "option": {"auto_stop_grace_period_sec": 10}}' \
http://localhost:8080/api/ctrl/viewing/start_by_service_id
# stop viewing (group 0)
curl -X POST -H "Content-Type: application/json" \
-d '{"group_id": 0}' \
http://localhost:8080/api/ctrl/viewing/stop
# list scanned channels
curl http://localhost:8080/api/ctrl/channel/channels
# present event of a service
curl http://localhost:8080/api/ctrl/psisi/0/present_event/32736/32736/1024
Using cpp-httplib for the internal HTTP server.
Using nlohmann/json for json SerDes.
Setting value etc. Have static data by using cereal json SerDes. (no use DB.)
Using the coseq fiber-based coroutine framework for concurrency.
I have been influenced and referred to by the following repositories.
MIT