项目地址, 求 star
https://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpeg播放音乐(保程序不死)
这个是 FFmpeg 解码出音频, 给 AudioTrack 播放, 这回才算是 java 与 c 语言之间合作
这回我们将会从 c++ 里调用 java 函数, 下面就是关于 c++ 使用 AudioTrack 的代码
- private AudioTrack audioTrack;
- // 这个方法 是 C 进行调用 通道数
- public void createTrack(int sampleRateInHz,int nb_channals) {
- int channaleConfig;// 通道数
- if (nb_channals == 1) {
- channaleConfig = AudioFormat.CHANNEL_OUT_MONO;
- } else if (nb_channals == 2) {
- channaleConfig = AudioFormat.CHANNEL_OUT_STEREO;
- }else {
- channaleConfig = AudioFormat.CHANNEL_OUT_MONO;
- }
- int buffersize=AudioTrack.getMinBufferSize(sampleRateInHz,
- channaleConfig, AudioFormat.ENCODING_PCM_16BIT);
- audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRateInHz,channaleConfig,
- AudioFormat.ENCODING_PCM_16BIT,buffersize,AudioTrack.MODE_STREAM);
- audioTrack.play();
- }
- //C 传入音频数据
- public void playTrack(byte[] buffer, int lenth) {
- if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
- audioTrack.write(buffer, 0, lenth);
- }
- }
我们再来看看 c++ 的代码
首先注册组件, 然后得到音频流
- av_register_all();
- AVFormatContext *pFormatCtx = avformat_alloc_context();
- //open
- if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0) {
- LOGE("%s","打开输入视频文件失败");
- return;
- }
- // 获取视频信息
- if(avformat_find_stream_info(pFormatCtx,NULL) <0){
- LOGE("%s","获取视频信息失败");
- return;
- }
- int audio_stream_idx=-1;
- int i=0;
- for (int i = 0; i < pFormatCtx->nb_streams; ++i) {
- if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
- LOGE("找到音频 id %d", pFormatCtx->streams[i]->codec->codec_type);
- audio_stream_idx=i;
- break;
- }
- }
获取解码器
- // 获取解码器上下文
- AVCodecContext *pCodecCtx=pFormatCtx->streams[audio_stream_idx]->codec;
- // 获取解码器
- AVCodec *pCodex = avcodec_find_decoder(pCodecCtx->codec_id);
- // 打开解码器
- if (avcodec_open2(pCodecCtx, pCodex, NULL)<0) {
- }
设置缓存区, 保存解码前后的数据
- // 申请 avpakcet, 装解码前的数据
- AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
- // 申请 avframe, 装解码后的数据
- AVFrame *frame = av_frame_alloc();
设置解码出的声音一系列的属性, 比如: 单声道双声道采集点大小采集率, 还可以在这里对声音添加特效,
- // 得到 SwrContext , 进行重采样, 具体参考 http://blog.csdn.net/jammg/article/details/52688506
- SwrContext *swrContext = swr_alloc();
- // 缓存区
- uint8_t *out_buffer = (uint8_t *) av_malloc(44100 * 2);
- // 输出的声道布局 (立体声)
- uint64_t out_ch_layout=AV_CH_LAYOUT_STEREO;
- // 输出采样位数 16 位
- enum AVSampleFormat out_formart=AV_SAMPLE_FMT_S16;
- // 输出的采样率必须与输入相同
- int out_sample_rate = pCodecCtx->sample_rate;
- //swr_alloc_set_opts 将 PCM 源文件的采样格式转换为自己希望的采样格式
- swr_alloc_set_opts(swrContext, out_ch_layout, out_formart, out_sample_rate,
- pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0,
- NULL);
- swr_init(swrContext);
- // 获取通道数 2
- int out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
通过反射能够运行 java 函数
- // 反射得到 Class 类型
- jclass david_player = env->GetObjectClass(instance);
- // 反射得到 createAudio 方法
- jmethodID createAudio = env->GetMethodID(david_player, "createTrack", "(II)V");
- // 反射调用 createAudio
- env->CallVoidMethod(instance, createAudio, 44100, out_channer_nb);
- jmethodID audio_write = env->GetMethodID(david_player, "playTrack", "([BI)V");
在一边解码的时候一边给数据给 AudioTrack 播放
- while (av_read_frame(pFormatCtx, packet)>= 0) {
- if (packet->stream_index == audio_stream_idx) {
- // 解码 mp3 编码格式 frame----pcm frame
- avcodec_decode_audio4(pCodecCtx, frame, &got_frame, packet);
- if (got_frame) {
- LOGE("解码");
- swr_convert(swrContext, &out_buffer, 44100 * 2, (const uint8_t **) frame->data, frame->nb_samples);
- // 缓冲区的大小
- int size = av_samples_get_buffer_size(NULL, out_channer_nb, frame->nb_samples,
- AV_SAMPLE_FMT_S16, 1);
- jbyteArray audio_sample_array = env->NewByteArray(size);
- env->SetByteArrayRegion(audio_sample_array, 0, size, (const jbyte *) out_buffer);
- env->CallVoidMethod(instance, audio_write, audio_sample_array, size);
- env->DeleteLocalRef(audio_sample_array);
- }
- }
- }
释放资源
- av_frame_free(&frame);
- swr_free(&swrContext);
- avcodec_close(pCodecCtx);
- avformat_close_input(&pFormatCtx);
- env->ReleaseStringUTFChars(input_, input);
FFmpeg 只是音视频处理的工具, 他没有播放视频和音频的能力, 所以我们需要 SurfaceView 显示视频, AudioTrack 播放声音, 而且 OpenGLES 也能播放声音, 这个后面说
来源: http://blog.csdn.net/z979451341/article/details/79281397