yumetodoの旅とプログラミングとかの記録

旅や登山の記録やプログラミング関連の話とかフリーソフト紹介とか

FFmpegAPIをVisual Studioで使えるように導入する

今回はなんとなくffmpegAPIを導入します。いや、音楽再生で真っ先に思い浮かんだのがffmpegだったもんで。え?DirectX?DxLib?知らない子ですね(ぉい)。この記事ではFFmpegをビルドするなんてことはしません。

「いや、とりあえず、仮設置だから、ね --by じゃりみち

って言っとけば逃げられる・・・のかな?

ffmpegAPIとは?

動画編集をしたり、音声編集をしたことのある人なら一度は聞いたことがあるはず、あのffmpegです。数多くのコーデックに対応しているソフトウェアです。
AviUtlの入力プラグインとして不動の地位を築きつつあるL-SMASH Worksもffmpegの力なしにはなかったかもしれません。
で、そのFFmpegを共有ライブラリ付きでコンパイルすると、プログラムから直接FFmpegAPIを利用することが可能になります。

「じゃあ、今回の目標はそこはかとなくffmpegAPIに触れるように導入するのか」だって?いいえ。

そこはかとなく
なんとなく、どことなく。特にどこがどうと言うわけではないが、雰囲気として感じられる様子などを表す表現。
--実用日本語表現辞典
さりげなく
何事もないように振る舞うさま。それらしいようすを感じさせない。
--デジタル大辞泉

「そこはかとなく」導入してはいけません、「さりげなく」導入しようと思います。大丈夫、ビルド済みのライブラリーを取ってきてコピーするだけの簡単なお仕事です(サンプルコード書くのに25時間もかかったなんて言えない)。

目標(再掲)

  • ffmpegAPIをVisual Studio 2013 Community から使えるように、Win32、x64両対応できるように導入する

環境

項目 内容
FFmpegバージョン FFmpeg git-078be09(2015/02/10)
Visual Studio Visual Studio 2013 Community for Desktop Update4
ビルド構成 Win32 or x64 | Debug or Release
OS Windows 7 Homr Premium(64bit)

inttypes.hとstdint.h

少なくともVisual Studio 2010の頃はinttypes.hはVisual Studioになく、さらに昔はstdint.hもなかったらしいです。その頃のVisual Studioをお使いの場合は下記のリンクをどうぞ。

Visual Studio 2013 communityではいずれも必要ありません

手順抜粋

ざっというと・・・というより、これだけでいい気もするけど、こんな感じ。

  1. 導入
    1. SharedとDevの32bit,64bit両方落とす
    2. inttypes.hとstdint.hをDL、配置(古いバージョンのVCのみ)
    3. Devを配置
    4. Sharedを配置
  2. Visual Studio の設定

手順詳細

注意

.7zファイルの解凍(展開)方法については解説しません。各自CubeICEや7-Zipなどを導入して解凍(展開)できるようにしておいてください。

DL

  1. SharedとDevの32bit,64bit両方落とす
    http://ffmpeg.zeranoe.com/builds/
    にアクセスし、

    • Download FFmpeg git-[最新バージョン] 32-bit Shared
    • Download FFmpeg git-[最新バージョン] 64-bit Shared
    • Download FFmpeg git-[最新バージョン] 32-bit Dev
    • Download FFmpeg git-[最新バージョン] 64-bit Dev

    をクリックして4つともDLする。編集時点git-078be09。

配置

注意

以下ライブラリーは「C:\lib\ffmpeg20150210」に入れていくものとする。適当にパスは読み替えてね。

  1. 「C:\lib\ffmpeg20150210」フォルダー(ディレクトリ)を作る。
  2. 「C:\lib\ffmpeg20150210\win32」フォルダー(ディレクトリ)を作る。
  3. 「C:\lib\ffmpeg20150210\win64」フォルダー(ディレクトリ)を作る。
  4. 「C:\lib\ffmpeg20150210\include」フォルダー(ディレクトリ)を作る。
  5. 先ほどDLした

    • FFmpeg git-[最新バージョン] 32-bit Dev
    • FFmpeg git-[最新バージョン] 64-bit Dev

    を2つとも解凍(展開)する。

  6. 32-bit Devの中身を「include」フォルダー以外すべて「C:\lib\ffmpeg20150210\win32」に入れる
  7. 64-bit Devの中身を「include」フォルダー以外すべて「C:\lib\ffmpeg20150210\win64」に入れる
  8. 64-bit Devの中の「include」の中身を「C:\lib\ffmpeg20150210\include」に入れる。いや、32bitも64bitも中身同じだけど。
  9. 先ほどDLした

    • Download FFmpeg git-[最新バージョン] 32-bit Shared
    • Download FFmpeg git-[最新バージョン] 64-bit Shared

    を2つとも解凍(展開)する。

  10. ffmpeg-[最新日付]-git-[最新バージョン]-win32-shared\bin」の中身のうち.dllを「C:\Windows\SysWOW64」に入れる。
  11. ffmpeg-[最新日付]-git-[最新バージョン]-win64-shared\bin」の中身のうち.dllを「C:\Windows\System32」に入れる。
  12. 「C:\Program Files (x86)\Microsoft Visual Studio [VCのバージョン]\VC\include」にinttypes.h、stdint.hを入れる(古いバージョンのみ)

x64構成を作る(ない人のみ)

注意

以下フツーにVisual Studioで「Win32 プロジェクト」ないし、「Win32 コンソールアプリケーション」を空のプロジェクトとして作ってあるものとします。

詳細は
http://www.buildinsider.net/small/opencv/03
の「2.1 新規プロジェクト作成」の項目に投げます。ソースファイルはまだいらない。

