【FFmpeg(2016)】SwrContext 변환 PCM 오디 오 비트

10893 단어 C/C++FFmpeg
[관련 블 로그]
【FFmpeg(2016)】PCM 인 코딩 AAC
[FFmpeg(2016)]SwrContext 재 샘플링 구조 체
【머리말】
이틀 동안 오디 오 인 코딩 을 하고 있 지만 FFmpeg 인 코딩 라 이브 러 리 avcodec 는 20m 정도 크기 때문에 다른 라 이브 러 리 로 인 코딩 하기 로 했 습 니 다.인터넷 에서 faac 의 부피 가 작고 직접 디 코딩 을 하 는 것 을 발견 하여 faac 라 이브 러 리 를 인 코딩 모듈 로 사용 하기 로 결정 했다.
그러나 faac 의 원본 코드 에서 다음 과 같은 형식의 PCM 인 코딩 만 지원 합 니 다.
PCM Sample Input Format0 FAAC_INPUT_NULL invalid, signifies a misconfigured config1 FAAC_INPUT_16BIT native endian 16bit2 FAAC_INPUT_24BIT native endian 24bit in 24 bits (not implemented)3 FAAC_INPUT_32BIT native endian 24bit in 32 bits (DEFAULT)4 FAAC_INPUT_FLOAT 32bit floating point
내 가 인 코딩 하고 자 하 는 PCM 은 32bit float 인 데 faac 인 코딩 을 사용 할 때 항상 오류 가 발생 하여 ffmpeg 의 인 코딩 라 이브 러 리 를 사용 하여 자릿수 변환 을 하기 로 결정 했다.
[FFmpeg 및 오디 오 관련 개념]
4
enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1,
    AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
    AV_SAMPLE_FMT_S16,         ///< signed 16 bits
    AV_SAMPLE_FMT_S32,         ///< signed 32 bits
    AV_SAMPLE_FMT_FLT,         ///< float
    AV_SAMPLE_FMT_DBL,         ///< double

    AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
    AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
    AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
    AV_SAMPLE_FMT_FLTP,        ///< float, planar
    AV_SAMPLE_FMT_DBLP,        ///< double, planar
    AV_SAMPLE_FMT_S64,         ///< signed 64 bits
    AV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar

    AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
};
이것 은 ffmpeg 가 나열 한 오디 오 형식 으로 P 를 가 진 것 은 평면 데이터 임 을 나타 낸다.
오디 오 저장 형식의 두 가지 개념 을 소개 합 니 다. 스테레오 저장 소 와 더 블 사 운 드 채널 이 교차 하 다.
교차 스테레오 저장,즉 두 채널 의 오디 오 저장 형식 은 L(하나의 샘플링 포인트)RLRLRLR 입 니 다.
더 블 사 운 드 채널,즉 L(하나의 샘플링 포인트)LLLLLLLLLLRRRRRRRRRRRRRRRLLLLLLLLLLLLLRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
지금까지 FFmpeg API 에서 나 는 sample 만 있 는 것 을 발견 했다.fmt 는 AVSAMPLE_FMT_FLTP 의 오디 오 인 코더 가 열 릴 수 있 습 니 다.즉,인 코딩 형식 만 AV 로 지원 합 니 다.SAMPLE_FMT_FLTP 의 PCM 오디 오.
다른 형식의 PCM 데이터 인 코딩 은 지원 되 지 않 지만 모든 형식의 상호 변환 을 지원 하기 때문에 어떤 형식의 오디 오 를 가지 고 있 든 SwrContext 를 사용 하여 AV 로 변환 하면 됩 니 다.SAMPLE_FMT_FLTP,FFmpeg 인 코딩 을 사용 할 수 있 습 니 다.
【코드】
원본 PCM 데이터:
sample_rate = 48000
channels = 2
sample_fmt = AV_SAMPLE_FMT_FLT
대상 PCM 데이터:
sample_rate = 48000
channels = 2
sample_fmt = AV_SAMPLE_FMT_U8
원본 데이터 와 대상 데 이 터 는 모두 교차 스테레오 저장 소 입 니 다.(주의:FFmpeg PCM 형식 매 거 진 에 P 가 있 는 접 두 사 는 이 매개 변수 로 초기 화 된 인 코더 임 을 표시 합 니 다.원본 데 이 터 는 교차 스테레오 저장 소 여야 합 니 다)
	SwrContext *swr_ctx = NULL;

    FILE *fp_in = NULL;
	fp_in = fopen("in.pcm", "rb");
    FILE *outpcm = NULL;
	outpcm = fopen("out.pcm", "wb");

	uint8_t **convert_data;		
	uint8_t* frame_buf;
    int framenum = 100000;

    int nb_samples = 1024;
    int channels = 2;
    int samplerate = 48000;

	swr_ctx = swr_alloc_set_opts(
		NULL,
		AV_CH_LAYOUT_STEREO,
		AV_SAMPLE_FMT_U8,
		samplerate,
		AV_CH_LAYOUT_STEREO,
		AV_SAMPLE_FMT_FLT,
		samplerate,
		0, NULL);

	swr_init(swr_ctx);

	/*      ,    */
	convert_data = (uint8_t**)calloc(
        channels,
		sizeof(*convert_data));
	int sizes = av_samples_alloc(
        convert_data, NULL,
		channels, nb_samples,
		AV_SAMPLE_FMT_U8, 0);

    /*        */
	size = av_samples_get_buffer_size(NULL, channels, nb_samples, AV_SAMPLE_FMT_FLT, 0);

	frame_buf = (uint8_t *)av_malloc(size);

	ret = avcodec_fill_audio_frame(pFrame, channels, AV_SAMPLE_FMT_FLT, (const uint8_t*)frame_buf, size, 0);


	if (ret < 0)
	{
		qDebug() << "avcodec_fill_audio_frame error ";
		return 0;
	}


	for (i = 0; i < framenum; i++) {
		av_init_packet(&pkt);
		pkt.data = NULL;    // packet data will be allocated by the encoder
		pkt.size = 0;
		//Read raw data
		if (fread(frame_buf, 1, size, fp_in) <= 0) {
			printf("Failed to read raw data! 
"); return -1; } else if (feof(fp_in)) { break; } /* , ( )*/ swr_convert(swr_ctx, convert_data, nb_samples , (const uint8_t**)pFrame->data, nb_samples ); fwrite(convert_data[0], nb_samples * channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_U8), 1, outpcm); }

[코드 해석]
FFmpeg 처리 중 교차 스테레오 저장 및 2 성 모 노 채널 에서 구조 체 의 저장 방식 이 약간 다르다.
AVSAMPLE_FMT_FLT 등급 AVSAMPLE_FMT_U8,그러면 대상 데이터 의 메모리 공간 은 원본 데이터 의 1/4 만 필요 합 니 다.
다음은 avsample_alloc 무슨 일이 야?
int av_samples_alloc(uint8_t **audio_data, int *linesize, int nb_channels,
                     int nb_samples, enum AVSampleFormat sample_fmt, int align)
{
    uint8_t *buf;
    //        
    int size = av_samples_get_buffer_size(NULL, nb_channels, nb_samples,
                                          sample_fmt, align);
    if (size < 0)
        return size;

    buf = av_malloc(size);
    if (!buf)
        return AVERROR(ENOMEM);

    size = av_samples_fill_arrays(audio_data, linesize, buf, nb_channels,
                                  nb_samples, sample_fmt, align);
    if (size < 0) {
        av_free(buf);
        return size;
    }

    //  ...

    return size;
}
int av_samples_fill_arrays(uint8_t **audio_data, int *linesize,
                           const uint8_t *buf, int nb_channels, int nb_samples,
                           enum AVSampleFormat sample_fmt, int align)
{
    int ch, planar, buf_size, line_size;

    planar   = av_sample_fmt_is_planar(sample_fmt);
    buf_size = av_samples_get_buffer_size(&line_size, nb_channels, nb_samples,
                                          sample_fmt, align);
    if (buf_size < 0)
        return buf_size;

    audio_data[0] = (uint8_t *)buf;     //              
    for (ch = 1; planar && ch < nb_channels; ch++)
        audio_data[ch] = audio_data[ch-1] + line_size;

    if (linesize)
        *linesize = line_size;

    return buf_size;
}

이상 호출 avsamples_get_buffer_size 의 첫 번 째 매개 변 수 는 모든 채널 이 차지 하 는 바이트 수 를 되 돌려 줍 니 다.
이 PCM 이 평면 데이터 에 속한다 면 메모 리 는 모든 채널 에 평균 적 으로 분 배 됩 니 다.이때 audio 와 같 습 니 다.data[0] audio_data[1]....;
비 평면 데이터 에 대해 서 는 첫 번 째 요 소 를 audiodata[0]포인터
int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,
                               enum AVSampleFormat sample_fmt, int align)
{
    int line_size;
    int sample_size = av_get_bytes_per_sample(sample_fmt);
    int planar      = av_sample_fmt_is_planar(sample_fmt);

    /* validate parameter ranges */
    if (!sample_size || nb_samples <= 0 || nb_channels <= 0)
        return AVERROR(EINVAL);

    /* auto-select alignment if not specified */
    if (!align) {
        if (nb_samples > INT_MAX - 31)
            return AVERROR(EINVAL);
        align = 1;
        nb_samples = FFALIGN(nb_samples, 32);
    }

    /* check for integer overflow */
    if (nb_channels > INT_MAX / align ||
        (int64_t)nb_channels * nb_samples > (INT_MAX - (align * nb_channels)) / sample_size)
        return AVERROR(EINVAL);

    line_size = planar ? FFALIGN(nb_samples * sample_size,               align) :   //            
                         FFALIGN(nb_samples * sample_size * nb_channels, align);
    if (linesize)
        *linesize = line_size;

    return planar ? line_size * nb_channels : line_size;
}

왜냐하면 저희 가 AV 를 사용 하 거 든 요.SAMPLES_FMT_U8,그래서 결과 데 이 터 를 처리 할 때 convertdata[0]포인터.
avcodec_fill_audio_frame 호출 동 리 는 AV 이기 때 문 입 니 다.SAMPLES_FMT_FLT 비평 면 형식,pFrame->data[0]모든 데이터(좌우 채널)가리 키 기
사실 그들의 메모리 공간 은 똑 같이 크다.
비 planr:data[0]->[8192 바이트]
planr:data[0]->[4096 바이트]data[1]->[4096 바이트]
[평면 과 비 평면 형식의 상호 전환]
평면 데이터 에 몇 개의 채널 이 있 습 니까?그러면 data 2 차원 포인터 에 몇 개의 요소 가 메모 리 를 가리 키 고 있 습 니 다.예 를 들 어 3 개의 채널 이 라면.
그러면 AVFrame 의 data[0]data[1]data[2]는 각각 응답 위 치 를 가리 키 며 모든 채널 에 메모 리 를 제공 합 니 다(참고 로 모든 채널 의 데이터 크기 는 같 습 니 다).
다음은 FFmpeg 이 지원 하 는 layot 입 니 다.
/**
 * @}
 * @defgroup channel_mask_c Audio channel layouts
 * @{
 * */
#define AV_CH_LAYOUT_MONO              (AV_CH_FRONT_CENTER)
#define AV_CH_LAYOUT_STEREO            (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
#define AV_CH_LAYOUT_2POINT1           (AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY)
#define AV_CH_LAYOUT_2_1               (AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER)
#define AV_CH_LAYOUT_SURROUND          (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)
#define AV_CH_LAYOUT_3POINT1           (AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY)
#define AV_CH_LAYOUT_4POINT0           (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER)
#define AV_CH_LAYOUT_4POINT1           (AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY)
#define AV_CH_LAYOUT_2_2               (AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
#define AV_CH_LAYOUT_QUAD              (AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
#define AV_CH_LAYOUT_5POINT0           (AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
#define AV_CH_LAYOUT_5POINT1           (AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY)
#define AV_CH_LAYOUT_5POINT0_BACK      (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
#define AV_CH_LAYOUT_5POINT1_BACK      (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_LOW_FREQUENCY)
#define AV_CH_LAYOUT_6POINT0           (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_CENTER)
#define AV_CH_LAYOUT_6POINT0_FRONT     (AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
#define AV_CH_LAYOUT_HEXAGONAL         (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_BACK_CENTER)
#define AV_CH_LAYOUT_6POINT1           (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER)
#define AV_CH_LAYOUT_6POINT1_BACK      (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_BACK_CENTER)
#define AV_CH_LAYOUT_6POINT1_FRONT     (AV_CH_LAYOUT_6POINT0_FRONT|AV_CH_LOW_FREQUENCY)
#define AV_CH_LAYOUT_7POINT0           (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
#define AV_CH_LAYOUT_7POINT0_FRONT     (AV_CH_LAYOUT_5POINT0|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
#define AV_CH_LAYOUT_7POINT1           (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
#define AV_CH_LAYOUT_7POINT1_WIDE      (AV_CH_LAYOUT_5POINT1|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
#define AV_CH_LAYOUT_7POINT1_WIDE_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
#define AV_CH_LAYOUT_OCTAGONAL         (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_CENTER|AV_CH_BACK_RIGHT)
#define AV_CH_LAYOUT_HEXADECAGONAL     (AV_CH_LAYOUT_OCTAGONAL|AV_CH_WIDE_LEFT|AV_CH_WIDE_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT|AV_CH_TOP_BACK_CENTER|AV_CH_TOP_FRONT_CENTER|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT)
#define AV_CH_LAYOUT_STEREO_DOWNMIX    (AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT)

좋은 웹페이지 즐겨찾기