Categories: プログラム技術

Ubuntu LinuxでVLC Media Playerのオーディオデバイスを選択して再生する方法 – C言語とVLCライブラリ活用

今回は、Linuxで自作簡易プレイヤーを作成したのですが、サウンドカードの選択の仕方についてネットにあまり情報がなかったので、備忘録としてまとめてみようと思います。
実際にはQT(GUIフレームワーク)で、音楽プレイヤーを作成していたのですが、そこまで記載すると超大作となってしまうので、vlcのサウンドカード制御にしぼった形で紹介します。

動作環境

まず、家にLinuxを動かせる環境がないので、Windows上で仮想環境を入れてLinuxを動かしています。
仮想環境とは、Window上で仮想的に他のOSを動かせるようにするものです。
今回はUbuntuをインストールして使用しています。

ホストWindows11
仮想環境VirtualBox 6.1.32 r149290
LinuxUbuntu 18.04 LTS

VirtualBoxについて興味のある方は、無料で使用できますので、以下からダウンロードして使ってみてください。

Oracle VM VirtualBox

プログラミングするための前準備

ここでは、実際にプログラミングをするための前準備について記載します。
具体的には、必要なライブラリのインストールです。

まずは、仮想環境からUbuntuを起動してください。
※仮想環境のインストールおよびUbuntuのインストールについては割愛します。
起動できたら、ターミナルを起動してください。
ターミナルの起動方法は以下です。

ターミナルが起動できたら以下の通り順に入力してください。

これは、上2段がUbuntu自体のアップデートで、2段がvlcmediaplayerおよびライブラリのインストールです。

$sudo apt-get update
$sudo apt-get upgrade
$sudo apt-get install vlc
$sudo apt-get install libvlc-dev
※$は入力不要です。

あとはC言語をビルドするために以下2つも実行してインストールしてください。

$sudo apt-get gcc
$sudo apt-get make

これで準備は完了です。

※Ubuntuの環境によって、もっと必要となるものがあるかもしれませんが、ご了承ください。

プログラミング

まずはプログラム全体とMakefileをお見せします。
Makefileとは、プログラムをコンパイルするレシピみたいなものです。
今回は、vlcというライブラリだけロードしています。
※その他も色々ありますが、基本的なものしかありませんので調べてみてください。

Makefile

CC            = gcc
CFLAGS        = -O4 -Wall -I/usr/local/include
DEST          = /usr/local/bin
LDFLAGS       = -L/usr/local/lib
LIBS          = -lvlc
OBJS          = vlc_test.o
PROGRAM       = vlc_test

all:            $(PROGRAM)

$(PROGRAM): $(OBJS)
   $(CC) $(OBJS) $(LDFLAGS) $(LIBS) -o $(PROGRAM)

clean:; rm -f *.o *~ $(PROGRAM)

install: $(PROGRAM)
   install -s $(PROGRAM) $(DEST)

vlc_test.c

#include "stdio.h"
#include "string.h"
#include <unistd.h>
#include "vlc/vlc.h"

libvlc_audio_output_t *serch_and_getaudiocard(libvlc_instance_t *p_instance)
{
    libvlc_audio_output_t * list = libvlc_audio_output_list_get ( p_instance );

    if( list != NULL )
    {
        while(1)
        {   
            if( list->p_next != NULL )
            {/* 次デバイスがあれば */                if( strcmp(list->psz_name,"alsa") == 0)
                {
                    break;
                }
                else
                {
                    list = list->p_next;
                }
            }
            else
            {/* 終端 */                break;                
            }
        }
    }
    return list;
}

