faad2ライブラリーを使用して、AACフォーマットで作成されたMP4ファイルの音声をデコード/再生するプログラムについて解説します。
MP4ファイルのメタデータを読み込むためには、faad2-2.7のインストールで入手したライブラリの中のmp4ffプロジェクトが必要となります。VisualC++NET2008以上のバージョンがあればそのままでも使えますが、今までVisualC++NET2003で解説してきたので、敢えてVisualC++NET2003で新たにプロジェクトを作成します。
まずはVisualC++NET2003を起動し、メニューから「ファイル」/「新規」/「プロジェクト」を選択して、「新規プロジェクト」ダイヤログを表示させます。テンプレートは「Win32 Project」、プロジェクト名は「libmp4ff-vc7」、「ブラウズ」ボタンを押してプロジェクトフォルダーを作成するフォルダーを「faad2-2.7」と同じフォルダーに設定し、「OK」ボタンを押します。
「アプリケーションウィザード」の「アプリケーション設定」タブにて、アプリケーション形式は「Static Library」を選択し、追加オプションの「Precompiled Header」のチェックを外し、追加サポートの「MFC」のチェックも外してから「完了」ボタンを押します。
「libmp4ff-vc7」プロジェクトが作成され、内容がソリューションエクスプローラに表示されます。
ソリューションエクスプローラで「libmp4ff-vc7」プロジェクトを右クリックして、ポップアップメニューから「追加」→「既存ファイルの追加」を選択し、「faad2-2.7フォルダー」→「commonフォルダー」→「mp4ffフォルダー」にあるすべてのファイル(CPPファイルとヘッダーファイル)をプロジェクトに追加します。
ソリューションエクスプローラで「libmp4ff-vc7」プロジェクトを右クリックして、ポップアップメニューから「プロパティ」を選択し、「構成プロパティ」→「C++」→「コード生成」タグで、「Runtime Library」の欄を「Multi-threaded Debug DLL (/MDd)」に変更します。
同じく、「構成プロパティ」→「C++」→「プリプロセッサー」タグで、「Preprocessor Definitions」の欄に「USE_TAGGING」を追加します。
修正事項
ユーザー側のヘッダーファイルで、mp4ff.hのインクルードを避けるための不具合の修正。
「mp4ff.h」の45行目、typedef struct の後に構造体名「_mp4ff_callback_t」を追加します。
/* file callback structure */
typedef struct _mp4ff_callback_t ←構造体名の追加
{
uint32_t (*read)(void *user_data, void *buffer, uint32_t length);
uint32_t (*write)(void *udata, void *buffer, uint32_t length);
uint32_t (*seek)(void *user_data, uint64_t position);
uint32_t (*truncate)(void *user_data);
void *user_data;
} mp4ff_callback_t;
mp4ファイル書き込み時に、「mp4.h」ヘッダーファイルのインクルードすることによって、データ型定義が2重定義エラーになる不具合の修正。
「mp4ff_int_types.h」の10行目から19行目までをコメントアウトして、そのすぐ後の行に以下データ型定義8行を追加します。
#ifndef _MP4FF_INT_TYPES_H_
#define _MP4FF_INT_TYPES_H_
#if defined (_WIN32)
#ifdef __MINGW32__
#include
#endif /* #ifdef __MINGW32__ */
/*typedef signed char int8_t; ←ここからコメントアウト。
typedef unsigned char uint8_t;
typedef signed short int16_t;
typedef unsigned short uint16_t;
typedef signed long int32_t;
typedef unsigned long uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;*/ ←ここまで
typedef __int64 int64_t; ←ここから追加
typedef __int32 int32_t;
typedef __int16 int16_t;
typedef __int8 int8_t;
typedef unsigned __int64 uint64_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int8 uint8_t; ←ここまで
#else
#include
#endif
#endif
カバー画像のバイナリー値が読み込めない不具合の修正。
「mp4ff.h」の119行目のすぐ後に構造体メンバー「uint32_t len;」を追加します。
/* metadata tag structure */
typedef struct
{
char *item;
char *value;
uint32_t len; ←追加する構造体メンバー
} mp4ff_tag_t;
「mp4ffint.h」の154行目のすぐ後に構造体メンバー「uint32_t len;」を追加します。
/* metadata tag structure */
typedef struct
{
char *item;
char *value;
uint32_t len; ←追加する構造体メンバー
} mp4ff_tag_t;
「mp4ffint.h」の288行目、mp4ff_tag_add_field関数のプロトタイプに、引数「int32_t len」を追加します。
/* mp4meta.c */
static int32_t mp4ff_tag_add_field(mp4ff_metadata_t *tags, const char *item, const char *value, int32_t len); ←引数を追加
static int32_t mp4ff_tag_add_field(mp4ff_metadata_t *tags, const char *item, const char *value, int32_t len)←引数追加
{
void *backup = (void *)tags->tags;
if (!item || (item && !*item) || !value) return 0;
tags->tags = (mp4ff_tag_t*)realloc(tags->tags, (tags->count+1) * sizeof(mp4ff_tag_t));
if (!tags->tags)
{
if (backup) free(backup);
return 0;
} else {
tags->tags[tags->count].item = strdup(item);
// tags->tags[tags->count].value = strdup(value); ←コメントアウト
tags->tags[tags->count].len = len; ←ここから追加
if (len >= 0) {
tags->tags[tags->count].value = malloc(len + 1);
if (tags->tags[tags->count].value != NULL) {
memcpy(tags->tags[tags->count].value, value, len);
tags->tags[tags->count].value[len] = 0;
}
}
else {
tags->tags[tags->count].value = strdup(value);
} ←ここまで
if (!tags->tags[tags->count].item || !tags->tags[tags->count].value)
{
if (!tags->tags[tags->count].item) free (tags->tags[tags->count].item);
if (!tags->tags[tags->count].value) free (tags->tags[tags->count].value);
tags->tags[tags->count].item = NULL;
tags->tags[tags->count].value = NULL;
tags->tags[tags->count].len = 0; ←新たに追加する行
return 0;
}
tags->count++;
return 1;
}
}
同じく「mp4meta.c」のmp4ff_tag_set_field関数の85行目、mp4ff_tag_add_field関数呼び出しに引数「0」を追加します。
static int32_t mp4ff_tag_set_field(mp4ff_metadata_t *tags, const char *item, const char *value)
{
unsigned int i;
if (!item || (item && !*item) || !value) return 0;
for (i = 0; i < tags->count; i++)
{
if (!stricmp(tags->tags[i].item, item))
{
free(tags->tags[i].value);
tags->tags[i].value = strdup(value);
return 1;
}
}
return mp4ff_tag_add_field(tags, item, value, 0); ←引数0を追加
}
static int32_t mp4ff_parse_tag(mp4ff_t *f, const uint8_t parent_atom_type, const int32_t size)
{
uint8_t atom_type;
uint8_t header_size = 0;
uint64_t subsize, sumsize = 0;
char * name = NULL;
char * data = NULL;
uint32_t done = 0;
uint32_t len = 0; ←新たに追加する行
while (sumsize < size)
{
uint64_t destpos;
subsize = mp4ff_atom_read_header(f, &atom_type, &header_size);
destpos = mp4ff_position(f)+subsize-header_size;
if (!done)
{
if (atom_type == ATOM_DATA)
{
mp4ff_read_char(f); /* version */
mp4ff_read_int24(f); /* flags */
mp4ff_read_int32(f); /* reserved */
/* some need special attention */
if (parent_atom_type == ATOM_GENRE2 || parent_atom_type == ATOM_TEMPO)
{
if (subsize - header_size >= 8 + 2)
{
uint16_t val = mp4ff_read_int16(f);
if (parent_atom_type == ATOM_TEMPO)
{
char temp[16];
sprintf(temp, "%.5u BPM", val);
mp4ff_tag_add_field(&(f->tags), "tempo", temp, -1); ←引数-1を追加
}
else
{
const char * temp = mp4ff_meta_index_to_genre(val);
if (temp)
{
mp4ff_tag_add_field(&(f->tags), "genre", temp, -1); ←引数-1を追加
}
}
done = 1;
}
} else if (parent_atom_type == ATOM_TRACK || parent_atom_type == ATOM_DISC) {
/* if (!done && subsize - header_size >= 8 + 8) */
/* modified by AJS */
if ( !done && (subsize - header_size) >=
(sizeof(char) + sizeof(uint8_t)*3 + sizeof(uint32_t) + /* version + flags + reserved */
// + sizeof(uint16_t) /* leading uint16_t */ ←削除
+ (parent_atom_type == ATOM_TRACK ? sizeof(uint16_t) : 0) ←新たに追加
+ sizeof(uint16_t) /* track / disc */
+ sizeof(uint16_t)) /* totaltracks / totaldiscs */
)
{
uint16_t index,total;
char temp[32];
mp4ff_read_int16(f);
index = mp4ff_read_int16(f);
total = mp4ff_read_int16(f);
/* modified by AJS */
if (parent_atom_type == ATOM_TRACK) ←新たに追加
mp4ff_read_int16(f); ←コメントアウトを外す
sprintf(temp,"%d",index);
mp4ff_tag_add_field(&(f->tags), parent_atom_type == ATOM_TRACK ? "track" : "disc",
temp, -1); ←引数-1を追加
if (total>0)
{
sprintf(temp,"%d",total);
mp4ff_tag_add_field(&(f->tags), parent_atom_type == ATOM_TRACK ? "totaltracks" : "totaldiscs",
temp, -1); ←引数-1を追加
}
done = 1;
}
} else
{
if (data) {free(data);data = NULL;}
data = mp4ff_read_string(f,(uint32_t)(subsize-(header_size+8)));
len = (uint32_t)(subsize-(header_size+8)); ←新たに追加する行
}
} else if (atom_type == ATOM_NAME) {
if (!done)
{
mp4ff_read_char(f); /* version */
mp4ff_read_int24(f); /* flags */
if (name) free(name);
name = mp4ff_read_string(f,(uint32_t)(subsize-(header_size+4)));
}
}
mp4ff_set_position(f, destpos);
sumsize += subsize;
}
}
if (data)
{
if (!done)
{
if (name == NULL) mp4ff_set_metadata_name(f, parent_atom_type, &name);
if (name) mp4ff_tag_add_field(&(f->tags), name, data, len); ←引数「len」を追加
}
free(data);
}
if (name) free(name);
return 1;
}
同じく「mp4meta.c」の365行目附近に、mp4ff_meta_find_by_name_and_return_len関数を新たに追加します。
static int32_t mp4ff_meta_find_by_name_and_return_len(const mp4ff_t *f, const char *item, char **value)
{
uint32_t i;
for (i = 0; i < f->tags.count; i++)
{
if (!stricmp(f->tags.tags[i].item, item))
{
uint32_t len = f->tags.tags[i].len;
*value = NULL;
*value = malloc(len);
if (*value != NULL) {
memcpy(*value, f->tags.tags[i].value, len);
return len;
}
}
}
*value = NULL;
/* not found */
return 0;
}
同じく「mp4meta.c」のmp4ff_meta_get_coverart関数内、458行目の行をコメントアウトして、そのすぐ後に以下追加します。
int32_t mp4ff_meta_get_coverart(const mp4ff_t *f, char **value)
{
// return mp4ff_meta_find_by_name(f, "cover", value); ←コメントアウト
return mp4ff_meta_find_by_name_and_return_len(f, "cover", value); ←追加
}
最後にビルドしてエラーがなければ成功です。