本来不是什么难搞的问题,代码写完了,音频流信息中的详细信息,具体代码表现为
format_ctx->streams[audio_stream_index]->codecpar是空指针。
这个查了一圈也没人给出正确答案,实际上是由于我自己编译的ffmpeg时候,开启的选项的导致的。把音频解码器相关的给禁掉了。重新开启相关编译选项,编译ffmpeg后,一切正常。
具体的选项为:
ffmpeg 交叉编译
./configure --prefix=../arm-ffmpegbuild \
--enable-shared \
--enable-libmp3lame \
--enable-libx264 \
--enable-gpl \
--disable-asm \
--enable-version3 \
--enable-libmp3lame \
--enable-libx264 \
--enable-libvpx \
--enable-nonfree \
--cross-prefix=aarch64-linux- \
--target-os=linux \
--extra-cflags="-I /opt/ffmpeg_test_make/lame-3.100/lamebuild/include" \
--extra-ldflags="-L /opt/ffmpeg_test_make/lame-3.100/lamebuild/lib" \
--enable-cross-compile \
--enable-small \
--arch=arm64 \
--enable-decoder=h264 \
--enable-parser=h264 \
--enable-demuxer=rtsp \
--extra-ldflags="-L ../x264build/lib" \
--extra-cflags="-I ../x264build/include"
lame交叉编译
./configure \
--host=aarch64-linux \
--prefix=/opt/ffmpeg_test_make/lame-3.100/lamebuild \
cc=aarch64-linux-gcc
话不多说上代码:
bool FfpDecoderWav::dump_wav(std::string rtsp_url, std::string file_path) {
AVDictionary *format_options = NULL;
av_dict_set(&format_options, "rtsp_transport", "tcp", 0); // 以tcp的方式打开,
av_register_all();
avformat_network_init();
// 打开 RTSP 流
int reconnect_times = 3;
AVFormatContext *format_ctx = NULL;
bool online = false;
while (reconnect_times-- > 0) {
if (format_ctx != NULL) {
avformat_close_input(&format_ctx);
format_ctx = NULL;
}
format_ctx = avformat_alloc_context();
if (avformat_open_input(&format_ctx, rtsp_url.c_str(), NULL, &format_options) != 0) {
Logger::error("open rtsp url:{} faile", rtsp_url);
// std::this_thread::sleep_for(std::chrono::milliseconds(500));
usleep(100000);
} else {
online = true;
break;
}
}
av_dict_free(&format_options); // 释放 format_options
if (!online) {
return false;
}
Logger::info("open rtsp url:{} for wav success", rtsp_url);
// 查找音频流
int audio_stream_index = -1;
if (avformat_find_stream_info(format_ctx, NULL) < 0) {
Logger::info("can not avformat_find_stream_info url:{}", rtsp_url);
return false;
}
AVCodec *codec = NULL;
audio_stream_index = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
std::cout << "codec name :" << codec->name << std::endl;
std::cout << "codec long_name :" << codec->long_name << std::endl;
std::cout << "codec AVMediaType :" << (int)codec->type << std::endl;
std::cout << "codec AVCodecID :" << (int)codec->id << std::endl;
if (audio_stream_index < 0 || codec == NULL) {
Logger::info("can not find sound stream rtsp url:{}", rtsp_url);
return false;
}
Logger::info("find sound stream success index:{}", audio_stream_index);
av_dump_format(format_ctx, 0, rtsp_url.c_str(), 0);
bool had_audio_code = true;
SwrContext *swr_ctx = NULL;
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
if (format_ctx->streams[audio_stream_index]->codecpar) {
Logger::info("avcodec_alloc_context3 success channels={}", codec_ctx->channels);
Logger::info("avcodec_alloc_context3 success sample_rate={}", codec_ctx->sample_rate);
// std::cout << "had codecpar inf" << std::endl;
// printf("had codecpar inf\n");
avcodec_parameters_to_context(codec_ctx, format_ctx->streams[audio_stream_index]->codecpar);
if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
Logger::info("avcodec_open2 error rtsp url:{}", rtsp_url);
return false;
}
Logger::info("avcodec_open2 success channels={}", codec_ctx->channels);
Logger::info("avcodec_open2 success sample_rate={}", codec_ctx->sample_rate);
// 创建重采样上下文
swr_ctx = swr_alloc_set_opts(NULL, NUM_CHANNELS, AV_SAMPLE_FMT_S16, SAMPLE_RATE, codec_ctx->channels,
codec_ctx->sample_fmt, codec_ctx->sample_rate, 0, NULL);
Logger::info("swr_alloc_set_opts success");
if (!swr_ctx || swr_init(swr_ctx) < 0) {
// Logger::info("swr_init error rtsp url:{}", rtsp_url);
return false;
}
} else {
printf("cdecpar is nullodecpar is nullodecpar is nullodecpar is null\n");
std::cout << "codecpar is null" << std::endl;
had_audio_code = false;
}
// 创建输出 WAV 文件
std::ofstream wav_file(file_path.c_str(), std::ios::binary);
if (!wav_file) {
// Logger::info("fopen local_path save wav failed path:{}", file_path);
return false;
}
// Logger::info("open wav_file success");
// 写入 WAV 文件头
WAVHeader wav_header;
unsigned int file_size = sizeof(wav_header);
// Logger::info("wav_header size:{}", file_size);
wav_file.write((const char *)&wav_header, file_size);
time_t start_time = time(NULL);
AVPacket packet;
int ret = 0;
int count = 1000;
while (true) {
if (ret = av_read_frame(format_ctx, &packet) < 0) {
// Logger::info("av_read_frame failed: {}", ret);
break;
}
time_t current_time = time(NULL);
time_t duration = current_time - start_time;
if (duration > 60) {
// Logger::info("save sound end by 20 s time");
break;
}
if (packet.stream_index == audio_stream_index) {
if (!had_audio_code) {
wav_file.write((char *)packet.data, packet.size);
std::cout << "write sws data codecpar inf insfsjfjaslkjfas" << std::endl;
printf(" wav_file.write((char *)packet.data, packet.size);\n");
continue;
}
AVFrame *frame = av_frame_alloc();
if (avcodec_send_packet(codec_ctx, &packet) >= 0 && avcodec_receive_frame(codec_ctx, frame) >= 0) {
uint8_t *out_buffer[NUM_CHANNELS];
int out_samples = 0;
int out_size = 0;
for (int i = 0; i < NUM_CHANNELS; i++) {
out_buffer[i] = (uint8_t *)malloc(frame->nb_samples * 2 * sizeof(uint8_t));
}
out_samples = swr_convert(swr_ctx, out_buffer, frame->nb_samples, (const uint8_t **)frame->data,
frame->nb_samples);
out_size = out_samples * NUM_CHANNELS * 2;
wav_file.write(reinterpret_cast<char *>(out_buffer[0]), out_size);
// std::cout << "write sws data codecpar inf" << std::endl;
// printf(" wav_file.write(reinterpret_cast<char *>(out_buffer[0]), out_siz22;\n");
for (int i = 0; i < NUM_CHANNELS; i++) {
free(out_buffer[i]);
}
}
av_frame_free(&frame);
}
av_packet_unref(&packet);
}
// 更新 WAV 文件头中的数据大小
uint32_t subchunk2Size = static_cast<unsigned int>(wav_file.tellp()) - 44;
uint32_t chunkSize = subchunk2Size + 36;
wav_file.seekp(4, std::ios::beg);
wav_file.write(reinterpret_cast<char *>(&chunkSize), 4);
wav_file.seekp(40, std::ios::beg);
wav_file.write(reinterpret_cast<char *>(&subchunk2Size), 4);
// 关闭文件
wav_file.close();
// 释放资源
avcodec_close(codec_ctx);
avcodec_free_context(&codec_ctx);
avformat_close_input(&format_ctx);
swr_free(&swr_ctx);
// Logger::info("save local_path wav success path:{}", file_path);
return true;
}
wav格式的数据头文件:
struct WAVHeader {
char chunkID[4] = {'R', 'I', 'F', 'F'};
uint32_t chunkSize = 0;
char format[4] = {'W', 'A', 'V', 'E'};
char subchunk1ID[4] = {'f', 'm', 't', ' '};
uint32_t subchunk1Size = 16;
uint16_t audioFormat = 1;
uint16_t numChannels = NUM_CHANNELS;
uint32_t sampleRate = SAMPLE_RATE;
uint32_t byteRate = SAMPLE_RATE * NUM_CHANNELS * 16 / 8;
uint16_t blockAlign = 4;
uint16_t bitsPerSample = 16;
char subchunk2ID[4] = {'d', 'a', 't', 'a'};
uint32_t subchunk2Size = 4;
};
最后,就是wav注意的地方,一共是两个值:
chunkSize 和subchunk2Size
// 更新 WAV 文件头中的数据大小
也就是说:subchunk2Size是出去wav文件头部数据意外的数据长度。
即文件总长度减去头部长度44个字节。
chunkSize=subchunk2Size+36
具体为什么,可以查看wav格式的说明。