[FFmpeg] 동영상 디코딩

2016. 8. 31. 11:34Application Programming/FFmpeg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include "Header.h"
 
int main(void)
{
    av_register_all(); //ffmpedg에서 지원되는 모든 Demuxer, Muxer, Codec, Protocol을 사용할 수 있도록 등록
    //전체 코드에서 한 번 불러주면 된다!
    
    AVCodec * pVideoCodec;
    AVCodec * pAudioCodec;
 
    AVPacket packet; //인코딩 된 파일 데이터 
 
    AVCodecContext * m_Context;        //어떻게 디코딩 할 것인지
    AVFormatContext *pFormatCtx = NULL;
 
 
 
    int ret = avformat_open_input(&pFormatCtx, "C:\\Sample.mp4"NULLNULL);
    //미디어 파일 열기
    //파일의 헤더로 부터 파일 포맷에 대한 정보를 읽어낸 뒤 첫번째 인자 (AVFormatContext) 에 저장한다.
    //그 뒤의 인자들은 각각 Input Source (스트리밍 URL이나 파일경로), Input Format, demuxer의 추가옵션 이다.
    if (ret != 0)
    {
        av_log(NULL, AV_LOG_ERROR, "File Open Failed\n");
        exit(-1);
    }
    //파일이 제대로 열리지 않는다면 실패했다는 로그와 함께 종료     
 
 
    av_log(NULL, AV_LOG_INFO, "File Open Success\n");    //콘솔창에 로그메시지를 띄운다
 
    ret = avformat_find_stream_info(pFormatCtx, NULL); //열어놓은 미디어 파일의 스트림 정보를 가져온다
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Fail to get Stream Inform\n");
        exit(-1);
    }
    av_log(NULL, AV_LOG_INFO, "Get Stream Inform Success\n");
 
    int VSI = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1-1NULL0);
    int ASI = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, VSI, NULL0);
    //첫번째 인자 : 스트림을 가져오고자 하는 미디어 파일을 담고 있는 AVFormatContext 구조체
    //두번째 인자 : 가져오고자 하는 스트림의 타입
    //세번째 인자 : 특별히 지정하고자 하는 스트림의 인텍스 번호. -1일 경우 자동선택
    //네번째 인자 : 연관된 스트림의 인덱스 번호. -1일 경우 가장 처음으로 만나는 타겟 스트림을 가져온다.
    //다섯번째 인자 : Decoder까지 함께 검색할지 말지. AV코덱 구조체의 주소를 넘기도록 되어있다.
    //마지막 인자 : 따로 flag가 지정되지 않음. 현재 사용되지 않는 부분.
 
 
 
 
    pVideoCodec = avcodec_find_decoder(pFormatCtx->streams[VSI]->codecpar->codec_id);
    if (pVideoCodec == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "No Decoder\n");
        exit(-1);
    }
 
    pAudioCodec = avcodec_find_decoder(pFormatCtx->streams[ASI]->codecpar->codec_id);
    if (pAudioCodec == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "No Decoder\n");
        exit(-1);
    }
 
 
    AVCodecContext *pVCtx = avcodec_alloc_context3(pVideoCodec);
    AVCodecContext *pACtx = avcodec_alloc_context3(pAudioCodec);
    
    if (avcodec_open2(pVCtx,  pVideoCodec, NULL< 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Fail to Initialize Decoder\n");
        exit(-1);
    }
    //avcodec_open2 : 디코더 정보를 찾을 수 있다면 AVContext에 그 정보를 넘겨줘서 Decoder를 초기화 함
    //첫번째 인자 : AVCodecContext 구조체
    //두번째 인자 : AVCodec 구조체
    //세번째 인자 : 디코더 초기화에 필요한 추가 옵션. 비트레이트 정보나 스레트 사용여부를 정해줄 수 있다.
 
 
    
    if (avcodec_open2(pACtx, pAudioCodec, NULL< 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Fail to Initialize Decoder\n");
        exit(-1);
    }
 
 
 
    AVFrame *pVFrame = NULL;
    AVFrame *pAFrame = NULL;
    // 디코딩의 결과로 얻은 데이터를 저장하는 구조체
    // 위에서 인코딩의 결과가 packet이었던 것 처럼!
 
    int bGotPicture = 0;
    int bGotSound = 0;
 
 
    while (av_read_frame(pFormatCtx, &packet) >=0)
    {//파일로부터 인코딩 된 비디오/ 오디오 데이터를 읽어서 packet에 저장하는 함수        
        if (packet.stream_index == VSI) {
            // Decode Video
            if (!avcodec_receive_frame(pVCtx, pVFrame))
            {
                //이미지 렌더링 준비 완료!
            }
        }
        else if (packet.stream_index == ASI) {
            // Decode Audio
            if (!avcodec_receive_frame(pACtx, pAFrame))
            {
                //사운드 렌더링 준비 완료!
            }
        }
        
        av_packet_unref(&packet);
        //AVPacket의 경우 포인터타입으로 선언해서 사용하지 않음
        //구조체 내에 data라는 포인터 변수가 있기 때문임! 
        //따라서 다 사용하고 나서 Release해주어야 할 필요가 있음!!
    }
 
    avformat_close_input(&pFormatCtx);
    //열었던 미디어파일 닫기
    system("pause");
    return 0;
}
 
cs


http://egloos.zum.com/aslike/v/3091644 이곳을 참고했다!! 완전 잘 정리해주셔서 이해하기 수월했다


근데 버전업이 되어서 그런지 'AVStream::codec': was declared deprecated 라는 에러가 여러개 떴다!!

AVStream::codec은 AVCodecContext 라는 이름의 구조체이다.

codec->codec_id처럼 구조체의 멤버변수를 부르는 곳에서는 codec대신 codecpar를, codec 처럼 구조체 자체를 부르는 곳에서는 AVCodecContext객체를 따로 만들어 avcodec_alloc_context3(AVCodec) 을 넣어준 뒤에 사용했다!



그걸 수정하고 나니 avcodec_decode_video2와 avcodec_decode_audio4에서 같은 메시지가 뜨길래 찾아보니 이제 사용하지 않는 함수라 대신 avcodec_send_packet() 과 avcodec_recieve_frame()을 사용해야 한다고 한다. 

(https://libav.org/documentation/doxygen/master/deprecated.html)



여기까지 디코딩은 마쳤다! 이제 디코딩으로 얻어낸 정보를 화면에 뿌려주면 된다

'Application Programming > FFmpeg' 카테고리의 다른 글

[FFmpeg] 기본 소스코드  (0) 2016.08.30
[FFmpeg] 환경 구축  (0) 2016.08.27