opencore amr在iOS上decode

来源:互联网 发布:电子物料编码软件 编辑:程序博客网 时间:2024/05/18 00:29

http://blog.csdn.net/favormm/article/details/6804553


两周前空闲的时候编译了opencore for iOS, 如何编译的请参看这一篇文章。今天又有空,所以就试着去用了一下这个库,我想把.amr的文件decode为.wav格式的。在test目录下有简单的例子,教大家如何用这个库,于是我就照着里面的代码,写了一个for iOS在xcode里跑,结果大失所望, 转化出来的文件只有4K大小。

首先我说说我的方法。

新建了一个iOS的工程,然后把编译好的lib与include文件拖到工程里,然后修改wav.cpp后缀为wav.mm,并修改它的内容如下:

  1. #import <UIKit/UIkit.h>  
  2. #include "wav.h"  
  3.   
  4. void WavWriter::writeString(const char *str) {  
  5.     fputc(str[0], wav);  
  6.     fputc(str[1], wav);  
  7.     fputc(str[2], wav);  
  8.     fputc(str[3], wav);  
  9. }  
  10.   
  11. void WavWriter::writeInt32(int value) {  
  12.     fputc((value >>  0) & 0xff, wav);  
  13.     fputc((value >>  8) & 0xff, wav);  
  14.     fputc((value >> 16) & 0xff, wav);  
  15.     fputc((value >> 24) & 0xff, wav);  
  16. }  
  17.   
  18. void WavWriter::writeInt16(int value) {  
  19.     fputc((value >> 0) & 0xff, wav);  
  20.     fputc((value >> 8) & 0xff, wav);  
  21. }  
  22.   
  23. void WavWriter::writeHeader(int length) {  
  24.     writeString("RIFF");  
  25.     writeInt32(4 + 8 + 16 + 8 + length);  
  26.     writeString("WAVE");  
  27.   
  28.     writeString("fmt ");  
  29.     writeInt32(16);  
  30.   
  31.     int bytesPerFrame = bitsPerSample/8*channels;  
  32.     int bytesPerSec = bytesPerFrame*sampleRate;  
  33.     writeInt16(1);             // Format  
  34.     writeInt16(channels);      // Channels  
  35.     writeInt32(sampleRate);    // Samplerate  
  36.     writeInt32(bytesPerSec);   // Bytes per sec  
  37.     writeInt16(bytesPerFrame); // Bytes per frame  
  38.     writeInt16(bitsPerSample); // Bits per sample  
  39.   
  40.     writeString("data");  
  41.     writeInt32(length);  
  42. }  
  43.   
  44. WavWriter::WavWriter(const char *filename, int sampleRate, int bitsPerSample, int channels)   
  45. {  
  46.       
  47.     NSArray *paths               = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
  48.     NSString *documentPath       = [paths objectAtIndex:0];  
  49.     NSString *docFilePath        = [documentPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%s", filename]];  
  50.     NSLog(@"documentPath=%@", documentPath);  
  51.       
  52.     wav = fopen([docFilePath cStringUsingEncoding:NSASCIIStringEncoding], "wb");  
  53.     if (wav == NULL)  
  54.         return;  
  55.     dataLength = 0;  
  56.     this->sampleRate = sampleRate;  
  57.     this->bitsPerSample = bitsPerSample;  
  58.     this->channels = channels;  
  59.   
  60.     writeHeader(dataLength);  
  61. }  
  62.   
  63. WavWriter::~WavWriter() {  
  64.     if (wav == NULL)  
  65.         return;  
  66.     fseek(wav, 0, SEEK_SET);  
  67.     writeHeader(dataLength);  
  68.     fclose(wav);  
  69. }  
  70.   
  71. void WavWriter::writeData(const unsigned char* data, int length) {  
  72.     if (wav == NULL)  
  73.         return;  
  74.     fwrite(data, length, 1, wav);  
  75.     dataLength += length;  
  76. }  

其实只修改了一处代码,就是fopen时的第一个参数。


准备工程都做好了,现在开始decode了,于是我写了一个方法:

  1. const int sizes[] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, 6, 5, 5, 0, 0, 0, 0 };  
  2. - (IBAction)amrnbToWav:(id)sender  
  3. {  
  4.       
  5.     NSString * path = [[NSBundle mainBundle] pathForResource:  @"test" ofType: @"amr"];   
  6.       
  7.     FILE* in = fopen([path cStringUsingEncoding:NSASCIIStringEncoding], "rb");  
  8.     if (!in)   
  9.     {  
  10.         NSLog(@"open file error");  
  11.     }  
  12.       
  13.     char header[6];  
  14.     int n = fread(header, 1, 6, in);  
  15.     if (n != 6 || memcmp(header, "#!AMR\n", 6))   
  16.     {  
  17.         NSLog(@"Bad header");  
  18.     }  
  19.       
  20.     WavWriter wav("out.wav", 8000, 16, 1);  
  21.     void* amr = Decoder_Interface_init();  
  22.     while (true) {  
  23.         uint8_t buffer[500];  
  24.         /* Read the mode byte */  
  25.         n = fread(buffer, 1, 1, in);  
  26.         if (n <= 0)  
  27.             break;  
  28.         /* Find the packet size */  
  29.         int size = sizes[(buffer[0] >> 3) & 0x0f];  
  30.         if (size <= 0)  
  31.             break;  
  32.         n = fread(buffer + 1, 1, size, in);  
  33.         if (n != size)  
  34.             break;  
  35.           
  36.         /* Decode the packet */  
  37.         int16_t outbuffer[160];  
  38.         Decoder_Interface_Decode(amr, buffer, outbuffer, 0);  
  39.           
  40.         /* Convert to little endian and write to wav */  
  41.         uint8_t littleendian[320];  
  42.         uint8_t* ptr = littleendian;  
  43.         for (int i = 0; i < 160; i++) {  
  44.             *ptr++ = (outbuffer[i] >> 0) & 0xff;  
  45.             *ptr++ = (outbuffer[i] >> 8) & 0xff;  
  46.         }  
  47.         wav.writeData(littleendian, 320);  
  48.     }  
  49.     fclose(in);  
  50.     Decoder_Interface_exit(amr);  
  51. }  

