FFmpeg Player with Visual Studio - 04 FFmpeg을 이용하여 미디어 파일 열기 └ FFMPEG

4. FFmpeg을 이용하여 미디어 파일 열기


이번 강좌에서는 FFmpeg을 이용해서 어떻게 미디어 파일 또는 미디어 스트림을 열고, 미디어의 정보를 가져올 수 있는지 알아보도록 하겠습니다.


> Library 초기화 하기


거의 모든 오픈소스 Library들은 사용하기에 앞서 초기화가 필요합니다. FFmpeg의 초기화 과정은 전혀 어렵지 않습니다. 프로그램 시작 부에 아래 함수를 한번만 호출해 주시면 됩니다.


av_register_all();


av_register_all() 함수는 말 그대로 ffmpeg에서 지원되는 모든 Demuxer, Muxer, Protocol 및 Codec 등을 사용할 수 있도록 등록하는 행위입니다. ( 여기서 말한 지원되는 모든 컴포넌트들은 Library 버전에 따라서도 다를 수 있지만, Library를 컴파일 하는 과정에서 옵션으로 enable/disable 시킨 여부에 따라서 다를 수 있습니다.)


만일 네트워크를 통한 입력 source ( rtsp, rtp, udp, hls 스트림 등 ) 를 처리 할 일이 있다면, 아래의 추가 초기화 함수를 호출해 주시면 됩니다.


avformat_network_init();


avformat_network_init() 함수는 network 사용을 위한 추가 초기화 작업을 수행합니다. 가령 windows의 경우 winsock을 사용하기 위해서 초기화 함수인 WSAStartup() 함수를 호출해줘야 하는데, 그런 과정을 저 함수에서 처리한다고 보시면 됩니다. 이 함수를 호출했다면, 종료 시 해제 함수 avformat_network_deinit(); 을 같이 호출해 주셔야 합니다.


> 미디어 파일을 열어 보자!!


FFmpeg을 이용해서 미디어 파일을 열기 위해서는 최소한 파일의 URL 또는 파일 이름이 필요합니다. 당연한 내용이지요? 우리는 아래 함수를 이용하여, 미디어 파일을 열 수 있습니다.


avformat_open_input (AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options);


첫번째 패러미터는 AVFormatContext 구조체 입니다. 사용자가 avformat_alloc_context() 함수를 이용하여 직접 생성한 후 넘겨줘도 되고, NULL을 가리키는 포인트 변수를 넘겨서 내부에서 생성하도록 하셔도 됩니다.

참고로 AVFormatContext 구조체는 FFmpeg Library중 avformat Library의 가장 핵심적인 데이터 타입으로, 주로 I/O 및 Muxing/Demuxing 과정에 사용됩니다. 마치 c library의 FILE 포인터와 같이 미디어 I/O 및 Muxing/Demuxing에 필요한 정보들을 포함하고 있습니다.

avformat_open_input() 함수는 첫번째 인자로 넘긴 AVFormatContext 구조체에 I/O 및 Muxing/Demuxing에 필요한 정보를 채워주는 역할을 합니다.


두번째 패러미터는 열고자 하는 파일이름입니다. 단순히 변수 이름이 filename 이라고 해서 파일만 가능한 것이 아니라, rtsp, udp 등 스트리밍 URL 모두 가능합니다. 굳이 다시 이름 붙이자면 Input Source 정도가 적당할 것 같네요.


세번째 패러미터는 강제로 Input Format을 지정할 경우에 사용됩니다. 그냥 NULL 값으로 지정하시면 입력 소스에 따라서 자동으로 Input Format을 검색합니다.


마지막 패러미터는 demuxer의 추가 옵션을 지정할 때 사용합니다. 이 패러미터 역시 잘 사용하지 않으므로 그냥 NULL 값을 넣어주시면 됩니다. 몇몇 특수한 경우, 가령 raw-video 를 demuxing 할 일이 발생한다거나 할 때, ( 파일에는 아무 정보도 없기 때문에 Parsing을 위해서 raw-video의  video 해상도 크기 및 Color 포맷을 알려줘야 하겠죠? ) 해당 정보를 이 옵션 패러미터로 넘겨주는 겁니다.


> 미디어 정보를 가져오자


avformat_open_input() 함수에서 미디어 파일을 열 때, 파일 헤더 정보가 존재하면 미리 같이 읽습니다. Input Format에 따라서는 파일 헤더 정보에 모든 내용이 기록된 포맷도 있겠지만 정보가 충분하지 않거나, 헤더가 없는 Format도 존재합니다. 이 경우 부족한 정보를 얻기 위해서는 어떻게 해야할까요? 그렇습니다. 충분한 정보를 얻기 위해서 미리 Data를 조금 읽어들여서 Parsing 또는 Decode 해보는 방법이 있습니다. FFmpeg Library는 이를 위해서 다음과 같은 함수를 제공합니다.


