OSXアプリからffmpegのAPIを使う。
2016/3/11
OSXの共有ライブラリの使い方。
-
ライブラリとプログラムのソース。
mylib.h
extern char* hello();
mylib.c#include "mylib.h" char* hello() { return "hello"; }
main.c#include <stdio.h> #include "mylib.h" int main(int argc, char *argv[]) { puts(hello()); }
-
ダイナミックリンクの場合。
ライブラリは拡張子 .dylib となる。
$ gcc mylib.c -o mylib.dylib -dynamiclib $ gcc main.c -o main -lm mylib.dylib $ ./main hello
ライブラリ(.dylib)を削除したら動作NG。
$ rm mylib.dylib $ ./main dyld: Library not loaded: mylib.dylib ...
-
スタティックリンクの場合。
ライブラリはアーカイブして拡張子 .a となる。
$ gcc -c mylib.c $ ar -r libmylib.a mylib.o $ gcc main.c -o main -L. -lmylib $ ./main hello
ライブラリ(.a)を削除しても動作OK。
$ rm libmylib.a $ ./main hello
ffmpegのAPIの使い方。
- インストール。
-
共有ライブラリ。
/usr/local/include と /usr/local/lib
-
サンプルソース。
/usr/local/Cellar/ffmpeg/3.0/share/ffmpeg/examples
-
ビルド方法。
$ gcc a.c -I/usr/local/include -L/usr/local/lib -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lswresample -lswscale
-
テストプログラム。
a.c
// 動画ファイルから1000フレームごとにサムネイル画像を生成する。 // -v オプションでAudio/Videoの全フレーム情報を出力する。 #include <stdio.h> #include <libavcodec/avcodec.h> #include <libavfilter/avfilter.h> #include <libavformat/avformat.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> void saveRGB24(uint8_t* pRgb, int width, int height, int wrap, int count); // メイン。 int main(int argc, char *argv[]) { // パラメータ検査。 char* inputFilename = NULL; int verbose = 0; for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "-v") == 0) verbose = 1; else inputFilename = argv[i]; } if (!inputFilename) { printf("Usage: %s [-v(verbose)] input_movie_file\n", argv[0]); return -1; } // 初期化。 av_register_all(); // 動画ファイルのオープンとストリーム情報の取得。 AVFormatContext* pFormatCtx = NULL; if (avformat_open_input(&pFormatCtx, inputFilename, NULL, NULL) != 0) { printf("Error: avformat_open_input()\n"); return -1; } if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { printf("Error: avformat_find_stream_info()\n"); return -1; } // Videoストリームを探す。 int videoStreamIndex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); if (videoStreamIndex < 0) { printf("Error: av_find_best_stream() for video\n"); return -1; } // Videoコーデックの取得とオープン。 AVStream* pVideoStream = pFormatCtx->streams[videoStreamIndex]; AVCodecContext* pVideoCodecCtx = pVideoStream->codec; AVCodec* pVideoCodec = avcodec_find_decoder(pVideoCodecCtx->codec_id); if (pVideoCodec == 0) { printf("Error: avcodec_find_decoder()\n"); return -1; } if (avcodec_open2(pVideoCodecCtx, pVideoCodec, NULL) < 0) { printf("Error: avcodec_open2() for video\n"); return -1; } float videoFrameRate = av_q2d(pVideoStream->r_frame_rate); printf("video stream: #%d %s %f fps\n", videoStreamIndex, pVideoCodec->name, videoFrameRate); // Audioストリームを探す。 int audioStreamIndex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); if (audioStreamIndex < 0) { printf("Error: av_find_best_stream() for audio\n"); return -1; } // Audioコーデックの取得とオープン。 AVCodecContext* pAudioCodecCtx = pFormatCtx->streams[audioStreamIndex]->codec; AVCodec* pAudioCodec = avcodec_find_decoder(pAudioCodecCtx->codec_id); if (pAudioCodec == 0) { printf("Error: avcodec_find_decoder() audio\n"); return -1; } if (avcodec_open2(pAudioCodecCtx, pAudioCodec, NULL) < 0) { printf("Error: avcodec_open2() for audio\n"); return -1; } int audioSampleRate = pAudioCodecCtx->sample_rate; printf("audio stream: #%d %s %d Hz\n", audioStreamIndex, pAudioCodec->name, audioSampleRate); // Video/Audio読み込み用フレームの確保。 AVFrame* pSrcFrame = av_frame_alloc(); int srcWidth = pVideoCodecCtx->width; int srcHeight = pVideoCodecCtx->height; int srcFmt = pVideoCodecCtx->pix_fmt; // サムネイル画像キャプチャ用フレームの確保。(バッファ付き) AVFrame* pCapFrame = av_frame_alloc(); int capWidth = 160; int capHeight = 90; int capFmt = AV_PIX_FMT_RGB24; int bufferAlign = 32; unsigned char* pCapBuffer = (unsigned char *)av_malloc( av_image_get_buffer_size(capFmt, capWidth, capHeight, bufferAlign)); // バッファ確保 av_image_fill_arrays(pCapFrame->data, pCapFrame->linesize, pCapBuffer, capFmt, capWidth, capHeight, bufferAlign); // バッファ関連付け // Video→サムネイル画像変換用コンテキストの取得。 struct SwsContext* pSwsCtx = sws_getContext(srcWidth, srcHeight, srcFmt, capWidth, capHeight, capFmt, SWS_FAST_BILINEAR, NULL, NULL, NULL); // フレームを読み込む。 ->pkt AVPacket pkt; int videoCount = 0, audioCount = 0; while (av_read_frame(pFormatCtx, &pkt) == 0) { // Videoフレームのデコード。 pkt -> pSrcFrame if (pkt.stream_index == videoStreamIndex) { int got_picture; if (avcodec_decode_video2(pVideoCodecCtx, pSrcFrame, &got_picture, &pkt) < 0) { printf("Error: avcodec_decode_video2()\n"); } else { // 1フレーム完成したらキャプチャ。 pSrcFrame -> pCapFrame if (got_picture) { ++videoCount; if (videoCount % 1000 == 0) { sws_scale(pSwsCtx, (const uint8_t **)pSrcFrame->data, pSrcFrame->linesize, 0 , srcHeight, pCapFrame->data, pCapFrame->linesize); saveRGB24(pCapFrame->data[0], capWidth, capHeight, pCapFrame->linesize[0], videoCount); } if (verbose) printf("video #%d time:%f\n", videoCount, (float)videoCount / videoFrameRate); } } } // Audioフレームのデコード。 pkt -> pSrcFrame else if (pkt.stream_index == audioStreamIndex) { do { int got_frame; int ret = avcodec_decode_audio4(pAudioCodecCtx, pSrcFrame, &got_frame, &pkt); if (ret < 0) { printf("Error: avcodec_decode_audio4()\n"); } else { // 1フレーム完成したら表示。 if (got_frame) { if (pAudioCodecCtx->sample_fmt != AV_SAMPLE_FMT_FLTP) { printf("Error: unsupported audio sample format.\n"); continue; } ++audioCount; int nb = pSrcFrame->nb_samples; float* pCh0 = (float*)pSrcFrame->extended_data[0]; float* pCh1 = (float*)pSrcFrame->extended_data[1]; if (verbose) printf("audio #%d time:%f nb:%d ch0[0]:%f ch1[0]:%f\n", audioCount, (float)pkt.pts / audioSampleRate, nb, *pCh0, *pCh1); } } // コーデックによっては一度に全てを処理しないので残りを再処理。 int decoded = FFMIN(ret, pkt.size); pkt.data += decoded; pkt.size -= decoded; } while (0 < pkt.size) ; } // パケットメモリ解放。 av_packet_unref(&pkt); } printf("Total video frame: %d\n", videoCount); printf("Total audio frame: %d\n", audioCount); // メモリ解放。 sws_freeContext(pSwsCtx); av_free(pCapBuffer); av_free(pCapFrame); av_free(pSrcFrame); avcodec_close(pAudioCodecCtx); avcodec_close(pVideoCodecCtx); avformat_close_input(&pFormatCtx); return 0; } // サムネイル画像の保存。 void saveRGB24(uint8_t* pRgb, int width, int height, int wrap, int count) { char szFilename[255]; sprintf(szFilename, "frame%d.ppm", count); FILE* fp = fopen(szFilename, "wb"); fprintf(fp, "P6\n" "%d %d\n" "255\n", width, height); for (int y = 0; y < height; ++y) { fwrite(pRgb + y * wrap, 1, width * 3, fp); } fclose(fp); printf("saved %s\n", szFilename); }
-
実行するとサムネイル画像が生成される。
$ ./a.out a.mp4 video stream: #0 h264 23.976025 fps audio stream: #1 aac 44100 Hz saved frame1000.ppm saved frame2000.ppm saved frame3000.ppm saved frame4000.ppm saved frame5000.ppm Total video frame: 5787 Total audio frame: 10402
-
-v オプション付きで実行した場合。
$ ./a.out a.mp4 -v video stream: #0 h264 23.976025 fps audio stream: #1 aac 44100 Hz video #1 time:0.041708 video #2 time:0.083417 video #3 time:0.125125 video #4 time:0.166833 video #5 time:0.208542 video #6 time:0.250250 video #7 time:0.291958 video #8 time:0.333667 audio #1 time:0.000000 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #2 time:0.023220 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #3 time:0.046440 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #4 time:0.069660 nb:1024 ch0[0]:0.000264 ch1[0]:0.000194 audio #5 time:0.092880 nb:1024 ch0[0]:0.000143 ch1[0]:0.000206 audio #6 time:0.116100 nb:1024 ch0[0]:0.019836 ch1[0]:0.140846 audio #7 time:0.139320 nb:1024 ch0[0]:-0.389849 ch1[0]:-0.677427 audio #8 time:0.162540 nb:1024 ch0[0]:-0.250030 ch1[0]:0.230023 audio #9 time:0.185760 nb:1024 ch0[0]:0.087733 ch1[0]:0.035425 audio #10 time:0.208980 nb:1024 ch0[0]:0.302962 ch1[0]:0.192596 audio #11 time:0.232200 nb:1024 ch0[0]:-0.149757 ch1[0]:-0.192178 audio #12 time:0.255419 nb:1024 ch0[0]:-0.063701 ch1[0]:0.024724 audio #13 time:0.278639 nb:1024 ch0[0]:-0.107548 ch1[0]:-0.044170 audio #14 time:0.301859 nb:1024 ch0[0]:0.060223 ch1[0]:-0.046339 audio #15 time:0.325079 nb:1024 ch0[0]:0.185195 ch1[0]:0.249126 audio #16 time:0.348299 nb:1024 ch0[0]:-0.099612 ch1[0]:0.015085 audio #17 time:0.371519 nb:1024 ch0[0]:0.012071 ch1[0]:0.039029 audio #18 time:0.394739 nb:1024 ch0[0]:-0.107942 ch1[0]:-0.129321 audio #19 time:0.417959 nb:1024 ch0[0]:-0.035510 ch1[0]:-0.006251 audio #20 time:0.441179 nb:1024 ch0[0]:0.006868 ch1[0]:-0.047740 audio #21 time:0.464399 nb:1024 ch0[0]:-0.132884 ch1[0]:-0.114906 audio #22 time:0.487619 nb:1024 ch0[0]:0.770623 ch1[0]:0.744321 video #9 time:0.375375 video #10 time:0.417083 video #11 time:0.458792 video #12 time:0.500500 video #13 time:0.542208 video #14 time:0.583917 video #15 time:0.625625 video #16 time:0.667333 video #17 time:0.709042 video #18 time:0.750750 video #19 time:0.792458 audio #23 time:0.510839 nb:1024 ch0[0]:-0.047785 ch1[0]:0.032025 audio #24 time:0.534059 nb:1024 ch0[0]:-0.069319 ch1[0]:-0.046341 audio #25 time:0.557279 nb:1024 ch0[0]:-0.052823 ch1[0]:-0.055239 audio #26 time:0.580499 nb:1024 ch0[0]:0.000409 ch1[0]:0.045984 audio #27 time:0.603719 nb:1024 ch0[0]:-0.018718 ch1[0]:-0.022679 audio #28 time:0.626939 nb:1024 ch0[0]:-0.005122 ch1[0]:0.004806 audio #29 time:0.650159 nb:1024 ch0[0]:-0.040002 ch1[0]:-0.044932 audio #30 time:0.673379 nb:1024 ch0[0]:0.517481 ch1[0]:0.519830 audio #31 time:0.696599 nb:1024 ch0[0]:-0.031830 ch1[0]:0.126331 audio #32 time:0.719819 nb:1024 ch0[0]:0.247971 ch1[0]:0.278795 audio #33 time:0.743039 nb:1024 ch0[0]:-0.049470 ch1[0]:-0.392131 audio #34 time:0.766258 nb:1024 ch0[0]:-0.029295 ch1[0]:-0.129360 audio #35 time:0.789478 nb:1024 ch0[0]:0.208565 ch1[0]:0.197284 audio #36 time:0.812698 nb:1024 ch0[0]:-0.154098 ch1[0]:-0.072475 audio #37 time:0.835918 nb:1024 ch0[0]:-0.092006 ch1[0]:-0.128978 audio #38 time:0.859138 nb:1024 ch0[0]:-0.084938 ch1[0]:-0.101195 audio #39 time:0.882358 nb:1024 ch0[0]:-0.007016 ch1[0]:0.027006 audio #40 time:0.905578 nb:1024 ch0[0]:-0.100383 ch1[0]:-0.051558 audio #41 time:0.928798 nb:1024 ch0[0]:-0.003705 ch1[0]:-0.046289 audio #42 time:0.952018 nb:1024 ch0[0]:0.058329 ch1[0]:0.044502 audio #43 time:0.975238 nb:1024 ch0[0]:-0.009321 ch1[0]:0.034892 video #20 time:0.834167 ~~~~~~~~~~~~~~~~~~~~~~~~~ video #5772 time:240.740494 audio #10377 time:240.930252 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10378 time:240.953476 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10379 time:240.976685 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10380 time:240.999908 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10381 time:241.023132 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10382 time:241.046356 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10383 time:241.069565 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10384 time:241.092789 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10385 time:241.116013 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10386 time:241.139236 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10387 time:241.162445 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10388 time:241.185669 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10389 time:241.208893 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10390 time:241.232101 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10391 time:241.255325 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10392 time:241.278549 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10393 time:241.301773 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10394 time:241.324982 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10395 time:241.348206 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10396 time:241.371429 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 video #5773 time:240.782196 video #5774 time:240.823914 video #5775 time:240.865616 video #5776 time:240.907333 video #5777 time:240.949036 video #5778 time:240.990738 video #5779 time:241.032455 video #5780 time:241.074158 video #5781 time:241.115875 video #5782 time:241.157578 video #5783 time:241.199280 audio #10397 time:241.394653 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10398 time:241.417862 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10399 time:241.441086 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10400 time:241.464310 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10401 time:241.487534 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 audio #10402 time:241.510742 nb:1024 ch0[0]:0.000000 ch1[0]:0.000000 video #5784 time:241.240997 video #5785 time:241.282700 video #5786 time:241.324417 video #5787 time:241.366119 Total video frame: 5787 Total audio frame: 10402