Я пытаюсь преобразовать потоковый аудиофайл (через AVIOContext
) в подписанный 16-битный PCM, используя библиотеку LibAV. Однако мне трудно читать автоматически сгенерированную документацию и примеры, чтобы выполнить то, что я пытаюсь сделать.
Ниже представлена моя попытка декодировать любой потоковый аудиофайл, предоставляемый функцией extern read_packet
, и передавать полученные данные PCM в функцию extern process_data
.
#define BUFFER_SIZE 4096
extern int read_packet(void *, uint8_t *, int);
extern void process_data(uint8_t *, int);
int perform_decoding() {
AVFormatContext *ctx = avformat_alloc_context();
uint8_t *buffer = av_malloc(BUFFER_SIZE);
AVIOContext *aioctx = avio_alloc_context(
buffer, // Buffer
BUFFER_SIZE, // Buffer size
0, // write_flag
NULL, // opaque
read_packet, // read_packet
NULL, // write_packet
NULL // seek
);
ctx->pb = aioctx;
avformat_open_input(&ctx, "stream", NULL, NULL);
if (avformat_find_stream_info(ctx, NULL) < 0) { return -1; }
int audio_stream_index = -1;
AVStream *stream = NULL;
for (int i = 0; i < ctx->nb_streams; i++) {
stream = ctx->streams[i];
if (stream->codec.codec_type == AVMEDIA_TYPE_AUDIO) {
audio_stream_index = i;
break;
}
}
if (audio_stream_index == -1) { return -2; }
AVCodecContext *cctx = stream->codec;
AVCodecParameters *params = stream->codecpar;
/// At this point we have
/// (1) The codec context
/// (2) The codec parameters for the incoming song (which can change per incoming file stream)
/// At this point I do not have the AVCodec* which I need to decode frames
/// And `cctx->codec` == NULL at this point (this would have been my AVCodec*)
/// ---------- Confusion starts below this line ----------
/// To the best of my knowledge, this is the best way to get one
/// I am not sure if one already exists that I can use.
AVCodec *codec = avcodec_find_decoder(cctx->codec_id);
if (codec == NULL) { return -3; } // Codec not found
/// These three commented out lines are lines I have always found when `avcodec_find_decoder` is performed
// cctx = avcodec_alloc_context3(codec); // Do I need?
// avcodec_alloc_context3(cctx, codec, NULL); // Do I need?
// avcodec_open2(cctx, codec, NULL) { return -3; } // Do I need?
/// ---------- Decoder section ----------
SwrContext *swr = swr_alloc_set_opts(
NULL, // Allocating a new context
params->channel_layout, // Input channel layout
params->format, // Input format
params->sample_rate, // Input sample rate
STEREO, // Output channel layout
AV_SAMPLE_FMT_S16, // Output format
48000, // Output sample rate
0, // Log offset
NULL // Log ctx
);
if (swr == NULL) { return -4; }
AVPacket *packet = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
while (av_read_frame(ctx, packet) >= 0) { // Fetch a packet
avcodec_send_packet(cctx, packet); // Toss the packet into the codec context
avcodec_receive_frame(cctx, frame); // Pull out a frame
/// Send the data to an external function that exects the decoded track in signed 16bit PCM format (bytes)
/// where the first argument is the pointer to the buffer and the second argument is the size
process_data(frame->data[0], frame->linesize[0]);
/// Do I need to reallocate these bellow? Or can I just reuse the packet & frame objects?
// packet = av_packet_alloc();
// frame = av_frame_alloc();
}
return 0;
}
Как указано, приложение ниже предоставит кадры без данных (размер 0). Когда я пытаюсь комментировать некоторые из разделов, в которых я не уверен, самое большее, что я могу получить, - это первый кадр, содержащий полный кадр нулей, за которым следуют полностью мусорные данные в каждом последующем кадре.
Итак, я считаю, что я неправильно инициализировал кодек, учитывая результаты. Все до комментируемой линии путаницы проверяется. Частота дискретизации, формат и каналы все оформить из параметров. Запуск av_dump_format(ctx, 0, "stream", 0)
даже обеспечивает правильную информацию об отладке трека, включая длину самого трека.