avformat_find_stream_info( AVFormatContext *ic, AVDictionary ** options );


첫번째 패러미터는 앞서 avformat_open_input()에 사용한 AVFormatContext 객체를 사용하시면 됩니다.


두번째 패러미터는 Codec 옵션을 지정할 때 사용합니다. 때에 따라서는 정보를 얻기 위해서 미리 Data를 Decode 해봐야 할 때가 있는데, 그 때 임시로 생성할 Codec에 넘겨줄 Codec 옵션을 지정합니다. 역시 잘 사용하지 않으므로 그냥 NULL 값을 넣어주시면 됩니다.


참고로 avformat_find_stream_info() 는 blocking 함수 입니다. Input Source로 Network Protocol을 사용한다면 정보가 담겨있는 패킷이 발견될 때까지 계속해서 Read를 시도하기 때문에 시간이 지연되거나, 최악의 경우 패킷이 들어오지 않는다면, block되어 버리는 일이 발생할 수도 있으니 주의합시다.


> 미디어 파일을 닫자


마지막으로 열었던 미디어 파일을 닫아보겠습니다. 다음 함수를 호출해서 파일을 닫을 수 있습니다.


avformat_close_input (AVFormatContext **s);


위 함수는 단순히 열려있는 파일을 닫는 기능 뿐만아니라  avformat_open_input() 또는 avformat_alloc_context() 함수를 통해 할당된 AVFormatContext 객체 s 를 해제까지 해줍니다. 즉, 따로 free 계열의 함수를 호출할 필요 없이, 이 함수 하나로 파일 닫기 및 자원 해제가 가능합니다.


> 정리


지금까지의 과정을 실제 코드로 정리를 해보도록 하겠습니다.

다음 코드는 AVFormatContext를 할당하고, 특정 파일을 열며 (포맷 등은 자동으로 검색), 스트림 정보를 뽑아내서 AVFormatContext에 정보를 저장하는 과정을 보여주는 코드입니다.



const char *szFilePath = "test.mp4";

///> Initialize libavformat and register all the muxers, demuxers and protocols.
av_register_all();

///> Do global initialization of network components.
avformat_network_init();

int ret;
AVFormatContext *pFmtCtx =
NULL;

///> Open an input stream and read the header.
ret = avformat_open_input( &pFmtCtx, szFilePath,
NULL, NULL );
if( ret != 0 ) {
av_log(
NULL, AV_LOG_ERROR, "File [%s] Open Fail (ret: %d)\n", ret );
exit( -1 );
}
av_log(
NULL, AV_LOG_INFO, "File [%s] Open Success\n", szFilePath );

///> Read  of a media file to get stream information
ret = avformat_find_stream_info( pFmtCtx,
NULL );
if( ret < 0 ) {
av_log(
NULL, AV_LOG_ERROR, "Fail to get Stream Information\n" );
exit( -1 );
}
av_log(
NULL, AV_LOG_INFO, "Get Stream Information Success\n" );

///> Close an opened input AVFormatContext.
avformat_close_input( &pFmtCtx );

///> Undo the initialization done by avformat_network_init.
avformat_network_deinit();


첨부한 소스코드에는 정보를 출력하는 부분까지 추가되어 있습니다.

(visual studio 2010용 프로젝트 파일입니다)

FFmpegFirst.7z.001
FFmpegFirst.7z.002

프로젝트를 여신 후에 szFilePath 값을 바꿔가면서 테스트 해보세요.

입력은 반드시 파일일 필요가 없으며, HLS, UDP 등의 네트워크 스트림도 가능합니다.


이번 강좌는 여기서 마치도록 하겠습니다.




