编码成amr格式

来源:互联网 发布:js鼠标悬停图片放大 编辑:程序博客网 时间:2024/06/05 06:23
==============================================================================


解决什么问题
解决语音聊天时,录音文件体积偏大的问题。


怎么解决
使用amr格式来编码,可有效降低体积并保持较好的音质。


==============================================================================
简介
使用ffmpeg跟opencore-amrnb来完成amr格式的编码保存。


==============================================================================
代码演示,在ios上运行的代码


// 打开解码器
- (BOOL)openInCodec
{
    NSString* inpath = _infilepath;
    BOOL openSuccess = NO;
    int audio_stream_index = -1;
    do {
        _inFormatContext = avformat_alloc_context();
        int ret = avformat_open_input(&_inFormatContext, [inpath UTF8String], NULL, NULL);
        if (ret != 0) {
            NSLog(@"open input failed");
            break;
        }
        ret = avformat_find_stream_info(_inFormatContext, NULL);
        if (ret < 0) {
            NSLog(@"find stream info failed");
            break;
        }
        for (int pos = 0; pos < _inFormatContext->nb_streams; pos ++) {
            if (_inFormatContext->streams[pos]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
                audio_stream_index = pos;
                break;
            }
        }
        if (audio_stream_index == -1) {
            NSLog(@"no audio stream");
            break;
        }
        
        _inCodecContext = _inFormatContext->streams[audio_stream_index]->codec;
        _inCodec = avcodec_find_decoder(_inCodecContext->codec_id);
        if (_inCodec == NULL) {
            NSLog(@"no avcodec found");
            _inCodecContext = NULL;
            break;
        }
        ret = avcodec_open2(_inCodecContext, _inCodec, NULL);
        if (ret != 0) {
            NSLog(@"open codec failed");
            _inCodecContext = NULL;
            break;
        }
        _audiostream_index = audio_stream_index;
        
        openSuccess = YES;
    }while (0);
    
    if (!openSuccess) {
        [self destroyffmpeg];
    }
    
    return openSuccess;
}


// 打开amr编码器
- (BOOL)openOutCodec
{
    NSString* outpath = [NSHomeDirectory()stringByAppendingString:@"/Documents/out.amr"];
    NSString* outfilepath = outpath;
    BOOL openSuccess = NO;
    do {
        int ret = avformat_alloc_output_context2(&_outFormatContext, NULL, "amr", [outfilepath UTF8String]);
        if (ret < 0) {
            NSLog(@"alloc output format failed");
            break;
        }
        AVOutputFormat* outFormat = _outFormatContext->oformat;
        outFormat->audio_codec = AV_CODEC_ID_AMR_NB;
        outFormat->video_codec = AV_CODEC_ID_NONE;
        
        _outCodec = avcodec_find_encoder(outFormat->audio_codec);
        if (_inCodec == NULL) {
            NSLog(@"no encoder found");
            break;
        }
        AVStream* audioStream = avformat_new_stream(_outFormatContext, _outCodec);
        if (audioStream == NULL) {
            NSLog(@"new stream failed");
            break;
        }
        audioStream->id = 0;
        audioStream->index = 0;
        _outCodecContext = audioStream->codec;
        if (_outCodecContext == NULL) {
            NSLog(@"no codecContext in stream");
            break;
        }
        _outCodecContext->sample_fmt = AV_SAMPLE_FMT_S16;
        _outCodecContext->channels = 1;
        _outCodecContext->channel_layout = av_get_default_channel_layout(1);
        _outCodecContext->sample_rate = 8000;
        _outCodecContext->bit_rate = 12200;
        if (outFormat->flags & AVFMT_GLOBALHEADER) {
            _outCodecContext->flags |= CODEC_FLAG_GLOBAL_HEADER;
        }
        
        ret = avcodec_open2(_outCodecContext, _outCodec, NULL);
        if (ret < 0) {
            NSLog(@"open encoder failed");
            break;
        }
        
        av_dump_format(_outFormatContext, 0, [outfilepath UTF8String], 1);
        
        if (!(_outFormatContext->flags & AVFMT_NOFILE)) {
            ret = avio_open2(&_outFormatContext->pb, [outfilepath UTF8String], AVIO_FLAG_WRITE, &_outFormatContext->interrupt_callback, NULL);
            if (ret < 0) {
                NSLog(@"avio_open2 failed: %d(%s)", ret, av_err2str(ret));
                break;
            }
        }
        
        AVDictionary* dict = NULL;
        ret = avformat_write_header(_outFormatContext, &dict);
        if (ret < 0) {
            NSLog(@"write header failed: %d(%s)", ret, av_err2str(ret));
            break;
        }
        
        openSuccess = YES;
    }while (0);
    
    if (!openSuccess) {
        [self destroyffmpeg];
    }
    
    return openSuccess;
}


