EasyDarwin开源音频解码项目EasyAudioDecoder:EasyPlayer Android音频解码库(第二部分,封装解码器接口)
上一节我们讲了如何基于ffmpeg-Android工程编译安卓上的支持音频的ffmpeg静态库:http://blog.csdn.net/xiejiashu/article/details/52524099,这篇文章我们将介绍如何封装安卓的解码器。
首先,为了能让我们的app调用调用,我们需要定义一套Java的native接口,解码器主要有三个接口:
- create 创建解码器,参数分别为:
- codec:音频编码格式,参考EasyTypes.h里面的EASY_SDK_AUDIO_CODEC_*宏定义;
- sample_rate:采样率,通常为8000、44000等等;
- channels:通道数,1、2分别表示单通道、双通道;
- sample_bit:采样精度,通常为16bit;
返回解码器句柄,即后续接口里面用到的handle
- decode 解码,参数为:
- handle :解码器句柄
- buffer:要解码的buffer(编码后的音频数据)
- offset:编码数据在buffer里的起始位置
- length:编码数据的长度
- pcm:解码后的pcm数据
- outLen 长度至少为1的int数组,如果解码成功,那outLen[0]被置为pcm的数据长度
- close 关闭解码器,参数为解码器的句柄。关闭后句柄无效,应该置为0.
package org.easydarwin.audio;
/**
* Created by John on 2016/3/18.
*/
public class AudioCodec {
static {
System.loadLibrary("AudioCodecer");
}
public static native int create(int codec, int sample_rate, int channels, int sample_bit);
public static native int decode(int handle, byte[] buffer, int offset, int length, byte[] pcm, int[] outLen);
public static native void close(int handle);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
接下来我们要实现相应的native接口,头文件可通过调用javah命令(这里不再详述)来生成。以AAC解码来说明,create代码片段如下:
// 创建获取解码器
AVCodec *pCodec = avcodec_find_decoder(AV_CODEC_ID_AAC);
if (pCodec == NULL)
{
LOGI("find aac decoder error");
printf("find aac decoder error\r\n");
return 0;
}
// 创建解码Context并open解码器
pCodecCtx = avcodec_alloc_context3(pCodec);
pCodecCtx->channels = channels;
pCodecCtx->sample_rate = sample_rate;
pCodecCtx->bit_rate = bit_rate;
if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
printf("open codec error\r\n");
return 0;
}
// 分配内存,存放解码后的数据
pFrame = av_frame_alloc();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
decode:
// 源码流赋值
packet.size = length;
packet.data = buffer;
int got_frame = 0;
// 解码、数据拷贝
while (pkt_pos < nLen)
{
// pkt_pos = 0;
int got_frame = 0;
src_len = avcodec_decode_audio4(pAACD->pCodecCtx, pAACD->pFrame, &got_frame, &packet);
if (src_len < 0)
{
return -3;
}
data_len += src_len
if (got_frame)
{
memcpy(pAACD->pFrame, pAACD->audio_buf, len);
dst_len += len;
}
pkt_pos += src_len;
packet.data = pData + pkt_pos;
packet.size = nLen - pkt_pos;
}
if (NULL != outLen)
*outLen = dst_len;
// 释放
av_free_packet(&packet);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
close:
av_frame_free(&pFrame);
avcodec_close(pComponent->pCodecCtx);
avcodec_free_context(&pCodecCtx);
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
对于其它格式的音频算法,只要在avcodec_find_decoder时传入不同的算法ID即可。
接下来,我们需要使用NDK编译出ANDROID上可以使用的动态库,为此我们需要编辑Android.mk文件,其内容如下:
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH:= $(call my-dir)
SRC_ROOT_PATH := $(call my-dir)
LOCAL_INCLUDE := $(LOCAL_PATH)/aacdec/include
# 加载预编译的静态库
include $(CLEAR_VARS)
LOCAL_MODULE := libavcodec
LOCAL_SRC_FILES := $(LOCAL_PATH)/aacdec/lib/libavcodec.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libavdevice
LOCAL_SRC_FILES := $(LOCAL_PATH)/aacdec/lib/libavdevice.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libavfilter
LOCAL_SRC_FILES := $(LOCAL_PATH)/aacdec/lib/libavfilter.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libavformat
LOCAL_SRC_FILES := $(LOCAL_PATH)/aacdec/lib/libavformat.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libavutil
LOCAL_SRC_FILES := $(LOCAL_PATH)/aacdec/lib/libavutil.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libswresample
LOCAL_SRC_FILES := $(LOCAL_PATH)/aacdec/lib/libswresample.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libswscale
LOCAL_SRC_FILES := $(LOCAL_PATH)/aacdec/lib/libswscale.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_C_INCLUDES += $(LOCAL_INCLUDE)
PROJECT_FILES := $(wildcard $(SRC_ROOT_PATH)/*.cpp)
PROJECT_FILES += $(wildcard $(SRC_ROOT_PATH)/*.c)
PROJECT_FILES += $(wildcard $(SRC_ROOT_PATH)/aacdec/*.cpp)
$(warning $(PROJECT_FILES))
PROJECT_FILES := $(PROJECT_FILES:$(LOCAL_PATH)/%=%)
$(warning $(PROJECT_FILES))
LOCAL_SRC_FILES := $(PROJECT_FILES)
LOCAL_CFLAGS := -D__unix__ -DANDROID_OS -D__arm__ -D__STDC_CONSTANT_MACROS
LOCAL_MODULE := AudioCodecer
LOCAL_LDLIBS += -L$(LOCAL_PATH)/aacdec/lib -lavcodec -lswscale -lswresample -lavutil -lavformat -lavfilter -lavdevice -llog -lz
CFLAGS += -mfpu=neon
LOCAL_STATIC_LIBRARIES := libavcodec libswscale libswresample libavutil libavformat libavfilter libavdevice
include $(BUILD_SHARED_LIBRARY)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
我们这里需要把之前编译出来的静态库链接起来,生成一个动态库。其中,静态库lib文件和头文件分别放在/aacdec/lib/ 和 /aacdec/include/ 下。
接下来,cd到jni目录,执行ndk-build,如果顺利的话,我们要的动态库文件就会生成。
整个项目源码见Github
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术