덧글

  • 감사합니다 2014/04/14 16:08 # 삭제 답글

    따끈따끈한 포스팅 따라하며 초보자인 저도 이해하기 쉽게 잘 작성하셔서
    많은 도움이 되고 있습니다.
    감사합니다.
  • 티아로이 2014/04/15 21:18 # 삭제 답글

    open cv를 이용해서

    캠으로 촬영한영상을 서버에 스트리밍방식으로 바로 저장하려고

    여러번 시도하다가 아주 좋은 글을 발견하는군요. 정말 감사합니다!
  • 티아로이 2014/04/15 21:36 # 삭제 답글

    관리자님, 한가지 궁금한게 있습니다.
    올려주신 예제도 그렇고 7zip으로 업로드해주신 예제도 그렇고

    패스설정도 똑같이 잘 했는데 빌드오류가 나서 자꾸 실패한다고 뜹니다...

    visual studio c++ 2010 버전인데, 왜 그런걸까요 ㅠ..
  • 티아로이 2014/04/15 21:39 # 삭제 답글

    오류내용은


    =============================================================================
    1>------ 빌드 시작: 프로젝트: main, 구성: Debug Win32 ------
    1> main.cpp
    1>LINK : fatal error LNK1123: COFF로 변환하는 동안 오류가 발생했습니다. 파일이 잘못되었거나 손상되었습니다.
    ========== 빌드: 성공 0, 실패 1, 최신 0, 생략 0 ==========


    입니다..
  • 체인지겟타 2014/04/18 15:09 #

    답변이 좀 늦었습니다

    VS2010 문제로 일단 프로젝트 설정에서

    Manifest Tool ( 메니페스트 도구 ) -> Input and Output (입력 및 출력) -> Embed Manifest( 메니페스트 포함 ) 옵션을 No (아니오) 로 설정해 보시거나

    만일 서비스팩 1 을 설치하지 않으셨다면, 서비스 팩을 설치해 보시길 추천 드립니다

    http://www.microsoft.com/ko-kr/download/search.aspx?q=VS2010
  • 티아로이 2014/04/22 21:23 # 삭제 답글

    감사합니다! 서비스팩1 을 설치하니 정상실행되는군요~~! 다음 강의도 기다리고 있겠습니다!!!

    아 그리고 실례가 안된다면 혹시 webrtc 기술에 대해 아시는지요..?
    web rtc를 응용한 record rtc 란 기술이 github 사이트에 있길래

    해당 기술을 이용하면 영상파일, 음성파일 두가지 파일을 저장할수있습니다.
    그걸 다시 ffmpeg로 하나의 미디어파일로 (h.264, mp4)로 만들어 개인서버에 저장하려하는데
    가능 할까요..? ffmpeg를 처음접해본터라 너무 어렵네요 ㅠㅠ..
  • K 2014/04/24 15:15 # 삭제 답글

    강좌 감사히 보았습니다 !

    첨부주신 예제에 AVMediaType2Str과 AVCodecID2Str은
    콘솔에 좀더 알기쉽게 정보를 출력하기 위해 만드신 것 같은데,

    이 숫자코드값과 return 문자열 관계에 대한 Table은 어디에 가면 볼 수 있을까요?

    저는 올려주신 강좌를 보고 2.2.1 64-bit 버전으로 따라가고 있는데
    위 예제의 "ALIAS_PIX"와 "VP7"의 enum이 선언되지 않은 변수라고 나와서요 ' '
  • 체인지겟타 2014/04/28 10:20 #

    Table이 따로 있는 것은 아니고, 헤더에 정의된 enum 값을 단순히 문자열로 바꾸도록 한 함수입니다
    강좌 기준으로 최신버전의 FFmpeg Library를 사용하고 있기 때문에 Library 버전에 따라서 없는 enum이 있을 수도 있겠네요

    해당 enum 값은 ffmpeg 라이브러리 include 헤더 중 libavcodec/avcodec.h 파일에 정의되어 있습니다

    에러가 나시는 enum 값만 함수내에서 주석 처리하시면 될 것 같습니다
  • 배우미 2014/05/25 01:07 # 삭제 답글

    감사합니다, 많이 배웠습니다.

    헌데 release 모드에 av_register_all 부분에서 실행중에 에러가 납니다.
    debug 모드에서 떨군 라이브러리라 그런다고 제딴엔 생각이 드는데요.

    어디에 문제가 있는지, debug 용이래서 그렇다면 release 용을 올려 주실 수 없습니까?

    님강좌 잘보고 저도 뭘좀 만들어 보려는데 debug에서는 잘 되는데 relese 로 떨구자니 에러가 나네요.
    도와주시면 고맙겠습니다.
  • 백지 2014/07/10 14:03 # 삭제 답글

    감사합니다.
    강좌 잘 보고 있습니다.

    계속 추가 강좌 기다립니다.
  • tets 2015/03/19 20:11 # 삭제 답글

    test
  • tets 2015/03/19 20:11 # 삭제 답글

    test
  • tets 2015/03/19 20:11 # 삭제 답글

    test
  • 마르세유 2017/04/16 09:01 # 삭제 답글

    잘보고갑니다 정말 좋은 글인거같아요 ^^
댓글 입력 영역