テレビ視聴アプリの作成

Chinachuの録画予約機能には特に不満はないのだが、テレビ視聴機能が今一つ使い勝手が悪い。ふと、昔、自分でテレビ視聴アプリを作ったことがあったのを思い出し、コンパイルしてみたら、今でも使えるので驚いた。それは今から9年ほど前、私がプログラミングを学んで初めて作ったもので、なつかしい。

プログラムを見ると、関数に引数を渡すということを知らなかったようで、チャンネルごとに関数を作っていた。また、システムに与えるコマンドを直接、プログラムに書き込めばいいものを、わざわざスクリプトファイルを作って、それを呼び出していた。しかもコマンド1つごとにファイルを作っていた。そんなわけでプログラムがやたらと長く、ファイルが50近くもあった。

今から見ると稚拙にもほどがあると思うが、それでも何とかして作り上げてやるという熱意は感じる。それはさて、当時のプログラムに修正を加えたものが、これから作成する視聴アプリである。WindowsでTVTestなどを使っている人から見ると「なにこれ」といったレベルだが、単にテレビを視聴するだけなら十分かなと思う。

準備

①「Ubuntu 16.04 + Chinachu + PT2でテレビ」の記事に基づいて、recpt1で録画できる環境を作る(Chinachuはなくてもよい)。

②端末に以下のコマンドをコピー&ペーストして「Enter」キーを押す。

sudo apt-get -y install libgtk2.0-dev vlc

GUIツールキットとしてGTK+2を使う。

また、再生プレイヤーとしてVLCプレイヤーを使う。

③「[sudo] **** のパスワード:」でパスワードを入力して「Enter」キーを押す。

アプリの作成

①以下のプログラムをテキストエディターにコピー&ペーストし、ファイル名を「tv.c」としてデスクトップに保存する。

#include <gtk/gtk.h>

GtkWidget *table[4];

void system();

// 放送を選択する関数を定義する。
void select_broadcasting(GtkWidget *widget, gpointer data) {
    if (GPOINTER_TO_INT(data) == 0) {
        gtk_widget_show(table[1]);
        gtk_widget_hide(table[2]);
        gtk_widget_hide(table[3]);
    }
    else if (GPOINTER_TO_INT(data) == 1) {
        gtk_widget_show(table[2]);
        gtk_widget_hide(table[3]);
        gtk_widget_hide(table[1]);
    }
    else {  // GPOINTER_TO_INT(data) == 2
        gtk_widget_show(table[3]);
        gtk_widget_hide(table[1]);
        gtk_widget_hide(table[2]);
    }
}

// チャンネルを選択する関数を定義する。
void select_channel(GtkWidget *widget, gpointer data) {
    system("killall recpt1; killall cvlc");
    system("rm temp.ts");
    system(data);
    sleep(2);
    system("cvlc temp.ts &");
}

// ウィンドウを閉じたときの動作を設定する関数を定義する。
void destroy(GtkWidget *widget, gpointer data) {
    system("killall recpt1; killall cvlc");
    system("rm temp.ts");
    gtk_main_quit();
}

