RaGo

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
<p>#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include <libavcodec/avcodec.h>
#include <libavdevice\avdevice.h>
#include <libavfilter\avfilter.h>
#include <libavformat\avformat.h>
#include <libavutil\avutil.h>
 
int main() {
    av_register_all(); //ffmpeg를 사용하기전 초기화
 
    AVCodec * pVideoCodec;
    AVCodec * pAudioCodec;
 
    AVPacket packet = *av_packet_alloc(); //패킷 데이터
    //av_init_packet(&packet);
    //av_new_packet(&packet, AV_INPUT_BUFFER_PADDING_SIZE);
 
    AVCodecContext * m_Context; //디코딩 정보
    AVFormatContext * pFormatCtx = NULL;
 
    int ret = avformat_open_input(&pFormatCtx, "E:\\sample.mp4", NULL, NULL);
    //미디어 파일 열기 file주소 or URL
    //파일의 헤더로 부터 파일 포맷에 대한 정보를 읽어낸 뒤 AVFormatContext에 저장한다.
    //그 뒤의 인자들은 각각 Input Source (스트리밍 URL이나 파일경로), Input Format, demuxer의 추가옵션 이다.
    if (ret != 0)
    {
        av_log(NULL, AV_LOG_ERROR, "File Open Failed\n");
        exit(-1);
    }
 
    ret = avformat_find_stream_info(pFormatCtx, NULL); //열어놓은 미디어 파일의 스트림 정보를 가져온다
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Fail to get Stream Inform\n");
        exit(-1);
    }
 
    int VSI = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);//비디오 스트림 정보를 가져온다.
    int ASI = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, VSI, NULL, 0);//오디오 스트림 정보를 가져온다.
 
    /*분석중...*/
    av_dump_format(pFormatCtx, 0, "E:\\sample.mp4", 0);
 
 
    /*codec_id에 맞는 비디오 디코더를 찾는다*/
    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_parameters_to_context(pVCtx, pFormatCtx->streams[VSI]->codecpar)<0) {
        printf("avcodec_parameters_to_context Error\n");
        exit(-1);
    }
    if (avcodec_parameters_to_context(pACtx, pFormatCtx->streams[ASI]->codecpar)<0) {
        printf("avcodec_parameters_to_context Error\n");
        exit(-1);
    }
 
    /*
    *디코더 정보를 찾을 수 있다면 AVContext에 그 정보를 넘겨줘서 Decoder를 초기화 함
     *세번째 인자 : 디코더 초기화에 필요한 추가 옵션. 비트레이트 정보나 스레트 사용여부를 정해줄 수 있다.
    */
    if (avcodec_open2(pVCtx, pVideoCodec, NULL) < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Fail to Initialize Decoder\n");
        exit(-1);
    }
 
    if (avcodec_open2(pACtx, pAudioCodec, NULL) < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Fail to Initialize Decoder\n");
        exit(-1);
    }
 
 
    AVFrame *pAFrame = av_frame_alloc(); //alloc 않하면 밑에서 프레임을 받아올수 없다.
    AVFrame *pVFrame = av_frame_alloc(); //alloc 않하면 밑에서 프레임을 받아올수 없다.
 
 
    while (av_read_frame(pFormatCtx, &packet) == 0) {
 
        if (packet.stream_index == VSI) {//패킷이 비디오 패킷이면...
            if (ret = avcodec_send_packet(pVCtx, &packet)) { //패킷 데이터를 디코더의 입력으로 제공합니다.
                printf("avcodec_send_packet failed %d %d %d\n", ret, AVERROR(EINVAL), AVERROR(ENOMEM));
            }
            while (avcodec_receive_frame(pVCtx, pVFrame) >= 0) { //디코더의 출력을 pVFrame으로 받아옵니다.
              //TODO 디코딩에 성공하면 이부분에서 pVframe을 가져와 사용한다.
 
            }
        }
        else if (packet.stream_index == ASI) {//패킷이 오디오 패킷이면...
            if (ret = avcodec_send_packet(pACtx, &packet)) {
                printf("avcodec_send_packet failed %d %d %d\n", ret, AVERROR(EINVAL), AVERROR(ENOMEM));
            }
            while (avcodec_receive_frame(pACtx, pAFrame) >= 0) {
              //TODO 디코딩에 성공하면 이부분에서 pVframe을 가져와 사용한다.
            }
        }
        av_packet_unref(&packet);
    }
}</p>

ffmpeg function Reference

* 모든(?) 혹은 대부분의 function은 실행에 실패했을경우 음수값을 반환하며 음수값에따라 오류를 판단한다.

av_register_all() //libavformat을 초기화합니다


avformat_open_input() //파일(스트림)을 열고 해더를 읽습니다.

avformat_find_stream_info() //스트림 정보를 얻기 위해 미디어 파일의 패킷을 읽습니다.

av_find_best_stream() //스트림에서 사용자가 원하는 스트림을 찾아 int형으로 반환합니다. [비디오, 오디오, 자막]
avcodec_find_decoder() //매개변수의 코덱 ID가 일치하는 디코더를 찾습니다.
avcodec_alloc_context3() //코덱을 이용하여AVCodecContext를 할당 필드를 디폴트 치로 설정하고 포인터를 반환합니다.
avcodec_parameters_to_context() //AVCodecParameters을 기반으로 AVCodecContext를 채웁니다.
avcodec_open2() //AVCodecContext를 주어진 AVCodec을 이용해 초기화 합니다. 이 작업을 하기전에 avcodec_alloc_context3()가 먼저 처리되야합니다.

av_packet_alloc() //AVPacket을 메모리에 할당하고 필드를 기본값으로 설정합니다.

av_init_packet() //패킷의 필드를 기본값으로 초기화합니다.

av_new_packet() //패킷을 원하는 크기로 할당하고 초기화합니다.


av_frame_alloc() //AVFrame을 메모리에 할당하고 필드를 기본값으로 설정합니다.


avcodec_send_packet() // 패킷 데이터를 디코더에 입력합니다.
av_read_frame() // 스트림의 다음의 프레임을 리턴합니다.
avcodec_receive_frame() // 디코더에서 디코딩 된 프레임을 반환합니다.
av_packet_unref() // 패킷이 참조하는 버퍼를 참조 해제하고 나머지 패킷 필드를 기본값으로 재설정


ffmpeg Struct Reference

AVCodec

AVCodecContext

AVPacket

AVFormatContext

AVFrame