始めに
libvlcでは動画などのメディアを読み込むための関数としてlibvlc_media_new_pathやlibvlc_media_new_fdなどが用意されていますが、これらはファイルやファイルディスクリプタを対象としたものでメモリ上のバイト列から直接読み込むための関数は用意されていません。すでにメモリ上に展開された動画、音楽をlibvlcで読み込むには汎用的に定義されたlibvlc_media_new_callbacksという関数を使用する必要があります。この関数の扱いについて詰まったので備忘録として記録しておきます。
環境
Ubuntu 22.04 LTS
libvlc 3.0.6
gcc 12.1.0
本題
libvlc3.0.6において関数libvlc_media_new_callbacksの宣言は次のようになっています。1
libvlc_media_t *libvlc_media_new_callbacks(libvlc_instance_t *inst,
libvlc_media_open_cb open_cb,
libvlc_media_read_cb read_cb,
libvlc_media_seek_cb seek_cb,
libvlc_media_close_cb close_cb,
void *opaque);
それぞれの引数は次のようにします。
- inst: vlcのインスタンスへのポインタ
- open_cb: メディアを開く際のコールバック関数ポインタ
- read_cb: メディアを読む際のコールバック関数ポインタ
- seek_cb: メディアをシークする際のコールバック関数ポインタ
- close_cb: メディアを閉じる際のコールバック関数ポインタ
- opaque: 読み込むメディアへのポインタ
今から4つあるそれぞれのコールバックについて解説していきますがその前に、ここから先については簡単のためにopaqueが以下の構造体へのポインタであるものとして話を進めていきます。
typedef struct {
uint64_t size; // メディアのサイズ
uint64_t pos; // メディアの読み込み位置のポジション
unsigned char *data; // メディアの本体
} my_media;
それぞれのコールバック関数の例
open_cb
int open_cb(void *opaque, void **datap, uint64_t *sizep) {
// opaque: libvlc_media_new_callbacksでのopaque
// datap: 他の関数でopaqueとして渡されるポインタのセット先
// size: mediaのサイズのセット先
mymedia *media = (media*)opaque;
*datap = opaque;
// サイズが不明な場合はUINT64_MAXをセットする
*sizep = media->size;
// posもセットしないと二回目の再生で壊れる
media->pos = 0;
// 成功時に0を、失敗時は0以外の値を返す
return 0;
}
libvlc_media_new_callbacks引数のopen_cbとしてNULLポインタを渡すとdatapにはopaqueがセットされ、mediaのサイズは不明となります。
read_cb
ssize_t read_cb(void *opaque, unsigned char *buf, size_t len) {
// opaque: open_cbにてdatapにセットされたポインタ
// buf: このポインタにlen分のmediaをコピーする
// len: bufにコピーするメディアのサイズ
my_media *media = (my_media*)opaque;
// open_cbにてサイズを指定した場合でもそれを超えるようなlenが渡されるので注意
if (len + media->pos > media->size) {
len = media->size - media->pos;
}
memcpy(buf, media->data + media->pos, len);
media->pos += len;
// 返り値は実際に読み込んだデータサイズ、0のとき終わりまでの読み込みを、-1のときエラー発生を示す
return len;
}
libvlc_media_new_callbacks引数のread_cbとしてNULLポインタを渡すことは出来ません。
seek_cb
int seek_cb(void *opaque, uint64_t offset) {
// opaque: open_cbにてdatapにセットされたポインタ
// offset: シーク先への絶対オフセット
(my_media*)opaque->pos = offset;
// 成功時に0を、失敗時に-1を返す
return 0;
}
libvlc_media_new_callbacks引数のseek_cbとしてNULLポインタを渡すとシークはサポートされなくなります。
close_cb
void close_cb(void *opaque) {
// opaque: open_cbにてdatapにセットされたポインタ
return;
}
何もしない場合、libvlc_media_new_callbacks引数のclose_cbにはNULLポインタを渡しても問題ありません。
例
以上のことを踏まえた、メモリから音楽を読み込み5秒間再生するコードの例が以下になります。NULLチェックなどいろいろやってないのであくまでも参考程度に。
#include <string.h>
#include <unistd.h>
#include <vlc/vlc.h>
typedef struct {
uint64_t size; // メディアのサイズ
uint64_t pos; // メディアの読み込み位置のオフセット
unsigned char *data; // メディアの本体
} my_media;
int open_cb(void *opaque, void **datap, uint64_t *sizep) {
// opaque: libvlc_media_new_callbacksでのopaque
// datap: 他の関数でopaqueとして渡されるポインタのセット先
// size: mediaのサイズのセット先
mymedia *media = (media*)opaque;
*datap = opaque;
// サイズが不明な場合はUINT64_MAXをセットする
*sizep = media->size;
// posもセットしないと二回目の再生で壊れる
media->pos = 0;
// 成功時に0を、失敗時は0以外の値を返す
return 0;
}
ssize_t read_cb(void *opaque, unsigned char *buf, size_t len) {
// opaque: open_cbにてdatapにセットされたポインタ
// buf: このポインタにlen分のmediaをコピーする
// len: bufにコピーするメディアのサイズ
my_media *media = (my_media*)opaque;
// open_cbにてサイズを指定した場合でもそれ以上読み込むようなlenが渡されるので注意
if (len + media->pos > media->size) {
len = media->size - media->pos;
}
memcpy(buf, media->data + media->pos, len);
media->pos += len;
// 返り値は実際に読み込んだデータサイズ、0のとき終わりまでの読み込みを、-1のときエラー発生を示す
return len;
}
int seek_cb(void *opaque, uint64_t offset) {
// opaque: open_cbにてdatapにセットされたポインタ
// offset: シーク先への絶対オフセット
((my_media*)opaque)->pos = offset;
// 成功時に0を、失敗時に-1を返す
return 0;
}
void close_cb(void *opaque) {
// opaque: open_cbにてdatapにセットされたポインタ
return;
}
int main(int argc, const char *argv[]) {
libvlc_instance_t *inst;
libvlc_media_player_t *mp;
libvlc_media_t *m;
my_media media;
inst = libvlc_new(argc, argv);
hoge(&media); // メディアをmy_media構造体に配置
m = libvlc_media_new_callbacks(inst, open_cb, read_cb, seek_cb, close_cb, &media);
mp = libvlc_media_player_new_from_media(m);
libvlc_media_release(m);
libvlc_media_player_play(mp);
sleep(5);
libvlc_media_player_stop(mp);
libvlc_media_player_release(mp);
libvlc_release(inst);
return 0;
}