説明しようと思ったんだけど、ちゃんと書いてるサイトがあるのでそちらに投げます。
http://www.buildinsider.net/small/opencv/03
の「2.2 新規プロジェクト作成(64bitアプリケーションの場合)」の項目です。

Visual Studio Project設定

  1. 構成:すべての構成
    プラットフォーム:すべてのプラットフォーム
    の状態で
    構成プロパティ→C/C++→全般→追加のインクルードディレクトリに
    C:\lib\ffmpeg20150210\include
    を追加、適用を押す
  2. 構成:すべての構成
    プラットフォーム:x64
    の状態で
    構成プロパティ→リンカー→全般→追加のライブラリーディレクトリで
    C:\lib\ffmpeg20150210\win64\lib
    を追加、適用を押す
  3. 構成:すべての構成
    プラットフォーム:Win32
    の状態で
    構成プロパティ→リンカー→全般→追加のライブラリーディレクトリで
    C:\lib\ffmpeg20150210\win32\lib
    を追加、適用を押す

テンプレート化(必要なら)

ファイル→テンプレートのエクスポートで
プロジェクトテンプレート→

テンプレート名「FFmpeg-API_win32_x64_Console」
テンプレートの説明「http://freesofutotravel.blog94.fc2.com/blog-entry-16.html
アイコンのイメージ:適当に↓とか
https://lh4.googleusercontent.com/-C8ZOe8nUlr0/AAAAAAAAAAI/AAAAAAAAAAg/Vc9qrl88PEI/photo.jpg

*適当でいいです

で完了を押す。

実験

下記のコードを実行して見てください。一応、C++でなくCでもコンパイルできるようにはなっているはず。

[実行ファイル名] [動画or音声ファイルのフルパス]

のように指定します。

サンプルコード

#ifdef _MSC_VER
#if _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES != 1
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
#endif
#if _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT != 1
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1
#endif
#endif /*_MSC_VER  */
#include <stdio.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#else
#include <stdbool.h>
#define nullptr NULL
#endif  /* __cplusplus */
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#ifdef __cplusplus
}
#endif  /* __cplusplus */
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "swscale.lib")
bool my_libav_file_open(AVFormatContext **p_format_ctx, char const* file_name){
	/*
	ほぼL-SMASH Worksのlibavsmash.cのlibavsmash_open_file関数。
	*/
	char error_string[96] = { 0 };
	av_register_all();
	avcodec_register_all();
	if (avformat_open_input(p_format_ctx, file_name, NULL, NULL))
	{
		strcpy(error_string, "Failed to avformat_open_input.\n");
		goto open_fail;
	}
	if (avformat_find_stream_info(*p_format_ctx, NULL) < 0)
	{
		strcpy(error_string, "Failed to avformat_find_stream_info.\n");
		goto open_fail;
	}
	return true;
open_fail:
	if (*p_format_ctx)
		avformat_close_input(p_format_ctx);
	fprintf(stderr, "%s", error_string);
	return false;
}
char* codec_type_string(enum AVMediaType type)
{
	switch (type) {
	case AVMEDIA_TYPE_UNKNOWN:
		return "Unknown";
	case AVMEDIA_TYPE_VIDEO:
		return "Video";
	case AVMEDIA_TYPE_AUDIO:
		return "Audio";
	case AVMEDIA_TYPE_DATA:
		return "Data";
	case AVMEDIA_TYPE_SUBTITLE:
		return "Subtitle";
	case AVMEDIA_TYPE_ATTACHMENT:
		return "Attachment";
	case AVMEDIA_TYPE_NB:
		return "NB";
	default:
		return "Undefined";
	}
}
int main(int argc, char *argv[]) {
	char* file_name;
	if (2 != argc){
        fprintf(stderr, "%s", "Invalid input");
		return -1;
	}
	else{
		file_name = argv[1];
	}
	AVFormatContext *format_ctx = nullptr;//必ずNULLで初期化、じゃないとメモリーリークが起こる
	if (false == my_libav_file_open(&format_ctx, file_name)) return -1;
	unsigned int i;
	for (i = 0; i < format_ctx->nb_streams; i++){
		printf("stream #%d\n", i);
		printf("\tcodec type: %s\n", codec_type_string(format_ctx->streams[i]->codec->codec_type));
		enum AVCodecID codec_id = format_ctx->streams[i]->codec->codec_id;
		AVCodec *codec = avcodec_find_decoder(codec_id);
		if (codec) {
			printf("\tcodec name: %s\n", codec->name);
		}
		else {
			printf("\tcodec id %d is not found\n", codec_id);
		}
	}
	if (nullptr != format_ctx)
		avformat_close_input(&format_ctx);
	return 0;
}
        

なんでjsいじってもSyntaxHighlighterがうまく色分けしてくれないんや・・・。あと、2 != argcの時に標準入力から動的確保した領域に文字列を受けるように書こうとしたら、「確保範囲外に書き込むな」ってデバッガーに怒られるし。どーなってんだってばよ。

サンプルコードの実行例

ファイル名:C:\Users\yumetodo\Videos\『魔法科高校の劣等生 LOST ZERO』プロモーションムービー.mp4
stream #0
        codec type: Video
        codec name: h264
stream #1
        codec type: Audio
        codec name: aac

参考サイト

なぜか知らん、FFmpegAPIの導入も使い方も解説サイトが少ない。どーなってんだってばよ。結局一番役に立ったのは、AviUtlのプラグインでお馴染み、L-SMASH Worksのソースコードでした

だから、どうしてそ~なった。

リファレンスの初見殺し度は異常だと私はおもうんです。

参考サイトを見る上での注意点として、libavformat 53.2.0以降ではav_open_input_file関数を始めとして多くの関数が廃止、変更されています。十分注意してください。