iOS创建OGG可播放的音频文件

时间:2020-09-13 17:15:17

标签: ios objective-c encode ogg opus

我在iOS上创建ogg opus音频文件时遇到问题,在下面的代码中,我能够将pcm音频编码为opus,然后添加到ogg容器中,最终结果中显示结果,但是当我尝试使用以下命令创建文件时此数据,文件为0秒,无法播放。有人可以帮我吗?

 #define WATSONSDK_AUDIO_FRAME_SIZE 160
   #define WATSONSDK_AUDIO_SAMPLE_RATE 8000.0
             self.opushelper = [[OpusHelper alloc] init];
                            [self.opushelper createEncoder: WATSONSDK_AUDIO_SAMPLE_RATE];
                                opusRef = self->_opushelper;
                            self.ogghelper = [[OggHelper alloc] init];
                                   oggRef = self->_ogghelper;
                           
                            NSMutableData *finaldata= [NSMutableData new];
                               NSData *headerdata =[[self ogghelper] getOggOpusHeader:WATSONSDK_AUDIO_SAMPLE_RATE];
                            [finaldata appendBytes:headerdata.bytes length:headerdata.length];
                            
                     
                            
                            if (pcmdata!=nil && [pcmdata length]!=0) {
                                
                                NSUInteger length = [pcmdata length];
                                NSUInteger chunkSize = WATSONSDK_AUDIO_FRAME_SIZE * 2;
                                NSUInteger offset = 0;
                                
                                do {
                                    NSUInteger thisChunkSize = length - offset > chunkSize ? chunkSize : length - offset;
                                    NSData* chunk = [NSData dataWithBytesNoCopy:(char *)[pcmdata bytes] + offset
                                                                         length:thisChunkSize
                                                                   freeWhenDone:NO];
                                    
                                    // opus encode block
                                    NSData *compressed = [opusRef encode:chunk frameSize:WATSONSDK_AUDIO_FRAME_SIZE];
                                    
                                    if(compressed != nil){
                                        NSMutableData *audioData = [NSMutableData new];
                                      audioData =  [oggRef writePacket:compressed frameSize:WATSONSDK_AUDIO_FRAME_SIZE];
                                        if(audioData != nil)
                                        {
                                            [finaldata appendBytes:audioData.bytes length:audioData.length];
                                        }
                                    }
            
                                    offset += thisChunkSize;
                                } while (offset < length);
                            }

这些是OpusHelper.h中使用的方法:

- (NSData*) encode:(NSData*) pcmData frameSize:(int) frameSize{
    
    opus_int16 *data  = (opus_int16*) [pcmData bytes];
    uint8_t *outBuffer  = malloc(pcmData.length * sizeof(uint8_t));
    
    // The length of the encoded packet
    opus_int32 encodedByteCount = opus_encode(_encoder, data, frameSize, outBuffer, (opus_int32)pcmData.length);
    
    if (encodedByteCount < 0) {
        NSLog(@"encoding error %@",[self opusErrorMessage:encodedByteCount]);
        return nil;
    }

    // Opus data initialized with size in the first byte
    NSMutableData *outputData = [[NSMutableData alloc] initWithCapacity:frameSize*2];
    // Append Opus data
    [outputData appendData:[NSData dataWithBytes:outBuffer length:encodedByteCount]];

    return outputData;
}
   

这些是OGGHelper.h中使用的方法:

- (NSData *) getOggOpusHeader:(int) sampleRate{
        packetCount = 0, granulePos = 0;
        long headerSize = 19;
        unsigned char opusHeader[headerSize];
        int offset = 0;
        // 0 - 7: OpusHead
        writeString(opusHeader, offset + 0, (unsigned char *)"OpusHead", 8);
        // Version, MUST The version number MUST always be '1' for this version of the encapsulation specification.
        opusHeader[offset + 8] = 1;
        // Output Channel Count
        opusHeader[offset + 9] = 1;
        // Pre-skip
        writeShort(opusHeader, offset + 10, 0);
        // Input Sample Rate (Hz)
        writeInt(opusHeader, offset + 12, sampleRate);
        // Output Gain (Q7.8 in dB), +/- 128 dB
        writeShort(opusHeader, offset + 16, 0);
        // Mapping Family (For channel mapping family 0, this value defaults to C-1 (i.e., 0 for mono and 1 for stereo), and is not coded.)
        opusHeader[offset + 18] = 0;
    
        ogg_packet opusHeaderPacket;
        opusHeaderPacket.packet = opusHeader;
        opusHeaderPacket.bytes = headerSize;
        opusHeaderPacket.b_o_s = 1;
        opusHeaderPacket.e_o_s = 0;
        opusHeaderPacket.granulepos = granulePos;
        opusHeaderPacket.packetno = packetCount++;
        ogg_stream_packetin(&streamState, &opusHeaderPacket);
        ogg_stream_flush(&streamState, &oggPage);
        
        NSMutableData *newData = [[NSMutableData alloc] initWithCapacity:0];
        [newData appendBytes:oggPage.header length:oggPage.header_len];
        [newData appendBytes:oggPage.body length:oggPage.body_len];
        
    //    NSLog(@"[Encoder] Ogg header, %ld bytes are written\n", opusHeaderPacket.bytes);
    
        offset = 0;
        NSString *comments = @"libopus";
    
        int commentsLength = (int)[comments lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    //    NSLog(@"Comments length=%d", commentsLength);
        unsigned char opusComments[commentsLength + 28];
        writeString(opusComments, offset, (unsigned char *)"OpusTags", 8);
        
        NSString *vendorString = @"IBM";
        int vendorStringLength = (int) vendorString.length;
        // Vendor String Length
        writeInt(opusComments, offset + 8, (int) vendorStringLength);
        // Vendor String
        writeString(opusComments, offset + 12, (unsigned char*)[comments cStringUsingEncoding:NSUTF8StringEncoding], vendorStringLength);
        // User Comment List Length
        writeInt(opusComments, offset + 20, 1);
        // Vendor comment size
        writeInt(opusComments, offset + 24, commentsLength);
        // Vendor comment
        writeString(opusComments, offset + 28, (unsigned char*)[comments cStringUsingEncoding:NSUTF8StringEncoding], commentsLength);
        
        ogg_packet opusCommentsPacket;
        opusCommentsPacket.packet = opusComments;
        opusCommentsPacket.bytes = commentsLength + 8;
        opusCommentsPacket.b_o_s = 0;
        opusCommentsPacket.e_o_s = 0;
        opusCommentsPacket.granulepos = 0;
        opusCommentsPacket.packetno = packetCount++;
        ogg_stream_packetin(&streamState, &opusCommentsPacket);
        ogg_stream_flush(&streamState, &oggPage);
        
        [newData appendBytes:oggPage.header length:oggPage.header_len];
        [newData appendBytes:oggPage.body length:oggPage.body_len];
    
    //    NSLog(@"[Encoder] Ogg comments, %ld bytes are written\n", opusCommentsPacket.bytes);
        
        return newData;
    }
    
    /**
     *  Write OggOpus packet
     *
     *  @param data      Opus data
     *  @param frameSize Frame size
     *
     *  @return NSMutableData instance or nil
     */
    - (NSMutableData *) writePacket: (NSData*) data frameSize:(int) frameSize{
        ogg_packet packet;
        packet.packet = (unsigned char *)[data bytes];
        packet.bytes = (long)([data length]);
        packet.b_o_s = 0;
        packet.e_o_s = 0;
        granulePos += (frameSize * 2);
        packet.granulepos = granulePos;
        packet.packetno = packetCount++;
        ogg_stream_packetin(&streamState, &packet);
    
        if (ogg_stream_pageout(&streamState, &oggPage)) {
            NSMutableData *newData = [NSMutableData new];
            [newData appendBytes:oggPage.header length:oggPage.header_len];
            [newData appendBytes:oggPage.body length:oggPage.body_len];
            return newData;
        }
        return nil;
    }

0 个答案:

没有答案