int main(void)
{
    const char * const vlc_args[] = {
              "--verbose=2", //be much more verbose then normal for debugging purpose
    };

    libvlc_instance_t* instance;
    libvlc_media_player_t *media_player;
    libvlc_audio_output_t *audiooutput;
    libvlc_media_t* media;
    libvlc_audio_output_device_t *audioDevices;

    /* インスタンスの生成 */    instance = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
    audiooutput = serch_and_getaudiocard(instance);

    audioDevices = libvlc_audio_output_device_list_get(instance, audiooutput->psz_name);

    if( audioDevices != NULL )
    {
        while(1)
        {
            if( audioDevices->p_next != NULL )
            {   
                if( strcmp(audioDevices->psz_description,"Intel 82801AA-ICH, Intel 82801AA-ICH Default Audio Device")==0 )

                {
                    break;
                }
                else{
                    audioDevices = audioDevices->p_next;
                }
            }
            else
            {
                break;
            }
        }   
    } 
    /* プレイヤーの生成 */    media = libvlc_media_new_path(instance, "/home/xxx/デスクトップ/test.mp3"); 
    media_player = libvlc_media_player_new_from_media(media);

    /* オーディオデバイスの設定 */    libvlc_audio_output_set(media_player, audiooutput->psz_name );            /* ALSAに設定 */    libvlc_audio_output_device_set(media_player,NULL, audioDevices->psz_device );  /* デバイスの設定 */    
    libvlc_media_player_play(media_player);

    sleep(100);
}

続いてはプログラムの解説ですが、解説は次ページにて記載します。

プログラム解説

サウンドカードを選択して音声を再生するまでの流れは以下の通りです。

  1. vlcプレイヤーのインスタンスを取得
  2. 使用できるAudio情報を取得
  3. 使用するオーディオカードを取得
  4. 使用するオーディオデバイスを取得
  5. 再生パスを指定してプレイヤーを生成
  6. オーディオデバイスを設定
  7. 再生

1.vlcプレイヤーのインスタンスを取得

以下がインスタンスを取得している部分となります。
libvlc_newというAPIを呼べばインスタンスを取得できます。
ここで与えているvlc_argsは、オプションの設定です。
近騎亜はログを出力するためにverboseというオプションを2に設定してあります。

   const char * const vlc_args[] = {
              "--verbose=2", //be much more verbose then normal for debugging purpose
    };

    libvlc_instance_t* instance;

    /* インスタンスの生成 */    instance = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);

2.使用できるAudio情報を取得

次は、Audioカード情報の取得です。
こちらは、libvlc_audio_output_t *serch_and_getaudiocard(libvlc_instance_t *p_instance)関数の以下の部分で取得しています。引数に、1で取得したインスタンスを与える必要があります。
これで使用できるサウンドカードの情報を取得できます。

 libvlc_audio_output_t * list = libvlc_audio_output_list_get ( p_instance );

3.使用するオーディオカードを取得

以下が、オーディオカードの一覧から使用するデバイスを取得するプログラムとなります。
2で取得した使用可能なオーディオカード一覧から使用するデバイスをサーチしています。
psz_nameという要素に名前が格納されていますので、そちらを文字列でサーチします。
こちらは、配列ではなくリスト構造となっていますので、次のデバイスに行くときは、p_nextに移動していけばサーチできます。
終端はNULLになっているので、NULLを見つければサーチ終了とすればよいです。
ちなみに今回は、ALSAのカードをサーチ対象にしています。

ALSA ・・・ Advanced Linux Sound Architectureの略で、Linux用のサウンドシステムを提供するためのソフトウェアのことです。


libvlc_audio_output_t *serch_and_getaudiocard(libvlc_instance_t *p_instance)
{
    libvlc_audio_output_t * list = libvlc_audio_output_list_get ( p_instance );

    if( list != NULL )
    {
        while(1)
        {   
            if( list->p_next != NULL )
            {/* 次デバイスがあれば */                if( strcmp(list->psz_name,"alsa") == 0)
                {
                    break;
                }
                else
                {
                    list = list->p_next;
                }
            }
            else
            {/* 終端 */                break;                
            }
        }
    }
    return list;
}

4.使用するオーディオデバイスを取得