int main(int argc, char *argv[]) {
    GtkWidget *window;
    GtkWidget *box;
    GtkWidget *radio_button[3];
    GtkWidget *btn_gr[12], *btn_bs[12], *btn_cs[12];
    GSList *group = NULL;
    gpointer ch1_gr, ch2_gr, ch3_gr, ch4_gr, ch5_gr, ch6_gr, ch7_gr, ch8_gr,
        ch9_gr, ch10_gr, ch11_gr, ch12_gr;
    gpointer ch1_bs, ch2_bs, ch3_bs, ch4_bs, ch5_bs, ch6_bs, ch7_bs, ch8_bs,
        ch9_bs, ch10_bs, ch11_bs, ch12_bs;
    gpointer ch1_cs, ch2_cs, ch3_cs, ch4_cs, ch5_cs, ch6_cs, ch7_cs, ch8_cs,
        ch9_cs, ch10_cs, ch11_cs, ch12_cs;

    // 初期化する。
    gtk_init(&argc, &argv);

    // 地上デジタルの設定を行う。
    ch1_gr = "recpt1 --b25 --strip --sid hd 27 7200 temp.ts &";
    ch2_gr = "recpt1 --b25 --strip --sid hd 26 7200 temp.ts &";
    ch3_gr = "recpt1 --b25 --strip --sid hd 25 7200 temp.ts &";
    ch4_gr = "recpt1 --b25 --strip --sid hd 22 7200 temp.ts &";
    ch5_gr = "recpt1 --b25 --strip --sid hd 21 7200 temp.ts &";
    ch6_gr = "recpt1 --b25 --strip --sid hd 24 7200 temp.ts &";
    ch7_gr = "recpt1 --b25 --strip --sid hd 23 7200 temp.ts &";
    ch8_gr = "recpt1 --b25 --strip --sid hd 16 7200 temp.ts &";
    ch9_gr = "recpt1 --b25 --strip --sid hd 20 7200 temp.ts &";
    ch10_gr = "recpt1 --b25 --strip --sid hd 18 7200 temp.ts &";
    ch11_gr = "recpt1 --b25 --strip --sid hd 32 7200 temp.ts &";
    ch12_gr = "recpt1 --b25 --strip --sid hd 28 7200 temp.ts &";

    // BSの設定を行う。
    ch1_bs = "recpt1 --b25 --strip 101 7200 temp.ts &";
    ch2_bs = NULL;
    ch3_bs = "recpt1 --b25 --strip 103 7200 temp.ts &";
    ch4_bs = "recpt1 --b25 --strip 141 7200 temp.ts &";
    ch5_bs = "recpt1 --b25 --strip 151 7200 temp.ts &";
    ch6_bs = "recpt1 --b25 --strip 161 7200 temp.ts &";
    ch7_bs = "recpt1 --b25 --strip 171 7200 temp.ts &";
    ch8_bs = "recpt1 --b25 --strip 181 7200 temp.ts &";
    ch9_bs = NULL;
    ch10_bs = NULL;
    ch11_bs = "recpt1 --b25 --strip 211 7200 temp.ts &";
    ch12_bs = "recpt1 --b25 --strip 222 7200 temp.ts &";

    // CSの設定を行う。
    ch1_cs = NULL;
    ch2_cs = NULL;
    ch3_cs = NULL;
    ch4_cs = NULL;
    ch5_cs = NULL;
    ch6_cs = NULL;
    ch7_cs = NULL;
    ch8_cs = NULL;
    ch9_cs = NULL;
    ch10_cs = NULL;
    ch11_cs = NULL;
    ch12_cs = NULL;

    // ウィンドウを作成する。
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_size_request(window, 140, 140);
    gtk_window_set_resizable(GTK_WINDOW (window), FALSE);
    gtk_window_set_keep_above(GTK_WINDOW(window), TRUE);

    // ボックスを作成する。
    box = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(window), box);

    // 放送選択用のテーブルを作成する。
    table[0] = gtk_table_new(3, 1, FALSE);
    gtk_box_pack_start(GTK_BOX(box), table[0], FALSE, TRUE, 0);

    // 放送選択用のラジオボタンを作成する。
    radio_button[0] = gtk_radio_button_new_with_label(group, "GR");
    radio_button[1] = gtk_radio_button_new_with_label_from_widget(
        GTK_RADIO_BUTTON(radio_button[0]), "BS");
    radio_button[2] = gtk_radio_button_new_with_label_from_widget(
        GTK_RADIO_BUTTON(radio_button[0]), "CS");

    // 放送選択用のラジオボタンをテーブルに設置する。
    gtk_table_attach_defaults(GTK_TABLE(table[0]), radio_button[0],
                                        0, 1, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[0]), radio_button[1],
                                        1, 2, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[0]), radio_button[2],
                                        2, 3, 0, 1);
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(radio_button[0]), TRUE);

    // チャンネル選択用のテーブルを作成する。
    table[1] = gtk_table_new(3, 4, FALSE);
    table[2] = gtk_table_new(3, 4, FALSE);
    table[3] = gtk_table_new(3, 4, FALSE);

    // チャンネル選択用のテーブルをボックスに設置する。
    gtk_box_pack_start(GTK_BOX(box), table[1], FALSE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(box), table[2], FALSE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(box), table[3], FALSE, TRUE, 0);

    // 地デジチャンネル選択用のボタンを作成する。
    btn_gr[0] = gtk_button_new_with_label("1");
    btn_gr[1] = gtk_button_new_with_label("2");
    btn_gr[2] = gtk_button_new_with_label("3");
    btn_gr[3] = gtk_button_new_with_label("4");
    btn_gr[4] = gtk_button_new_with_label("5");
    btn_gr[5] = gtk_button_new_with_label("6");
    btn_gr[6] = gtk_button_new_with_label("7");
    btn_gr[7] = gtk_button_new_with_label("8");
    btn_gr[8] = gtk_button_new_with_label("9");
    btn_gr[9] = gtk_button_new_with_label("10");
    btn_gr[10] = gtk_button_new_with_label("11");
    btn_gr[11] = gtk_button_new_with_label("12");

    // 地デジチャンネル選択用のボタンをテーブルに設置する。
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[0], 0, 1, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[1], 1, 2, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[2], 2, 3, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[3], 0, 1, 1, 2);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[4], 1, 2, 1, 2);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[5], 2, 3, 1, 2);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[6], 0, 1, 2, 3);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[7], 1, 2, 2, 3);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[8], 2, 3, 2, 3);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[9], 0, 1, 3, 4);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[10], 1, 2, 3, 4);
    gtk_table_attach_defaults(GTK_TABLE(table[1]), btn_gr[11], 2, 3, 3, 4);

    // BSチャンネル選択用のボタンを作成する。
    btn_bs[0] = gtk_button_new_with_label("1");
    btn_bs[1] = gtk_button_new_with_label("2");
    btn_bs[2] = gtk_button_new_with_label("3");
    btn_bs[3] = gtk_button_new_with_label("4");
    btn_bs[4] = gtk_button_new_with_label("5");
    btn_bs[5] = gtk_button_new_with_label("6");
    btn_bs[6] = gtk_button_new_with_label("7");
    btn_bs[7] = gtk_button_new_with_label("8");
    btn_bs[8] = gtk_button_new_with_label("9");
    btn_bs[9] = gtk_button_new_with_label("10");
    btn_bs[10] = gtk_button_new_with_label("11");
    btn_bs[11] = gtk_button_new_with_label("12");

    // BSチャンネル選択用のボタンをデーブルに設置する。
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[0], 0, 1, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[1], 1, 2, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[2], 2, 3, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[3], 0, 1, 1, 2);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[4], 1, 2, 1, 2);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[5], 2, 3, 1, 2);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[6], 0, 1, 2, 3);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[7], 1, 2, 2, 3);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[8], 2, 3, 2, 3);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[9], 0, 1, 3, 4);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[10], 1, 2, 3, 4);
    gtk_table_attach_defaults(GTK_TABLE(table[2]), btn_bs[11], 2, 3, 3, 4);

    // CSチャンネル選択用のボタンを作成する。
    btn_cs[0] = gtk_button_new_with_label("1");
    btn_cs[1] = gtk_button_new_with_label("2");
    btn_cs[2] = gtk_button_new_with_label("3");
    btn_cs[3] = gtk_button_new_with_label("4");
    btn_cs[4] = gtk_button_new_with_label("5");
    btn_cs[5] = gtk_button_new_with_label("6");
    btn_cs[6] = gtk_button_new_with_label("7");
    btn_cs[7] = gtk_button_new_with_label("8");
    btn_cs[8] = gtk_button_new_with_label("9");
    btn_cs[9] = gtk_button_new_with_label("10");
    btn_cs[10] = gtk_button_new_with_label("11");
    btn_cs[11] = gtk_button_new_with_label("12");

    // CSチャンネル選択用のボタンをデーブルに設置する。
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[0], 0, 1, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[1], 1, 2, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[2], 2, 3, 0, 1);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[3], 0, 1, 1, 2);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[4], 1, 2, 1, 2);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[5], 2, 3, 1, 2);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[6], 0, 1, 2, 3);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[7], 1, 2, 2, 3);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[8], 2, 3, 2, 3);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[9], 0, 1, 3, 4);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[10], 1, 2, 3, 4);
    gtk_table_attach_defaults(GTK_TABLE(table[3]), btn_cs[11], 2, 3, 3, 4);

    // ラジオボタンを選択したときの動作を設定する。
    g_signal_connect(G_OBJECT(radio_button[0]), "toggled",
                              G_CALLBACK(select_broadcasting),
                              GINT_TO_POINTER(0));
    g_signal_connect(G_OBJECT(radio_button[1]), "toggled",
                              G_CALLBACK(select_broadcasting),
                              GINT_TO_POINTER(1));
    g_signal_connect(G_OBJECT(radio_button[2]), "toggled",
                              G_CALLBACK(select_broadcasting),
                              GINT_TO_POINTER(2));

    // 地デジチャンネルボタンをクリックしたときの動作を設定する。
    g_signal_connect(G_OBJECT(btn_gr[0]), "clicked",
                              G_CALLBACK(select_channel), ch1_gr);
    g_signal_connect(G_OBJECT(btn_gr[1]), "clicked",
                              G_CALLBACK(select_channel), ch2_gr);
    g_signal_connect(G_OBJECT(btn_gr[2]), "clicked",
                              G_CALLBACK(select_channel), ch3_gr);
    g_signal_connect(G_OBJECT(btn_gr[3]), "clicked",
                              G_CALLBACK(select_channel), ch4_gr);
    g_signal_connect(G_OBJECT(btn_gr[4]), "clicked",
                              G_CALLBACK(select_channel), ch5_gr);
    g_signal_connect(G_OBJECT(btn_gr[5]), "clicked",
                              G_CALLBACK(select_channel), ch6_gr);
    g_signal_connect(G_OBJECT(btn_gr[6]), "clicked",
                              G_CALLBACK(select_channel), ch7_gr);
    g_signal_connect(G_OBJECT(btn_gr[7]), "clicked",
                              G_CALLBACK(select_channel), ch8_gr);
    g_signal_connect(G_OBJECT(btn_gr[8]), "clicked",
                              G_CALLBACK(select_channel), ch9_gr);
    g_signal_connect(G_OBJECT(btn_gr[9]), "clicked",
                              G_CALLBACK(select_channel), ch10_gr);
    g_signal_connect(G_OBJECT(btn_gr[10]), "clicked",
                              G_CALLBACK(select_channel), ch11_gr);
    g_signal_connect(G_OBJECT(btn_gr[11]), "clicked",
                              G_CALLBACK(select_channel), ch12_gr);

    // BSチャンネルボタンをクリックしたときの動作を設定する。
    g_signal_connect(G_OBJECT(btn_bs[0]), "clicked",
                              G_CALLBACK(select_channel), ch1_bs);
    g_signal_connect(G_OBJECT(btn_bs[1]), "clicked",
                              G_CALLBACK(select_channel), ch2_bs);
    g_signal_connect(G_OBJECT(btn_bs[2]), "clicked",
                              G_CALLBACK(select_channel), ch3_bs);
    g_signal_connect(G_OBJECT(btn_bs[3]), "clicked",
                              G_CALLBACK(select_channel), ch4_bs);
    g_signal_connect(G_OBJECT(btn_bs[4]), "clicked",
                              G_CALLBACK(select_channel), ch5_bs);
    g_signal_connect(G_OBJECT(btn_bs[5]), "clicked",
                              G_CALLBACK(select_channel), ch6_bs);
    g_signal_connect(G_OBJECT(btn_bs[6]), "clicked",
                              G_CALLBACK(select_channel), ch7_bs);
    g_signal_connect(G_OBJECT(btn_bs[7]), "clicked",
                              G_CALLBACK(select_channel), ch8_bs);
    g_signal_connect(G_OBJECT(btn_bs[8]), "clicked",
                              G_CALLBACK(select_channel), ch9_bs);
    g_signal_connect(G_OBJECT(btn_bs[9]), "clicked",
                              G_CALLBACK(select_channel), ch10_bs);
    g_signal_connect(G_OBJECT(btn_bs[10]), "clicked",
                              G_CALLBACK(select_channel), ch11_bs);
    g_signal_connect(G_OBJECT(btn_bs[11]), "clicked",
                              G_CALLBACK(select_channel), ch12_bs);

    // CSチャンネルボタンをクリックしたときの動作を設定する。
    g_signal_connect(G_OBJECT(btn_cs[0]), "clicked",
                              G_CALLBACK(select_channel), ch1_cs);
    g_signal_connect(G_OBJECT(btn_cs[1]), "clicked",
                              G_CALLBACK(select_channel), ch2_cs);
    g_signal_connect(G_OBJECT(btn_cs[2]), "clicked",
                              G_CALLBACK(select_channel), ch3_cs);
    g_signal_connect(G_OBJECT(btn_cs[3]), "clicked",
                              G_CALLBACK(select_channel), ch4_cs);
    g_signal_connect(G_OBJECT(btn_cs[4]), "clicked",
                              G_CALLBACK(select_channel), ch5_cs);
    g_signal_connect(G_OBJECT(btn_cs[5]), "clicked",
                              G_CALLBACK(select_channel), ch6_cs);
    g_signal_connect(G_OBJECT(btn_cs[6]), "clicked",
                              G_CALLBACK(select_channel), ch7_cs);
    g_signal_connect(G_OBJECT(btn_cs[7]), "clicked",
                              G_CALLBACK(select_channel), ch8_cs);
    g_signal_connect(G_OBJECT(btn_cs[8]), "clicked",
                              G_CALLBACK(select_channel), ch9_cs);
    g_signal_connect(G_OBJECT(btn_cs[9]), "clicked",
                              G_CALLBACK(select_channel), ch10_cs);
    g_signal_connect(G_OBJECT(btn_cs[10]), "clicked",
                              G_CALLBACK(select_channel), ch11_cs);
    g_signal_connect(G_OBJECT(btn_cs[11]), "clicked",
                              G_CALLBACK(select_channel), ch12_cs);

    // ウィンドウを閉じたときの動作を設定する。
    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);

    // ウィンドウを表示する。
    gtk_widget_show_all(window);
    gtk_widget_hide(table[2]);
    gtk_widget_hide(table[3]);

    gtk_main();

    return 0;
}

②端末に以下のコマンドをコピー&ペーストして「Enter」キーを押す。

gcc -Wall -g ~/デスクトップ/tv.c -o ~/デスクトップ/tv `pkg-config --cflags gtk+-2.0` `pkg-config --libs gtk+-2.0`

デスクトップに「tv」ファイルが生成する。これが視聴アプリである。

使い方

起動

○デスクトップの「tv」ファイル(「tv.c」ファイルではない)をダブルクリックする。

視聴アプリが起動する。

視聴

①見たい放送を選択する。

地デジとBSには私が住んでいる地域に合わせた設定がしてある。CSの設定はしていない。

②見たいチャンネルをクリックする。

デスクトップに「temp.ts」ファイルが生成され、VLCプレイヤーで再生される。録画している番組をリアルタイムで再生することによって視聴の代用としているわけである。

終了

○視聴アプリの「×」ボタンをクリックする。

VLCプレイヤーも終了し、デスクトップの「temp.ts」ファイルも削除される。

(2017/01/07更新)