Files

211 lines
5.8 KiB
C

#include "ffmpeg_audio.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/channel_layout.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>
#ifdef WIN32
#define fprintf fprintf_s
#endif
static void write_u16_le(FILE *f, uint16_t v)
{
fputc((int)(v & 0xff), f);
fputc((int)((v >> 8) & 0xff), f);
}
static void write_u32_le(FILE *f, uint32_t v)
{
fputc((int)(v & 0xff), f);
fputc((int)((v >> 8) & 0xff), f);
fputc((int)((v >> 16) & 0xff), f);
fputc((int)((v >> 24) & 0xff), f);
}
static int write_wav_header(FILE *f,
int sample_rate,
int channels,
int bits_per_sample,
uint32_t data_size)
{
const uint32_t byte_rate =
(uint32_t)(sample_rate * channels * bits_per_sample / 8);
const uint16_t block_align =
(uint16_t)(channels * bits_per_sample / 8);
fwrite("RIFF", 1, 4, f);
write_u32_le(f, 36u + data_size);
fwrite("WAVE", 1, 4, f);
fwrite("fmt ", 1, 4, f);
write_u32_le(f, 16); /* fmt chunk size */
write_u16_le(f, 1); /* PCM */
write_u16_le(f, (uint16_t)channels);
write_u32_le(f, (uint32_t)sample_rate);
write_u32_le(f, byte_rate);
write_u16_le(f, block_align);
write_u16_le(f, (uint16_t)bits_per_sample);
fwrite("data", 1, 4, f);
write_u32_le(f, data_size);
return ferror(f) ? 0 : 1;
}
static int rewrite_wav_header(FILE *f,
int sample_rate,
int channels,
int bits_per_sample,
uint32_t data_size)
{
if (fseek(f, 0, SEEK_SET) != 0) {
return 0;
}
return write_wav_header(f,
sample_rate,
channels,
bits_per_sample,
data_size);
}
static void print_if_present(const char *label, const char *value)
{
if (value && value[0]) {
printf("%s: %s\n", label, value);
}
}
int main(int argc, char **argv)
{
const char *infile;
const char *outfile;
fmpg_instance *fmpg = NULL;
FILE *out = NULL;
int sample_rate;
int channels;
int bits_per_sample;
uint64_t total_written = 0;
if (argc != 3) {
fprintf(stderr, "usage: %s input-audio output.wav\n", argv[0]);
return 1;
}
infile = argv[1];
outfile = argv[2];
fprintf(stderr, "ffmpeg compiled version: %s\n", fmpg_ffmpeg_version());
fprintf(stderr, "ffmpeg runtime version avformat: %d - %s\n", avformat_version(), fmpg_int_version2string(avformat_version()));
//fprintf(stderr, "ffmpeg runtme config string: %s\n", avformat_configuration());
fmpg = fmpg_init();
if (!fmpg) {
fprintf(stderr, "fmpg_init failed\n");
return 1;
}
if (!fmpg_open_file(fmpg, infile)) {
fprintf(stderr, "could not open input file: %s\n", infile);
fmpg_free(fmpg);
return 1;
}
sample_rate = fmpg_audio_sample_rate(fmpg);
channels = fmpg_audio_channels(fmpg);
bits_per_sample = fmpg_audio_bits_per_sample(fmpg);
if (sample_rate <= 0 || channels <= 0 || bits_per_sample != 32) {
fprintf(stderr, "unexpected audio format reported by decoder\n");
fmpg_free(fmpg);
return 1;
}
out = fopen(outfile, "wb");
if (!out) {
fprintf(stderr, "could not open output file: %s\n", outfile);
fmpg_free(fmpg);
return 1;
}
/*
* The final WAV data size is only known after decoding. Write a temporary
* header now and patch it after the decode loop.
*/
if (!write_wav_header(out,
sample_rate,
channels,
bits_per_sample,
0)) {
fprintf(stderr, "could not write WAV header\n");
fclose(out);
fmpg_free(fmpg);
return 1;
}
while (fmpg_decode_next(fmpg)) {
const uint8_t *buf = fmpg_buffer(fmpg);
const int size = fmpg_buffer_size(fmpg);
if (buf && size > 0) {
if (fwrite(buf, 1, (size_t)size, out) != (size_t)size) {
fprintf(stderr, "write error\n");
fclose(out);
fmpg_free(fmpg);
return 1;
}
total_written += (uint64_t)size;
}
}
if (total_written > 0xffffffffu) {
fprintf(stderr,
"warning: WAV data larger than 4GB; classic WAV header "
"will overflow\n");
}
if (!rewrite_wav_header(out,
sample_rate,
channels,
bits_per_sample,
(uint32_t)total_written)) {
fprintf(stderr, "could not rewrite WAV header\n");
fclose(out);
fmpg_free(fmpg);
return 1;
}
fclose(out);
printf("wrote: %s\n", outfile);
printf("sample rate: %d\n", sample_rate);
printf("channels: %d\n", channels);
printf("sample bits: %d\n", bits_per_sample);
printf("data bytes: %llu\n", (unsigned long long)total_written);
printf("samples out: %lld\n", (long long)fmpg_sample_position(fmpg));
if (fmpg_duration_ms(fmpg) >= 0) {
printf("duration ms: %lld\n", (long long)fmpg_duration_ms(fmpg));
}
if (fmpg_duration_samples(fmpg) >= 0) {
printf("duration smp:%lld\n", (long long)fmpg_duration_samples(fmpg));
}
print_if_present("title", fmpg_file_title(fmpg));
print_if_present("artist", fmpg_file_author(fmpg));
print_if_present("album", fmpg_file_album(fmpg));
fmpg_free(fmpg);
return 0;
}