注意要引入相应的头文件

  1. #import "wav.h"  
  2. #import "interf_dec.h"  
  3. #import "dec_if.h"  
  4. #import "interf_enc.h"  

哎,以为会成功,结果转化不成功啊。有没有大侠知道原因???

最开始我以为是wav头的问题,于是去研究了一下wav头,参看这张图,结果发现WavWriter这个类并没有错。后来,我以为是for iOS编译的问题,于是我直接在mac上编译了一份for mac的版本,然后建了一个mac工程测试,得到与ios上一样的结果。 最后得出结论,可能是用法错了。如何用呢,网上的资料太少了,有没有大侠用过的呀?

我把WAV头格式的图拿了过来,好东西要听鲁讯的,拿来主义,呵呵。



欢迎大家一起讨论。

我已把工程上传网盘,有空的人帮着研究一下。

http://115.com/file/bhiqw3xd#
amrDemoForiOS.zip

在cocoachina上问了高人,叫我参看:http://www.cublog.cn/u3/112227/showart_2233739.html

照着http://www.cublog.cn/u3/112227/showart_2233739.html的代码用了一下,这回正确了, 两个不同之处就是计算frame大小的时候那个数组不同,官方的sample却不能用,真是郁闷呀,官方的那个数组成员都小于20,有人知道为何官方那么写吗?想不通,不过这回成功转化为wav文件了。呵呵,吃了午饭,回来比较了一下代码,发现是因为我的amr文件有错误帧造成的,所以在读取的时候遇到错误帧的时候要丢弃错误帧,但不能结束处理,因为后面还有正确帧。所以我修改了一下转化函数如下:

  1. - (IBAction)amrnbToWav:(id)sender  
  2. {  
  3.       
  4.     NSString * path = [[NSBundle mainBundle] pathForResource:  @"test" ofType: @"amr"];   
  5.       
  6.     FILE* in = fopen([path cStringUsingEncoding:NSASCIIStringEncoding], "rb");  
  7.     if (!in)   
  8.     {  
  9.         NSLog(@"open file error");  
  10.     }  
  11.       
  12.     char header[6];  
  13.     int n = fread(header, 1, 6, in);  
  14.     if (n != 6 || memcmp(header, "#!AMR\n", 6))   
  15.     {  
  16.         NSLog(@"Bad header");  
  17.     }  
  18.       
  19.     WavWriter wav("out.wav", 8000, 16, 1);  
  20.     void* amr = Decoder_Interface_init();  
  21.     int frame = 0;  
  22.       
  23.       
  24.     unsigned char stdFrameHeader;  
  25.       
  26.     while (true) {  
  27.         uint8_t buffer[500];  
  28.         /* Read the mode byte */  
  29.           
  30.         unsigned char frameHeader;  
  31.         int size;  
  32.         int index;  
  33.         if (frame == 0)   
  34.         {  
  35.             n = fread(&stdFrameHeader, 1, sizeof(unsigned char), in);  
  36.             index = (stdFrameHeader >> 3) &0x0f;  
  37.         }  
  38.         else  
  39.         {  
  40.             while(1)   //丢弃错误帧,处理正确帧  
  41.             {  
  42.                 n = fread(&frameHeader, 1, sizeof(unsigned char), in);  
  43.                 if (feof(in)) return;  
  44.                 if (frameHeader == stdFrameHeader) break;  
  45.             }  
  46.             index = (frameHeader >> 3) & 0x0f;  
  47.         }  
  48.   
  49.         if (n <= 0)  
  50.             break;  
  51.         /* Find the packet size */  
  52.         size = sizes[index];  
  53.         if (size <= 0)  
  54.             break;  
  55.         n = fread(buffer + 1, 1, size, in);  
  56.         if (n != size)  
  57.             break;  
  58.           
  59.         frame++;  
  60.         /* Decode the packet */  
  61.         int16_t outbuffer[160];  
  62.         Decoder_Interface_Decode(amr, buffer, outbuffer, 0);  
  63.           
  64.         /* Convert to little endian and write to wav */  
  65.         uint8_t littleendian[320];  
  66.         uint8_t* ptr = littleendian;  
  67.         for (int i = 0; i < 160; i++) {  
  68.             *ptr++ = (outbuffer[i] >> 0) & 0xff;  
  69.             *ptr++ = (outbuffer[i] >> 8) & 0xff;  
  70.         }  
  71.         wav.writeData(littleendian, 320);  
  72.     }  
  73.     NSLog(@"frame=%d", frame);  
  74.     fclose(in);  
  75.     Decoder_Interface_exit(amr);  
  76. }  


