Преобразование.m4a в PCM с помощью libavcodec

78
8

Я пытаюсь преобразовать файл.m4a в необработанный файл PCM, чтобы воспроизвести его в Audacity.

Согласно AVCodecContext, это дорожка 44100 Гц, использующая формат образца AV_SAMPLE_FMT_FLTP, который, насколько мне известно, при декодировании с использованием avcodec_decode_audio4 я должен получить два массива значений с плавающей запятой (по одному для каждого канала).

Я не уверен в значении AVCodecContext bits_per_coded_sample = 16

К сожалению, Audacity воспроизводит результат так, как будто исходный трек смешивается с некоторым белым шумом.

Вот пример кода того, что я сделал. Обратите внимание: я также добавил случай для трека, который использует подписанные 16-битные неперемещаемые данные (sample_format = AC_SAMPLE_FMT_S16P), которые Audacity воспроизводит отлично.

int AudioDecoder::decode(std::string path)
{
const char* input_filename=path.c_str();

av_register_all();

AVFormatContext* container=avformat_alloc_context();
if(avformat_open_input(&container,input_filename,NULL,NULL)<0){
printf("Could not open file");
}

if(avformat_find_stream_info(container, NULL)<0){
printf("Could not find file info");
}
av_dump_format(container,0,input_filename,false);

int stream_id=-1;
int i;
for(i=0;i<container->nb_streams;i++){
if(container->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){
stream_id=i;
break;
}
}
if(stream_id==-1){
printf("Could not find Audio Stream");
}

AVDictionary *metadata=container->metadata;
AVCodecContext *ctx=container->streams[stream_id]->codec;
AVCodec *codec=avcodec_find_decoder(ctx->codec_id);

if(codec==NULL){
printf("cannot find codec!");
}

if(avcodec_open2(ctx,codec,NULL)<0){
printf("Codec cannot be found");
}

AVSampleFormat sfmt = ctx->sample_fmt;

AVPacket packet;
av_init_packet(&packet);
AVFrame *frame = avcodec_alloc_frame();

int buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE+ FF_INPUT_BUFFER_PADDING_SIZE;;
uint8_t buffer[buffer_size];
packet.data=buffer;
packet.size =buffer_size;

FILE *outfile = fopen("test.raw", "wb");

int len;
int frameFinished=0;

while(av_read_frame(container,&packet) >= 0)
{
if(packet.stream_index==stream_id)
{
//printf("Audio Frame read \n");
int len=avcodec_decode_audio4(ctx, frame, &frameFinished, &packet);

if(frameFinished)
{
if (sfmt==AV_SAMPLE_FMT_S16P)
{ // Audacity: 16bit PCM little endian stereo
int16_t* ptr_l = (int16_t*)frame->extended_data[0];
int16_t* ptr_r = (int16_t*)frame->extended_data[1];
for (int i=0; i<frame->nb_samples; i++)
{
fwrite(ptr_l++, sizeof(int16_t), 1, outfile);
fwrite(ptr_r++, sizeof(int16_t), 1, outfile);
}
}
else if (sfmt==AV_SAMPLE_FMT_FLTP)
{ //Audacity: big endian 32bit stereo start offset 7 (but has noise)
float* ptr_l = (float*)frame->extended_data[0];
float* ptr_r = (float*)frame->extended_data[1];
for (int i=0; i<frame->nb_samples; i++)
{
fwrite(ptr_l++, sizeof(float), 1, outfile);
fwrite(ptr_r++, sizeof(float), 1, outfile);
}
}
}
}
}
fclose(outfile);
av_close_input_file(container);
return 0;

}

Я надеюсь, что только что сделал наивное преобразование (большинство/менее значительных проблем с бит), но в настоящее время я не смог понять это. Обратите внимание, что Audacity может импортировать только данные RAW с плавающей запятой, если их 32-битный или 64-битный поплавок (большой или малый endian).

Спасибо за понимание.

спросил(а) 2021-01-19T11:20:12+03:00 6 месяцев, 2 недели назад
1
Решение
117

Вы должны использовать конвертер AV_SAMPLE_FMT_FLTP в AC_SAMPLE_FMT_S16P

Как конвертировать частоту дискретизации с AV_SAMPLE_FMT_FLTP в AV_SAMPLE_FMT_S16?

Вот рабочий пример (в pAudioBuffer у вас есть данные pcm в белом носе):

SwrContext *swr;
swr=swr_alloc();
av_opt_set_int(swr,"in_channel_layout",2,0);
av_opt_set_int(swr, "out_channel_layout", 2, 0);
av_opt_set_int(swr, "in_sample_rate", codecContext->sample_rate, 0);
av_opt_set_int(swr, "out_sample_rate", codecContext->sample_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16P, 0);
swr_init(swr);
int16_t * pAudioBuffer = (int16_t *) av_malloc (AUDIO_INBUF_SIZE * 2);
while(av_read_frame(fmt_cntx,&readingPacket)==0){
if(readingPacket.stream_index==audioSteam->index){
AVPacket decodingPacket=readingPacket;
while(decodingPacket.size>0){
int gotFrame=0;
int result=avcodec_decode_audio4(codecContext,frame,&gotFrame,&decodingPacket);
if(result<0){
av_frame_free(&frame);
avformat_close_input(&fmt_cntx);
return null;
}
if(result>=0 && gotFrame){
int data_size=frame->nb_samples*frame->channels;
swr_convert(swr,&pAudioBuffer,frame->nb_samples,frame->extended_data,frame->nb_samples);
jshort *outShortArray=(*pEnv)->NewShortArray(pEnv,data_size);
(*pEnv)->SetShortArrayRegion(pEnv,outShortArray,0,data_size,pAudioBuffer);
(*pEnv)->CallVoidMethod(pEnv,pObj,callBackShortBuffer,outShortArray,data_size);
(*pEnv)->DeleteLocalRef(pEnv,outShortArray);
decodingPacket.size -= result;
decodingPacket.data += result;
}else{
decodingPacket.size=0;
decodingPacket.data=NULL;
}}
av_free_packet(&decodingPacket);
}

ответил(а) 2021-01-19T11:20:12+03:00 6 месяцев, 2 недели назад
45

Я думаю, проблема в "nb_samples". Это не совсем вам нужно. Лучше попробовать "lineize [0]".

Пример:


char* ptr_l = (char*)frame->extended_data[0];
char* ptr_r = (char*)frame->extended_data[1];
size_t size = sizeof(float);
for (int i=0; i<frame->linesize[0]; i+=size)
{
fwrite(ptr_l, size, 1, outfile);
fwrite(ptr_r, size, 1, outfile);
ptr_l += size;
ptr_r += size;
}

Это для "float", и повторите то же для "int16_t". Но "размер" будет "sizeof (int16_t)"

ответил(а) 2021-01-19T11:20:12+03:00 6 месяцев, 2 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема