FFmpeg 개발

wwiki
이동: 둘러보기, 검색

FFmpeg 명령 사용을 먼저 해 볼 수 있다.

libavformat(lavf)[편집 | 원본 편집]

오디오 비디오 포맷 라이브러리

I/O and Muxing(Mux: multiplxer, 다중통신장치)/Demuxing Library.

다양한 미디어 컨테이너 형식을 처리하기 위한 라이브러리입니다. 데이터에 액세스하기 위한 여러 프로토콜(예: 파일, tcp, http 및 기타)을 지원하는 I/O모듈이 있습니다. lavf를 사용하기 전에 av_register_all()을 호출하여 컴파일된 모든 muxer, 디먹서 및 프로토콜을 등록해야 합니다. libavformat의 네트워크 기능을 사용하려면 avformat_network_init()를 호출해야 합니다.

멀티플렉싱 및 디먹싱에 사용되는 기본 lavf 구조는 AVFormatContext이며, 읽거나 쓰는 파일에 대한 모든 정보를 내보냅니다.

가장 중요한 것은 AVFormatContext에 다음이 포함되어 있습니다.

  • 입출력 컨테이너 포맷(AVInputFormat, AVOutputFormat): 입력을 위해 자동 감지되거나 사용자가 설정합니다. 항상 출력을 위해 사용자에 의해 설정됩니다.
  • 스트림(AVStream**): 오디오스트림, 비디오스트림, 자막스트림 등. 오디오스트림과 자막스트림은 언어별로 존재할 수 있다.
  • I/O컨텍스트(AVIOContext): lavf에 의해 열리거나 입력을 위해 사용자가 설정하며, 항상 출력을 위해 사용자가 설정합니다.[1]

(de)muxers에 옵션 전달[편집 | 원본 편집]

AVOptions 메커니즘을 사용하여 lavf muxers 및 demuxer를 구성 할 수 있습니다. 형식독립적인 일반 libavformat옵션은 AVFormatContext으로 제공한다. 사용자 프로그램에서 할당된 AVFormatContext(혹은 avformat_get_class()를 호출하여 얻은 AVClass로부터) av_opt_next()나 av_opt_find()함수를 호출해서 검사할 수 있다.

모듈[편집 | 원본 편집]

Core Functions[편집 | 원본 편집]

libavformat기능을 쿼리하거나 핵심구조체를 할당하는 함수들

avformat_network_init[편집 | 원본 편집]

파라미터: void

반환: int

이것은 선택 사항이며 더 이상 권장되지 않습니다.

이 기능은 구형 GnuTLS 또는 OpenSSL 라이브러리의 스레드 안전 문제를 해결하기 위해서만 존재합니다. 최신(4.0이상)버전에서 사용할 필요가 없다. 구형 OpenSSL등을 사용하는 경우에 스레드를 사용하기 전에 호출해라.

디먹싱[편집 | 원본 편집]

디먹서는 미디어 파일을 읽고 패킷(chunks of data, AVPacket)으로 분리한다.

패킷은 하나의 기본 스트림에 속하는 하나 개 이상의 인코딩 된 프레임을 포함한다. lavf API에서는 파일을 열기 위해서 avformat_open_input()함수를 호출한 후, 단일패킷을 읽기 위해서 av_read_frame()함수를 호출한 후, 마지막으로 avformat_close_input()을 호출해서 정리한다.

미디어 파일 열기


파일을 열기 위해서는 avformat_open_input()함수에 파일의 url을 전달해야 한다. 다음 코드처럼.

const char    *url = "file:in.mp3";
AVFormatContext *s = NULL;
int ret = avformat_open_input(&s, url, NULL, NULL);
if (ret < 0)
    abort();

위 코드는 AVFormatContext를 할당하고, 파일을 열어(포맷을 자동인식) 미디어파일 헤더를 읽고, 정보를 s에 내보낸다. 어떤 포맷은 헤더를 가지고 있지 않거나 충분한 정보를 저장하고 있지 않으므로, 놓친 정보를 찾기 위해서 avformat_find_stream_info() 함수를 호출해서 몇 프레임을 디코드를 시도해 보아라.

