ffmpeg. decode AMR to MP3
От: VictorKozachek  
Дата: 09.06.11 18:38
Оценка:
Приветствую,

Я на этом ресурсе новый человек, посему если что не так, поправьте пожалуйста.

Проблема в том, что папу Джобса как всегда сутра клюнул жаренный петух и теперь по какойто причине Apple убрали поддержку AudioQueue output поддержку для AMR кодека в iOS 4.3. В старых системах мое приложение замечательно так читало пакеты из RTSP и играло видео и аудио, теперь играет только видео. Я решил сделать методы перекодирования из AMR в MP3 используя ffmpeg библиотеку, которую я кокраз и использую для работы с RTSP. Т.е. идея такова: получаем AVPacket для аудио, декодируем его в raw buffer, а потом энкодируем в MP3, запихиваем в новый AVPacket и гадим его в AudioQueue. Я сделал два метода (для decode и encode), но, толи у меня не хватает понимания как это работает, толи Джобс гдета капнул, но звук полон всяких артифактов и звучит аки поезд что по рельсам едет. Два дня читал всякие доки и форумы, пока не нашел ничего. Может тут мне подскажут что не так. Вот собственно методы (смесь Objective C и С. код переделовался много раз, потому не слишком чист):

- (void) decodeAudioPacketToRaw:(AVPacket) inPacket
{
    AVCodec *codec;
    AVCodecContext *c= NULL;
    
    int out_size, size, len, total;
    uint8_t *outbuf;    
    uint8_t *readybuf;        
    uint8_t *buf;            
    
    total = 0;
    
    codec = avcodec_find_decoder(CODEC_ID_AMR_NB);
    if (!codec) {
        fprintf(stderr, "input codec not found\n");
        return;
    }
    
    c= avcodec_alloc_context();
    
    if (avcodec_open(c, codec) < 0) {
        fprintf(stderr, "could not open input codec\n");
        return;
    }
    
    readybuf = (uint8_t*) malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
    memset(readybuf, 0, AVCODEC_MAX_AUDIO_FRAME_SIZE);
    size = inPacket.size;
    
    NSLog(@"Input packet size is %i", size);
    
    while (size > 0) {
        outbuf = (uint8_t*) malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);   
        out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
        len = avcodec_decode_audio3(c, (short *)outbuf, &out_size, &inPacket);
        if (len < 0) {
            fprintf(stderr, "Error while decoding\n");
            return;
        }
        NSLog(@"Decoded len is %i", len);
        NSLog(@"Decoded out size is %i", out_size);        
        if (out_size > 0) {
            size -= len;
            inPacket.size -= len;
            inPacket.data += len;
            memcpy(readybuf + total, outbuf, out_size);
            total += out_size;
            //[self encodeAudioBuffer:(const short *)outbuf withSize:out_size andSourcePacket:inPacket];    Пробовал даже так, еще хуже                
        }
        free(outbuf);
    }    
    if(total > 0)
    {
        buf = (uint8_t*) malloc(total);        
        memcpy(buf, readybuf, total);
        free(readybuf);        
        [self encodeAudioBuffer:(const short *)buf withSize:total andSourcePacket:inPacket];        
        free(buf);
    }
}



- (void) encodeAudioBuffer:(const short *) in_buffer withSize:(unsigned int) in_buf_byte_size andSourcePacket:(AVPacket)sourcePacket
{
    AVCodec * codec;
    AVCodecContext * c= NULL;
    int count, out_size, outbuf_size, frame_byte_size;
    uint8_t * outbuf;    
    uint8_t * totaloutbuf;        

    printf("Audio encoding\n");
    
    codec = avcodec_find_encoder(CODEC_ID_MP3);
    if (!codec) {
        fprintf(stderr, "output codec not found\n");
        return;
    }
    
    c= avcodec_alloc_context();
    
    c->bit_rate = 16000;
    c->sample_rate = 8000;
    c->channels = 1;
    
    if (avcodec_open(c, codec) < 0) {
        fprintf(stderr, "could not open output codec\n");
        return;
    }
            
    NSLog(@"START");
    
    int total = 0;
    int test = in_buf_byte_size;
    out_size = 0;
    while(out_size <= 0)
    {
        outbuf_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
        outbuf = (uint8_t*) malloc(outbuf_size);
        memset(outbuf, 0, AVCODEC_MAX_AUDIO_FRAME_SIZE);
        
        out_size = avcodec_encode_audio(c, outbuf, outbuf_size, (short*)in_buffer);
        if(out_size > 0)
        {
            AVPacket resPacket;
            av_init_packet(&resPacket);
            resPacket.data = malloc(out_size);
            resPacket.size = 0;
            memset(resPacket.data, 0, out_size);
            memcpy(resPacket.data, outbuf, out_size);
            resPacket.size = out_size;
            
            // тут гадим пакет в очередь.

            test -= out_size;
            total += out_size;
        }
        
        free(outbuf);        
    }
        
    avcodec_close(c);
    av_free(c);
    NSLog(@"FINISH");    

}


На самом деле не совсем ясно, что возвращает avcodec_encode_audio. В одной инструкции говорится, что возвращает скока использовалось байт из входного буфера, в другой, скока получилось байт в output буфере. В общем загадка. Странно, но первый раз оно возвращает 0, потому я там цикл сделал. Забавно то, что MP2 при первой попытке возвращает значчение более 0, но AudioQueue на iPhone не поддержывает и его.

Помогите кто может, а то уже моск кипит!!!

Спасибо!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.