次はオーディオデバイスを取得する方法です。
まず、libvlc_audio_output_device_list_getを呼び出して、オーディオデバイスの一覧を取得します。
この時に引数として、1で取得したインスタンスと3で取得したオーディオデバイスを与えます。
これで、オーディオデバイスの一覧が取得できるので、あとは3と同じような方法でデバイスをサーチします。
ただし、今回は、psz_descriptionという要素を文字列で検索します。

    audioDevices = libvlc_audio_output_device_list_get(instance, audiooutput->psz_name);

    if( audioDevices != NULL )
    {
        while(1)
        {
            if( audioDevices->p_next != NULL )
            {   
                if( strcmp(audioDevices->psz_description,"Intel 82801AA-ICH, Intel 82801AA-ICH Default Audio Device")==0 )

                {
                    break;
                }
                else{
                    audioDevices = audioDevices->p_next;
                }
            }
            else
            {
                break;
            }
        }   
    } 

psz_descriptionで何を検索すれば分からないという方は、実際にVlcMediaPlayerを起動してそこから名前を調べてみてください。

5.再生パスを指定してプレイヤーを生成

以下が再生ファイルの指定とプレイヤーの生成部分となります。
libvlc_media_new_pathというAPIに1で生成したインスタンスとファイルパスを指定します。
それで生成されたオブジェクトをlibvlc_media_player_new_from_mediaに指定するとプレイヤーの実態が生成されます。

    /* プレイヤーの生成 */    media = libvlc_media_new_path(instance, "/home/xxx/デスクトップ/test.mp3"); 
    media_player = libvlc_media_player_new_from_media(media);

6.オーディオデバイスを設定

つついては、3・4で取得した情報をプレイヤーに与えて、オーディオデバイスを指定します。
libvlc_audio_output_set及びlibvlc_audio_output_device_setで、それぞれオーディオカードおよびデバイスの指定を行います。
引数には5で生成したプレイヤー情報とカード名(psz_name)とデバイス情報(psz_device)を指定します。

    /* オーディオデバイスの設定 */    libvlc_audio_output_set(media_player, audiooutput->psz_name );
    /* デバイスの設定 */    libvlc_audio_output_device_set(media_player,NULL, audioDevices->psz_device );

7.再生

最後は以下で再生です。
5で生成したプレイヤー情報を指定して、libvlc_media_player_playを呼び出せば、指定したオーディオデバイスで音声が再生されます。

  libvlc_media_player_play(media_player);

まとめ

今回は、Vlc Media Playerでのオーディオデバイスの指定の方法について記載してきましたが、意外と簡単にできるもんだなと思いました。
ただ、やっている人が少ないのか、インターネット上にあまり情報がなく色々調べながら手探りでやってみて何とかできたという感じでした。
同じように困っている方がいればと思い今回記事にしてみましたので、ぜひ参考にしてみてください!!

追記 2022/3/23

サウンドカードの指定についてですが、プレイヤーの作りにもよりますが、停止->再生を繰り返していると、何かの拍子に別のサウンドカードがある場合に別のサウンドカードに切り替わってしまうことがあるようです。
※私の場合は、裏で別の音源をGstreamerで再生することをしているので、これが主たる要因だとは思います。
ですので、プレイヤーの再生を行う時には毎回 サウンドカードを指定した方がよさそうです。
私は、再生時間とプログレスを表示するために1秒間隔でコールバックがくるようにしているのですが、この時にも念のためにサウンドカードの再設定を実行しています。
これで勝手に切り替わってしまう問題は解消しました。

Gstreamer とは、マルチメディア用のフレームワークのことで、よくLinuxの動画や音声の再生・カメラ動画の再生なんかに使われています。

にいやん

出身 : 関西 居住区 : 関西 職業 : 組み込み機器エンジニア (エンジニア歴13年) 年齢 : 38歳(2022年11月現在) 最近 業務の効率化で噂もありPython言語に興味を持ち勉強しています。 そこで学んだことを記事にして皆さんとシェアさせていただければと思いブログをはじめました!! 興味ある記事があれば皆さん見ていってください!! にほんブログ村