몇몇 경우에 avformat_alloc_context()를 호출해서 AVFormatContext를 미리 할당하고 avformat_open_input()에 전달하기 전에 조정하는 것이 좋습니다. lavf 내부 I/O layer대신에 입력 데이터를 읽기 위해서 custom function을 호출하려는 경우이다. 그렇게 하려면 avio_alloc_context()로 AVIOContext를 생성하고, 읽기콜백에 그것을 전달하세요. 그리고 AVFormatContext의 pb필드를 새로 생성한 AVIOContext로 설정해라.

파일포맷은 avformat_open_input()이 리턴할 때까지 알 수 없으므로, 미리 할당된 context에 private options을 디먹서에 설정하는 것은 불가능하다. 대신에 avformat_open_input()함수에 AVDictionary를 전달해서 가능하다.

AVDictionary *options = NULL;
av_dict_set(&options, "video_size", "640x480", 0);
av_dict_set(&options, "pixel_format", "rgb24", 0);
if (avformat_open_input(&s, url, NULL, &options) < 0)
    abort();
av_dict_free(&options);

이 코드는 private options인 'video_size', 'pixel_format'을 디먹서에게 전달한다. rowvideo 디먹서의 경우에 필요하다. 그렇지 않으면 원시 비디오 데이터를 어떻게 해석할지 알 수 없다. 만약 원시 비디오와 다르다고 판명되면, 디먹서에 의해 인식되지 않으므로 적용되지 않을 것이다. 인식되지 않은 그런 옵션은 options dictionary에 반환된다.(인식된 옵션들은 사용된다.) 다음처럼 인식되지 않은 옵션들은 원하는 대로 처리할 수도 있다.

AVDictionaryEntry *e;
if (e = av_dict_get(options, "", NULL, AV_DICT_IGNORE_SUFFIX)) {
    fprintf(stderr, "Option %s not recognized by the demuxer.\n", e->key);
    abort();
}

파일 읽기가 끝난 후에 avformat_close_input()을 호출해야 한다. 파일과 관련된 모든 것들을 해제한다.

열린 파일에서 읽기

열린 AVFormatContext에서 데이터를 읽는 것은 av_read_frame()를 반복적으로 호출하면 된다. 호출할 때마다 인코딩된 데이터인 하나의 AVStream을 포함한 AVPacket(AVPacket.stream_index번호로 식별된다.)을 리턴한다. 데이터를 디코딩하려면 패킷을 libavcodec의 디코딩 함수인 avcodec_send_packet()이나 avcodec_decode_subtitle2()에 직접 전달할 수 있다.

AVPacket.pts, AVPacket.dts, AVPacket.duration은 타이밍정보가 설정된다. 만약 스트림이 제공하지 않는다면 pts와 dts가 AV_NOPTS_VALUE이고 duration은 0이다. 타이밍 정보는 AVStream.time_base단위로 표시됩니다. 즉, 시간단위를 곱하여 초로 변환한다.

반환된 패킷에 AVPacket.buf가 설정되어 있으면 패킷이 동적으로 할당되므로 사용자가 이를 무한정 유지할 수 있다. 그렇지 않으면 AVPacket.buf는 NULL이고, 패킷 데이터는 디먹서 안의 정적 저장소에 백업되고(backed) 파일을 닫거나 av_read_frame()를 호출할 때까지만 유효하다. 더 사용하려면 av_dup_packet()함수가 av_malloc()를 호출해서 그것을 복사할 수 있다. 두 경우 모두 더 이상 필요하지 않으면 av_packet_unref()를 호출해서 패킷을 해제한다.

Functions[편집 | 원본 편집]
av_read_frame[편집 | 원본 편집]

Return the next frame of a stream.

파라미터

  • AVFormatContext * s
  • AVPacket * pkt

반환: int(성공이면 0, 에러거나 파일이 끝났으면 음수)

