Android上jni c++使用MediaCodec的方法



在Android上做媒体开发,避免不了对视频数据进行编解码。

软件编解码有FFmpeg。虽然FFmpeg软件编解码兼容性和适应性很好。但是在大部分主流的机器上,解码720p视频已经很勉强了,更不用提更高的分辨率。编码的话要求更高。

所以在移动平台上做视频编解码应当优先使用机器自带的硬件编解码来实现。
Android系统底层的硬件编解码框架是OpenMaxIL。使用这个可以跨的版本最多。从Android2.3以后支持。不过使用难度大。这里不讨论了。

Android从4.1以后提供了MediaCodec这个框架做视频编解码,但是5.0系统以前只提供了java版,5.0以后才能在jni native code使用。

很多情况下视频处理的数据都是放在native code那边处理的,编解码部分也希望在native code里面一起实现。

虽然可以用java 的MediaCodec稍微封装下,然后在jni 里面用c/c++代码里面用jni的交互接口(FindClass、Call...Method 等配合调用)。但是这种方法不仅代码封装性不好,还得频繁的进程native code与jvm的交互,效率不高。而且破坏代码可读和封装。


所以这里提供个4.1以后的系统都能在native code像5.0那样使用MediaCodec的方法,方便c++开发者使用MediaCodec:

这是编译好的so库以及使用代码和说明:MediaCodec c++

这是从Android5.0的源码里面抽取了MediaCodec so实现的代码,分别在Android4.1 ~ 4.4 源码上编译的so库。所以接口使用和5.0的MediaCodec其实是一样的。

ps:关于那些库的编译步骤,编译时要跟随不同系统版本的源码分别编译一个,因为编译时依赖了系统里面的东西,为了不重新编译整个系统源码,可以从Android模拟器里面把依赖到的so库pull出来,源码只提供头文件,编译时链接pull出来的so库即可。

编译好的库我已经提供出来了,直接下载使用即可。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
以下是使用C++ MediaCodec 进行视频解码的示例代码: ```c++ #include <jni.h> #include <android/log.h> #include <android/native_window_jni.h> #include <media/NdkMediaCodec.h> #include <media/NdkMediaExtractor.h> #define LOG_TAG "MediaCodec" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) extern "C" JNIEXPORT void JNICALL Java_com_example_mediadecoder_MainActivity_decode(JNIEnv *env, jobject thiz, jstring filePath, jobject surface) { const char *path = env->GetStringUTFChars(filePath, nullptr); AMediaExtractor *extractor = AMediaExtractor_new(); AMediaCodec *codec = nullptr; int32_t trackIndex = -1; bool sawInputEOS = false; bool sawOutputEOS = false; // 设置文件路径 media_status_t result = AMediaExtractor_setDataSource(extractor, path); if (result != AMEDIA_OK) { LOGE("AMediaExtractor_setDataSource error: %d", result); goto end; } // 查找视频轨道 int32_t numTracks = AMediaExtractor_getTrackCount(extractor); for (int32_t i = 0; i < numTracks; ++i) { AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, i); const char *mime = nullptr; AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime); if (mime && !strncmp(mime, "video/", 6)) { trackIndex = i; break; } AMediaFormat_delete(format); } if (trackIndex == -1) { LOGE("No video track found in %s", path); goto end; } // 获取视频格式 AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, trackIndex); int32_t width = 0, height = 0; AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width); AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height); LOGI("Video size: %dx%d", width, height); // 创建 MediaCodec 解码器 codec = AMediaCodec_createDecoderByType("video/avc"); result = AMediaCodec_configure(codec, format, ANativeWindow_fromSurface(env, surface), nullptr, 0); if (result != AMEDIA_OK) { LOGE("AMediaCodec_configure error: %d", result); goto end; } // 启动解码器 result = AMediaCodec_start(codec); if (result != AMEDIA_OK) { LOGE("AMediaCodec_start error: %d", result); goto end; } // 解码视频帧 AMediaCodecBufferInfo bufferInfo; int64_t presentationTimeUs; size_t bufIdx; while (!sawOutputEOS) { if (!sawInputEOS) { bufIdx = AMediaExtractor_getSampleTrackIndex(extractor); if (bufIdx == trackIndex) { result = AMediaExtractor_readSampleData(extractor, AMediaCodec_getInputBuffer(codec, bufIdx), 0); if (result == AMEDIA_OK) { presentationTimeUs = AMediaExtractor_getSampleTime(extractor); AMediaCodec_queueInputBuffer(codec, bufIdx, 0, AMediaExtractor_getSampleSize(extractor), presentationTimeUs, AMediaExtractor_getSampleFlags(extractor)); AMediaExtractor_advance(extractor); } else if (result == AMEDIA_ERROR_END_OF_STREAM) { sawInputEOS = true; AMediaCodec_queueInputBuffer(codec, bufIdx, 0, 0, 0, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); } else { LOGE("AMediaExtractor_readSampleData error: %d", result); goto end; } } } result = AMediaCodec_dequeueOutputBuffer(codec, &bufferInfo, 5000); if (result == AMEDIA_OK) { if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) { sawOutputEOS = true; } AMediaCodec_releaseOutputBuffer(codec, result, true); } else if (result == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { AMediaFormat *newFormat = nullptr; newFormat = AMediaCodec_getOutputFormat(codec); AMediaFormat_getInt32(newFormat, AMEDIAFORMAT_KEY_WIDTH, &width); AMediaFormat_getInt32(newFormat, AMEDIAFORMAT_KEY_HEIGHT, &height); LOGI("Video output format changed to %dx%d", width, height); AMediaFormat_delete(newFormat); } else if (result == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) { LOGI("AMediaCodec_dequeueOutputBuffer returned AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED"); } else { LOGE("AMediaCodec_dequeueOutputBuffer error: %d", result); goto end; } } end: if (codec) { AMediaCodec_stop(codec); AMediaCodec_delete(codec); } if (extractor) { AMediaExtractor_delete(extractor); } env->ReleaseStringUTFChars(filePath, path); } ``` 在此示例代码中,我们使用 `AMediaExtractor` 获取视频文件中的视频轨道,并使用 `AMediaCodec` 进行解码。注意,此代码仅支持解码 H.264 编码视频。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值