C 使用Libav对视频进行原始音频解码
我目前正在使用C 使用Libav对视频进行原始音频解码,c,ffmpeg,pcm,libav,C,Ffmpeg,Pcm,Libav,我目前正在使用libav将视频的音频流提取到原始PCM文件中 这段代码适用于mp3,但当我尝试使用mp4视频时,Audacity show上导入的原始格式奇怪地显示了0到-1之间的规则降序行 这是我的实现 #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <libavcodec/avcodec.h> #incl
libav
将视频的音频流提取到原始PCM文件中
这段代码适用于mp3,但当我尝试使用mp4视频时,Audacity show上导入的原始格式奇怪地显示了0到-1之间的规则降序行
这是我的实现
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
int decode_raw(AVFormatContext *format_ctx)
{
AVCodec *codec = NULL;
AVCodecContext* codec_ctx = NULL;
AVFrame* frame = NULL;
AVPacket packet;
int stream_idx = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
int res;
if (stream_idx < 0) {
printf("Could not find stream.\n");
return (1);
}
if ((codec_ctx = avcodec_alloc_context3(codec)) == NULL) {
printf("Could not allocate codec context.\n");
return (1);
}
if (avcodec_parameters_to_context(codec_ctx, format_ctx->streams[stream_idx]->codecpar) < 0) {
printf("Could not setup codec context parameters.\n");
return (1);
}
// Explicitly request non planar data.
codec_ctx->request_sample_fmt = av_get_packed_sample_fmt(codec_ctx->sample_fmt);
if (avcodec_open2(codec_ctx, codec, NULL) != 0) {
printf("Could not open codec.\n");
return (1);
}
if ((frame = av_frame_alloc()) == NULL) {
printf("Could not alloc frame.\n");
return (1);
}
av_init_packet(&packet);
int fd = open("raw", O_CREAT | O_WRONLY | O_TRUNC);
// Decode frames.
while ((res = av_read_frame(format_ctx, &packet)) == 0) {
// Does the packet belong to the correct stream?
if (packet.stream_index != stream_idx) {
av_packet_unref(&packet);
continue;
}
// We have a valid packet => send it to the decoder.
if ((res = avcodec_send_packet(codec_ctx, &packet)) != 0) {
printf("Failed to send packet: %d.\n", res);
break;
}
av_packet_unref(&packet);
res = avcodec_receive_frame(codec_ctx, frame);
if (res == AVERROR(EAGAIN) || res == AVERROR_EOF)
break;
else if (res < 0) {
printf("Failed to decode packet: %d.\n", res);
return (1);
}
write(fd, frame->extended_data[0], frame->linesize[0]);
}
close(fd);
av_frame_free(&frame);
avcodec_close(codec_ctx);
avcodec_free_context(&codec_ctx);
return (0);
}
int main(int argc, char **argv)
{
AVFormatContext *av_format_ctx = NULL;
if (argc != 2) {
printf("./streamer [file]\n");
return (1);
}
if (avformat_open_input(&av_format_ctx, argv[1], NULL, NULL) != 0) {
printf("Could not open input file.");
return (1);
}
if (avformat_find_stream_info(av_format_ctx, NULL) != 0) {
printf("Could not find stream information.");
return (1);
}
decode_raw(av_format_ctx);
avformat_close_input(&av_format_ctx);
return (0);
}
编辑#1 在经历了一连串令人失望的经历之后,AAC音频流在解码后似乎被破坏了。然而,ffmpeg的原始PCM输出对于MP4工作良好 我曾尝试使用
swr\u convert
对音频帧进行重新采样,但记录太差,导致了很多问题。问题
打印有关音频流的信息后。我注意到AAC(mp4文件的音频编解码器)不支持非平面格式(打包)
由于不支持请求的格式,mp4文件的音频流被解码为平面,与mp3文件不同
---------
Codec: MP3 (MPEG audio layer 3)
Supported sample formats: fltp, flt # MP3 support non planar
---------
Stream: 0
Sample Format: fltp
Sample Rate: 48000
Sample Size: 4
Channels: 2
Planar Output: yes
---------
Codec: AAC (Advanced Audio Coding)
Supported sample formats: fltp # AAC doesn't support non planar
---------
Stream: 1
Sample Format: fltp
Sample Rate: 44100
Sample Size: 4
Channels: 2
Planar Output: yes
解决方案 为了解决这个问题,我删除了上面的一行以保持流的平面。我还必须改变我在文件中的书写方式
void audio_pack_stream(AVCodecContext* codec_ctx, AVFrame *frame, uint8_t *dst, int *size)
{
int bytes = av_get_bytes_per_sample(codec_ctx->sample_fmt);
int actual = 0;
for (int i = 0; i < frame->nb_samples; i++) {
for(int j = 0; j < codec_ctx->channels; j++)
for (int k = 0; k < bytes; k++)
dst[*size++] = frame->extended_data[j][actual + k];
actual += bytes;
}
return (size);
}
// After avcodec_receive_frame
uint8_t output[4096 * 8];
int size;
audio_pack_stream(codec_ctx, frame, output, &size);
write(fd, output, size);
由于格式为平面LR,LR,LR
且未打包LL-RR
,因此我必须交替手动写入每个通道
因为逐字节写入需要很长时间,所以我编写了一个函数,在将缓冲区写入文件之前先写入缓冲区
void audio_pack_stream(AVCodecContext* codec_ctx, AVFrame *frame, uint8_t *dst, int *size)
{
int bytes = av_get_bytes_per_sample(codec_ctx->sample_fmt);
int actual = 0;
for (int i = 0; i < frame->nb_samples; i++) {
for(int j = 0; j < codec_ctx->channels; j++)
for (int k = 0; k < bytes; k++)
dst[*size++] = frame->extended_data[j][actual + k];
actual += bytes;
}
return (size);
}
// After avcodec_receive_frame
uint8_t output[4096 * 8];
int size;
audio_pack_stream(codec_ctx, frame, output, &size);
write(fd, output, size);
void audio\u pack\u流(AVCodecContext*codec\u ctx、AVFrame*frame、uint8\u t*dst、int*size)
{
int bytes=av_get_bytes_per_sample(编解码器ctx->sample_fmt);
int实际值=0;
对于(int i=0;inb_样本;i++){
对于(int j=0;jchannels;j++)
for(int k=0;kextended_data[j][actual+k];
实际+=字节;
}
返回(大小);
}
//avcodec_接收_帧后
uint8_t输出[4096*8];
整数大小;
音频包流(编解码器ctx、帧、输出和大小);
写入(fd、输出、大小);
void audio_pack_stream(AVCodecContext* codec_ctx, AVFrame *frame, uint8_t *dst, int *size)
{
int bytes = av_get_bytes_per_sample(codec_ctx->sample_fmt);
int actual = 0;
for (int i = 0; i < frame->nb_samples; i++) {
for(int j = 0; j < codec_ctx->channels; j++)
for (int k = 0; k < bytes; k++)
dst[*size++] = frame->extended_data[j][actual + k];
actual += bytes;
}
return (size);
}
// After avcodec_receive_frame
uint8_t output[4096 * 8];
int size;
audio_pack_stream(codec_ctx, frame, output, &size);
write(fd, output, size);