// 解码的过程,编码成amr(有些细节还需要考虑)
- (void)convert
{
    BOOL eof = NO;
    while (1) {
        AVPacket packet;
        int ret = av_read_frame(_inFormatContext, &packet);
        if (ret < 0) {
            if (ret == AVERROR_EOF || url_feof(_inFormatContext->pb)) {
                NSLog(@"read_frame eof");
                eof = YES;
                break;
            }
            else {
                NSLog(@"av_read_frame failed, %d(%s)", ret, av_err2str(ret));
                break;
            }
        }
        
        if (packet.stream_index == _audiostream_index) {
            int size = packet.size;
            AVFrame frame;
            AVPacket encodePacket;
            void* out_buffer = NULL;
            while (size > 0) {
//                av_frame_unref(&frame);
                int gotframe = 0;
                int len = avcodec_decode_audio4(_inCodecContext, &frame, &gotframe, &packet);
                if (len < 0) {
                    NSLog(@"audio decode failed");
                    break;
                }
                if (gotframe) {
                    bool needConvert = true;
                    int targetSampelrate = 8000;
                    int targetChannels = 1;
                    if (needConvert)  {
                        int out_size = av_samples_get_buffer_size(NULL, targetChannels, frame.nb_samples, AV_SAMPLE_FMT_S16, 0);
                        if (out_size >= 0) {
//                            if (_swr) {  // more think here for if
//                                [self deletetSwr];
//                            }
                            bool initswr = true;
                            if (_swr == NULL) {
                                uint64_t in_channel_layout = av_get_default_channel_layout(av_frame_get_channels(&frame));
                                uint64_t out_channel_layout = av_get_default_channel_layout(targetChannels);
                                int inSamplerate = frame.sample_rate;
                                _swr = swr_alloc_set_opts(NULL,
                                                          out_channel_layout, (enum AVSampleFormat )AV_SAMPLE_FMT_S16, targetSampelrate,
                                                          in_channel_layout, (enum AVSampleFormat)frame.format, inSamplerate, 0, NULL);
                                int ret = swr_init(_swr);
                                if (ret != 0) {
                                    initswr = false;
                                }
                            }
                            if (_swr && initswr) {
                                if (frame.extended_data && frame.data[0] && frame.linesize[0] > 0) {
                                    out_buffer = av_malloc(out_size);
                                    if (out_buffer) {
                                        int convertSamples = swr_convert(_swr, (uint8_t**)(&out_buffer), frame.nb_samples, (const uint8_t**)frame.extended_data, frame.nb_samples);
                                        int len = convertSamples * targetChannels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
                                        
                                        int nb_samples = _outCodecContext->frame_size / 1;
                                        int buffer_size = av_samples_get_buffer_size( NULL, _outCodecContext->channels, nb_samples, AV_SAMPLE_FMT_S16, 0 );
                                        
                                        AVFrame* decodeframe = avcodec_alloc_frame();
                                        decodeframe->nb_samples = nb_samples;
                                        ret = avcodec_fill_audio_frame( decodeframe, _outCodecContext->sample_fmt, AV_SAMPLE_FMT_S16, (const uint8_t *)out_buffer, buffer_size, 0 );
                                        
                                        
                                        if( ret < 0 )
                                        {
                                            NSLog( @"avcodec_fill_audio_frame error!");
                                            break;
                                        }
                                        
                                        encodePacket.data = NULL;
                                        encodePacket.size = 0;
                                        av_init_packet(&encodePacket);
                                        ret = avcodec_encode_audio2(_outCodecContext, &encodePacket, decodeframe, &gotframe);
                                        if (ret < 0) {
                                            NSLog(@"encode audio fail: %d(%s)", ret, av_err2str(ret));
                                            break;
                                        }
                                        if (gotframe == 0) {
                                            NSLog(@"did not got frame");
                                            break;
                                        }
                                        encodePacket.stream_index = 0;
                                        ret = av_write_frame(_outFormatContext, &encodePacket);
                                        if (ret < 0) {
                                            NSLog(@"av_write_frame failed: %d(%s)", ret, av_err2str(ret));
                                            break;
                                        }
                                        
                                        size -= len;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            av_free_packet(&encodePacket);
            if (out_buffer) {
                av_free(out_buffer);
            }
        }
    }
    if (eof) {
        av_write_trailer(_outFormatContext);
    }
}


==============================================================================
后续
* ffmpeg在android与ios上的编译与使用
* opencore-amr的编译与使用


==============================================================================
0 0
原创粉丝点击