这下就可以正常转amr为wav文件了。官方的那个数组是优化过的!!

请下载Demo Project 。有网友向我反映该Demo第一个button的转化不能成功,第二个button的转化能成功。我运行了一下,的确有这个问题。第一个button的转化我是参考opencore amr的sample写的,当时没有太在意,因为我测试第二button的转化成功了,于是就没有接着测第一个。今天研究了一下,发现主要原因是由对wav头没处理。标准的wav头会有一些定节对齐的问题,只需要将wav.mm里writeHeader(int length)函数修改如下就行了。

  1. void WavWriter::writeHeader(int length) {  
  2.     writeString("RIFF");  
  3.     writeInt32(4 + 8 + 20 + 8 + length);  //将16改为20  
  4.     writeString("WAVE");  
  5.   
  6.     writeString("fmt ");  
  7.     writeInt32(20);  
  8.   
  9.     int bytesPerFrame = bitsPerSample/8*channels;  
  10.     int bytesPerSec = bytesPerFrame*sampleRate;  
  11.     writeInt16(1);             // Format  
  12.     writeInt16(channels);      // Channels  
  13.     writeInt32(sampleRate);    // Samplerate  
  14.     writeInt32(bytesPerSec);   // Bytes per sec  
  15.     writeInt16(bytesPerFrame); // Bytes per frame  
  16.     writeInt16(bitsPerSample); // Bits per sample  
  17.       
  18.     writeInt32(0);             //这儿需要字节对齐  nExSize  
  19.   
  20.     writeString("data");  
  21.     writeInt32(length);  
  22. }  

现在就可以成功转化了,大功告成。wav头FMT chunk标准是24字节,这儿却需要28字节,不知道是不是字节对齐的原因造成的。

请下载完整Demo project。


原创粉丝点击