이 함수는 파일에 저장된 데이터를 반환하고, 디코더를 위한 유효한 프레임이 있는지 확인하지 않는다. 파일에 저장된 내용을 프레임으로 분할하고 호출할 때마다 하나씩 반환한다. 디코더는 디코딩에 가능한 최대 정보를 제공하기 위해 유효한 프레임간의 유효하지 않은 데이터를 생략하지 않는다.

만약 pkt->buf가 NULL이면 패킷(pkt)은 다음 av_read_frame() 또는 avformat_close_input()을 호출할 때까지 유효합니다. 그렇지 않으면 패킷은 무기한 유효하다. 두 경우 모두 av_packet_unref()를 호출하여 더이상 필요없는 패킷을 해제해야 한다. 비디오의 경우에는 정확히 하나의 프레임이 포함된다. 오디오의 경우 비디오 프레임마다 정수개의 프레임을 포함한다.(가령, PCM 혹은 ADPCM 데이터처럼 여러개의 오디오 스트림처럼) 만약 오디오 프레임들이 가변크기를 가진다면(MPEG 오디오처럼) 한개의 프레임을 포함한다.

pkt->pts, pkt->dts and pkt->duration은 항상 AVStream.time_base 단위로 정확한 값들이 설정된다. (만약 포맷이 그걸 제공하지 않으면 추측된다.) 비디오 프레임이 B-frames이면 pkt->ptsd은 AV_NOPTS_VALUE이다. 그래서 페이로드의 압축을 해제하지 않으면 pkt->dts를 사용하는 것이 좋다.

av_packet_unref[편집 | 원본 편집]

패킷을 정리한다.

파라미터

  • AVPacket * pkt

패킷이 참조하는 버퍼를 참조해제하고 나머지 패킷필드를 기본값으로 재설정한다.

libavcodec[편집 | 원본 편집]

Encoding/Decoding Library.

Modules[편집 | 원본 편집]

Decoding[편집 | 원본 편집]

Fuctions[편집 | 원본 편집]
av_parser_parse2[편집 | 원본 편집]

Parse a packet.

parameter

  • AVCodecParserContext *s
  • AVCodecContext *avctx
  • uint8_t **poutbuf
  • int *poutbuf_size
  • const uint8_t *buf: 인코딩된 데이터
  • int buf_size
  • int64_t pts
  • int64_t dts
  • int64_t pos

return: int

