如何在 iOS 上将 flac 转换为 wav?
How do I convert flac to wav on iOS?
我有一个使用 FLAC 编码的文件,我想将其转换为 WAV。
我已将 this FFMpeg lib 添加到我的项目并导入它。
我从 this answer 看到了一些代码,但我不清楚如何使用它:
#import "avformat.h"
// Some code goes here
/*
* avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)
*/
int openInputValue = avformat_open_input(&pFormatCtx, utf8FilePath, inputFormat, nil);
NSLog(@"%s - %d # openInputValue = %d", __PRETTY_FUNCTION__, __LINE__, openInputValue);
我目前在一个函数中有此代码,该函数需要 NSData
保存 FLAC 文件。 如果 avformat_open_input
是正确的调用,我该如何设置变量?如果不是正确的调用,那是什么?
This question 似乎是重复的,但实际上并没有很好的答案。
另请注意,我不想要播放器。此文件包含 MQA,因此我需要通过我自己的自定义解码器 运行 它。
我能够使用 this code for decoding and 实际编写 WAV header/body。
作为额外的好处,this 在解码 NSData 而不是文件方面非常有帮助。
这是我完成的解码器,但我不希望它在任何情况下都能工作,除了我的。
//
// FlacToWavConverter.m
// SuperpoweredMQAExample
//
// Created by Tony Lawrence on 5/18/17.
// Copyright © 2017 imect. All rights reserved.
//
#import "FlacToWavConverter.h"
#import "avformat.h"
#import "avcodec.h"
#import "avutil.h"
#import "swresample.h"
#import "file.h"
@implementation FlacToWavConverter
+(NSURL*)convertFlacToWav:(NSData*)data {
//const char* input_filename = [filePath UTF8String];
int buffer_size = 16384;
// This call is necessarily done once in your app to initialize
// libavformat to register all the muxers, demuxers and protocols.
av_register_all();
// A media container
AVFormatContext* container = avformat_alloc_context();
//Make a custom IO context so that we can read from memory instead of a file...
unsigned char* iobuffer = av_malloc(buffer_size);
struct buffer_data bd = { 0 };
bd.ptr = (uint8_t*)data.bytes;
bd.size = data.length;
AVIOContext* ioContext = avio_alloc_context(iobuffer, buffer_size, 0, &bd, &read_packet, NULL, NULL);
container->pb = ioContext;
if (avformat_open_input(&container, "arbitrary", NULL, NULL) < 0) {
NSLog(@"Could not open file");
}
if (avformat_find_stream_info(container, NULL) < 0) {
NSLog(@"Could not find file info");
}
int stream_id = -1;
// To find the first audio stream. This process may not be necessary
// if you can gurarantee that the container contains only the desired
// audio stream
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) {
NSLog(@"Could not find an audio stream");
}
// Extract some metadata
AVDictionary* metadata = container->metadata;
// Find the apropriate codec and open it
AVCodecContext* codec_context = container->streams[stream_id]->codec;
AVCodec* codec = avcodec_find_decoder(codec_context->codec_id);
if (avcodec_open2(codec_context, codec, NULL) < 0) {
NSLog(@"Could not find open the needed codec");
}
NSMutableData *pcmFile = [NSMutableData new];
AVPacket packet;
int8_t buffer[buffer_size];
while (1) {
//buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
// Read one packet into `packet`
if (av_read_frame(container, &packet) < 0) {
break; // End of stream. Done decoding.
}
// Decodes from `packet` into the buffer
if (avcodec_decode_audio3(codec_context, (int16_t*)buffer, &buffer_size, &packet) < 1) {
break; // Error in decoding
}
// Send the buffer contents to the audio device
[pcmFile appendBytes:buffer length:buffer_size];
}
avformat_close_input(&container);
//fprintf(stdout, "Done playing. Exiting...");
NSURL *file = [FlacToWavConverter getAndCreatePlayableFileFromPcmData:pcmFile];
NSLog(@"Got a playable file maybe? %@", [file absoluteString]);
return file;
}
+(NSURL *) getAndCreatePlayableFileFromPcmData:(NSData *)data
{
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *wavFilePath = [docsDir stringByAppendingPathComponent:@"output.wav"];
//NSLog(@"PCM data : %@",data);
FILE *fout;
short NumChannels = 2;
short BitsPerSample = 16;
int SamplingRate = 44100;
int numOfSamples = [data length];
int ByteRate = NumChannels*BitsPerSample*SamplingRate/8;
short BlockAlign = NumChannels*BitsPerSample/8;
int DataSize = NumChannels*numOfSamples*BitsPerSample/8;
int chunkSize = 16;
int totalSize = 46 + DataSize;
short audioFormat = 1;
if((fout = fopen([wavFilePath cStringUsingEncoding:1], "w")) == NULL)
{
printf("Error opening out file ");
}
fwrite("RIFF", sizeof(char), 4,fout);
fwrite(&totalSize, sizeof(int), 1, fout);
fwrite("WAVE", sizeof(char), 4, fout);
fwrite("fmt ", sizeof(char), 4, fout);
fwrite(&chunkSize, sizeof(int),1,fout);
fwrite(&audioFormat, sizeof(short), 1, fout);
fwrite(&NumChannels, sizeof(short),1,fout);
fwrite(&SamplingRate, sizeof(int), 1, fout);
fwrite(&ByteRate, sizeof(int), 1, fout);
fwrite(&BlockAlign, sizeof(short), 1, fout);
fwrite(&BitsPerSample, sizeof(short), 1, fout);
fwrite("data", sizeof(char), 4, fout);
fwrite(&DataSize, sizeof(int), 1, fout);
fclose(fout);
NSMutableData *pamdata = [NSMutableData dataWithData:data];
NSFileHandle *handle;
handle = [NSFileHandle fileHandleForUpdatingAtPath:wavFilePath];
[handle seekToEndOfFile];
[handle writeData:pamdata];
[handle closeFile];
NSLog(@"Saved wav: %@", wavFilePath);
return [NSURL URLWithString:wavFilePath];
}
struct buffer_data {
uint8_t *ptr;
size_t size; ///< size left in the buffer
};
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
struct buffer_data *bd = (struct buffer_data *)opaque;
buf_size = FFMIN(buf_size, bd->size);
//printf("ptr:%p size:%zu\n", bd->ptr, bd->size);
/* copy internal buffer data to buf */
memcpy(buf, bd->ptr, buf_size);
bd->ptr += buf_size;
bd->size -= buf_size;
return buf_size;
}
@end
我有一个使用 FLAC 编码的文件,我想将其转换为 WAV。
我已将 this FFMpeg lib 添加到我的项目并导入它。
我从 this answer 看到了一些代码,但我不清楚如何使用它:
#import "avformat.h"
// Some code goes here
/*
* avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)
*/
int openInputValue = avformat_open_input(&pFormatCtx, utf8FilePath, inputFormat, nil);
NSLog(@"%s - %d # openInputValue = %d", __PRETTY_FUNCTION__, __LINE__, openInputValue);
我目前在一个函数中有此代码,该函数需要 NSData
保存 FLAC 文件。 如果 avformat_open_input
是正确的调用,我该如何设置变量?如果不是正确的调用,那是什么?
This question 似乎是重复的,但实际上并没有很好的答案。
另请注意,我不想要播放器。此文件包含 MQA,因此我需要通过我自己的自定义解码器 运行 它。
我能够使用 this code for decoding and
作为额外的好处,this 在解码 NSData 而不是文件方面非常有帮助。
这是我完成的解码器,但我不希望它在任何情况下都能工作,除了我的。
//
// FlacToWavConverter.m
// SuperpoweredMQAExample
//
// Created by Tony Lawrence on 5/18/17.
// Copyright © 2017 imect. All rights reserved.
//
#import "FlacToWavConverter.h"
#import "avformat.h"
#import "avcodec.h"
#import "avutil.h"
#import "swresample.h"
#import "file.h"
@implementation FlacToWavConverter
+(NSURL*)convertFlacToWav:(NSData*)data {
//const char* input_filename = [filePath UTF8String];
int buffer_size = 16384;
// This call is necessarily done once in your app to initialize
// libavformat to register all the muxers, demuxers and protocols.
av_register_all();
// A media container
AVFormatContext* container = avformat_alloc_context();
//Make a custom IO context so that we can read from memory instead of a file...
unsigned char* iobuffer = av_malloc(buffer_size);
struct buffer_data bd = { 0 };
bd.ptr = (uint8_t*)data.bytes;
bd.size = data.length;
AVIOContext* ioContext = avio_alloc_context(iobuffer, buffer_size, 0, &bd, &read_packet, NULL, NULL);
container->pb = ioContext;
if (avformat_open_input(&container, "arbitrary", NULL, NULL) < 0) {
NSLog(@"Could not open file");
}
if (avformat_find_stream_info(container, NULL) < 0) {
NSLog(@"Could not find file info");
}
int stream_id = -1;
// To find the first audio stream. This process may not be necessary
// if you can gurarantee that the container contains only the desired
// audio stream
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) {
NSLog(@"Could not find an audio stream");
}
// Extract some metadata
AVDictionary* metadata = container->metadata;
// Find the apropriate codec and open it
AVCodecContext* codec_context = container->streams[stream_id]->codec;
AVCodec* codec = avcodec_find_decoder(codec_context->codec_id);
if (avcodec_open2(codec_context, codec, NULL) < 0) {
NSLog(@"Could not find open the needed codec");
}
NSMutableData *pcmFile = [NSMutableData new];
AVPacket packet;
int8_t buffer[buffer_size];
while (1) {
//buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
// Read one packet into `packet`
if (av_read_frame(container, &packet) < 0) {
break; // End of stream. Done decoding.
}
// Decodes from `packet` into the buffer
if (avcodec_decode_audio3(codec_context, (int16_t*)buffer, &buffer_size, &packet) < 1) {
break; // Error in decoding
}
// Send the buffer contents to the audio device
[pcmFile appendBytes:buffer length:buffer_size];
}
avformat_close_input(&container);
//fprintf(stdout, "Done playing. Exiting...");
NSURL *file = [FlacToWavConverter getAndCreatePlayableFileFromPcmData:pcmFile];
NSLog(@"Got a playable file maybe? %@", [file absoluteString]);
return file;
}
+(NSURL *) getAndCreatePlayableFileFromPcmData:(NSData *)data
{
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *wavFilePath = [docsDir stringByAppendingPathComponent:@"output.wav"];
//NSLog(@"PCM data : %@",data);
FILE *fout;
short NumChannels = 2;
short BitsPerSample = 16;
int SamplingRate = 44100;
int numOfSamples = [data length];
int ByteRate = NumChannels*BitsPerSample*SamplingRate/8;
short BlockAlign = NumChannels*BitsPerSample/8;
int DataSize = NumChannels*numOfSamples*BitsPerSample/8;
int chunkSize = 16;
int totalSize = 46 + DataSize;
short audioFormat = 1;
if((fout = fopen([wavFilePath cStringUsingEncoding:1], "w")) == NULL)
{
printf("Error opening out file ");
}
fwrite("RIFF", sizeof(char), 4,fout);
fwrite(&totalSize, sizeof(int), 1, fout);
fwrite("WAVE", sizeof(char), 4, fout);
fwrite("fmt ", sizeof(char), 4, fout);
fwrite(&chunkSize, sizeof(int),1,fout);
fwrite(&audioFormat, sizeof(short), 1, fout);
fwrite(&NumChannels, sizeof(short),1,fout);
fwrite(&SamplingRate, sizeof(int), 1, fout);
fwrite(&ByteRate, sizeof(int), 1, fout);
fwrite(&BlockAlign, sizeof(short), 1, fout);
fwrite(&BitsPerSample, sizeof(short), 1, fout);
fwrite("data", sizeof(char), 4, fout);
fwrite(&DataSize, sizeof(int), 1, fout);
fclose(fout);
NSMutableData *pamdata = [NSMutableData dataWithData:data];
NSFileHandle *handle;
handle = [NSFileHandle fileHandleForUpdatingAtPath:wavFilePath];
[handle seekToEndOfFile];
[handle writeData:pamdata];
[handle closeFile];
NSLog(@"Saved wav: %@", wavFilePath);
return [NSURL URLWithString:wavFilePath];
}
struct buffer_data {
uint8_t *ptr;
size_t size; ///< size left in the buffer
};
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
struct buffer_data *bd = (struct buffer_data *)opaque;
buf_size = FFMIN(buf_size, bd->size);
//printf("ptr:%p size:%zu\n", bd->ptr, bd->size);
/* copy internal buffer data to buf */
memcpy(buf, bd->ptr, buf_size);
bd->ptr += buf_size;
bd->size -= buf_size;
return buf_size;
}
@end