s = av_parser_init(codec->id);
avctx = avcodec_alloc_context3(codec);
ret = av_parser_parse2(s, avctx, &pkt->data, &pkt->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
avcodec_send_packet[편집 | 원본 편집]

파라미터

  • AVCodecContext * avctx 코덱 콘텍스트
  • const AVPacket * avpkt [in]

입력값. 일반적으로 단일 비디오 프레임이거나 몇 개의 완성된 오디오 프레임들이다. 패킷의 소유권은 호출한 곳에 유지되고, 디코더는 그 패킷의 값을 변경하지 않을 것이다. 디코더는 패킷 데이터에 대한 참조를 생성할 수 있다.(혹은 레퍼런스 카운트되지 않는다면 복사할 것이다.) 오래된 함수들과 다르게 패킷은 항상 전체가 소비되고, 패킷이 어떤 오디오 코덱처럼 여러 프레임들을 포함한다면 avcodec_receive_frame()함수를 여러 번 호출해야 할 것이다. avpkt가 NULL이거나 혹은 avpkt의 data가 NULL이거나 size가 0인 경우에는 스트림의 끝이라는 신호인 flush pakcet으로 간주된다. 처음 flush packet을 보내는 것은 성공을 반환한다. 다시 보내는 것은 불필요하고 AVERROR_EOF를 리턴할 것이다. 만약 디코더가 여전히 버퍼된 프레임을 가지고 있다면, flush packet을 보낸 후에 리턴한다.

반환: int

성공이면 0, 그렇지 않으면 음수인 에러코드를 리턴한다.

원시 패킷 데이터를 디코더에 입력으로 제공한다.

내부적으로 이 함수를 호출하면 각 패킷의 디코딩하는 것에 영향을 미치는 AVCodecContext의 관련된 필드를 복사할 것이다. 그리고 실제로 패킷이 디코딩되었을 때 적용한다.

경고

입력버퍼인 avpkt->data는 어떤 최적화된 bitstream reader는 끝을 넘어서 한번에 32비트나 64비트를 읽기 때문에 실제 읽은 바이트보다 AV_INPUT_BUFFER_PADDING_SIZE가 커야 한다.

동일한 AVCodecContext를 avcodec_decode_video2()와 같은 오래된 함수와 혼용하지 마라. 기대하지 않았던 값을 리턴할 것이다.

주의

AVCodecContext는 디코더에 전달하기 전에 avcodec_open2()함수를 호출하여 열어야 한다.

Core functions/structures.[편집 | 원본 편집]

Modules[편집 | 원본 편집]
AVPacket[편집 | 원본 편집]

예제[편집 | 원본 편집]

decode_video.c[편집 | 원본 편집]

/*
 * Copyright (c) 2001 Fabrice Bellard
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
/**
 * @file
 * video decoding with libavcodec API example
 *
 * @example decode_video.c
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#define INBUF_SIZE 4096
static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize,
                     char *filename)
{
    FILE *f;
    int i;
    f = fopen(filename,"w");
    fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);
    for (i = 0; i < ysize; i++)
        fwrite(buf + i * wrap, 1, xsize, f);
    fclose(f);
}
static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
                   const char *filename)
{
    char buf[1024];
    int ret;
    ret = avcodec_send_packet(dec_ctx, pkt);
    if (ret < 0) {
        fprintf(stderr, "Error sending a packet for decoding\n");
        exit(1);
    }
    while (ret >= 0) {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during decoding\n");
            exit(1);
        }
        printf("saving frame %3d\n", dec_ctx->frame_number);
        fflush(stdout);
        /* the picture is allocated by the decoder. no need to
           free it */
        snprintf(buf, sizeof(buf), "%s-%d", filename, dec_ctx->frame_number);
        pgm_save(frame->data[0], frame->linesize[0],
                 frame->width, frame->height, buf);
    }
}
int main(int argc, char **argv)
{
    const char *filename, *outfilename;
    const AVCodec *codec;
    AVCodecParserContext *parser;
    AVCodecContext *c= NULL;
    FILE *f;
    AVFrame *frame;
    uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t *data;
    size_t   data_size;
    int ret;
    AVPacket *pkt;
    if (argc <= 2) {
        fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);
        exit(0);
    }
    filename    = argv[1];
    outfilename = argv[2];
    pkt = av_packet_alloc();
    if (!pkt)
        exit(1);
    /* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
    memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
    /* find the MPEG-1 video decoder */
    codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    parser = av_parser_init(codec->id);
    if (!parser) {
        fprintf(stderr, "parser not found\n");
        exit(1);
    }
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }
    /* For some codecs, such as msmpeg4 and mpeg4, width and height
       MUST be initialized there because this information is not
       available in the bitstream. */
    /* open it */
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }
    f = fopen(filename, "rb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }
    while (!feof(f)) {
        /* read raw data from the input file */
        data_size = fread(inbuf, 1, INBUF_SIZE, f);
        if (!data_size)
            break;
        /* use the parser to split the data into frames */
        data = inbuf;
        while (data_size > 0) {
            ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
                                   data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            if (ret < 0) {
                fprintf(stderr, "Error while parsing\n");
                exit(1);
            }
            data      += ret;
            data_size -= ret;
            if (pkt->size)
                decode(c, frame, pkt, outfilename);
        }
    }
    /* flush the decoder */
    decode(c, frame, NULL, outfilename);
    fclose(f);
    av_parser_close(parser);
    avcodec_free_context(&c);
    av_frame_free(&frame);
    av_packet_free(&pkt);
    return 0;
}


외부링크[편집 | 원본 편집]

원문: https://ffmpeg.org/doxygen/4.1/index.html

https://ffmpeg.org/developer.html

https://ffmpeg.org/documentation.html

각주[편집 | 원본 편집]

  1. 암호화된 데이터를 전달하는 경우에 직접 할당하여 사용하는 듯 하다.