Android-NDK:native-codec

surface 解码_NDK中使用 MediaCodec 编解码视频 作者:anddymao背景MediaCodec 作为Android自带的视频编解码工具,可以直接利用底层硬件编解码能力,现在已经逐渐成为主流了。API21已经支持NDK方法了,MediaCodec api设计得非常精妙,另一个方面也是很多人觉得不好懂。内容MediaCodec的两个Buffer和三板斧MediaCodec内部包含InputBuffer和OutputBuffer,内部有一个自启线程,... 阅读详情
CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -UNDEBUG")

add_library(native-codec-jni SHARED
            looper.cpp
            native-codec-jni.cpp)

# Include libraries needed for native-codec-jni lib
target_link_libraries(native-codec-jni
                      android
                      log
                      mediandk
                      OpenMAXAL)

NativeCodec.java

/** Native methods, implemented in jni folder */
public static native void createEngine();
public static native boolean createStreamingMediaPlayer(AssetManager assetMgr, String filename);
public static native void setPlayingStreamingMediaPlayer(boolean isPlaying);
public static native void shutdown();
public static native void setSurface(Surface surface);
public static native void rewindStreamingMediaPlayer();

native-codec-jni.cpp

#include <assert.h>
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>

#include "looper.h"
#include "media/NdkMediaCodec.h"
#include "media/NdkMediaExtractor.h"

setSurface

// set the surface
void Java_com_example_nativecodec_NativeCodec_setSurface(JNIEnv *env, jclass clazz, jobject surface)
{
    // obtain a native window from a Java surface
    if (data.window) {
        ANativeWindow_release(data.window);
        data.window = NULL;
    }
    data.window = ANativeWindow_fromSurface(env, surface);
    LOGV("@@@ setsurface %p", data.window);
}

从一个java的surface获取window

createStreamingMediaPlayer

jboolean Java_com_example_nativecodec_NativeCodec_createStreamingMediaPlayer(JNIEnv* env,
        jclass clazz, jobject assetMgr, jstring filename)
{
    LOGV("@@@ create");

    // convert Java string to UTF-8
    const char *utf8 = env->GetStringUTFChars(filename, NULL);获取c文件字符串
    LOGV("opening %s", utf8);

    off_t outStart, outLen;
    int fd = AAsset_openFileDescriptor(AAssetManager_open(AAssetManager_fromJava(env, assetMgr), utf8, 0),
                                       &outStart, &outLen);//获取文件fd

    env->ReleaseStringUTFChars(filename, utf8);
    if (fd < 0) {
        LOGE("failed to open file: %s %d (%s)", utf8, fd, strerror(errno));
        return JNI_FALSE;
    }

    data.fd = fd;

    workerdata *d = &data;

    AMediaExtractor *ex = AMediaExtractor_new();//获取媒体的解析器
    media_status_t err = AMediaExtractor_setDataSourceFd(ex, d->fd,
                                                         static_cast<off64_t>(outStart),
                                                         static_cast<off64_t>(outLen));
    close(d->fd);
    if (err != AMEDIA_OK) {
        LOGV("setDataSource error: %d", err);
        return JNI_FALSE;
    }

    int numtracks = AMediaExtractor_getTrackCount(ex);//获取媒体轨,音频和视频

    AMediaCodec *codec = NULL;

    LOGV("input has %d tracks", numtracks);
    for (int i = 0; i < numtracks; i++) {
        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);//获取相应音频和视频轨的媒体信息
        const char *s = AMediaFormat_toString(format);把相应的媒体信息转换为字符串
        LOGV("track %d format: %s", i, s);
        const char *mime;
        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {//获取媒体类型,如video/avc等
            LOGV("no mime type");
            return JNI_FALSE;
        } else if (!strncmp(mime, "video/", 6)) {
            // Omitting most error handling for clarity.
            // Production code should check for errors.
            AMediaExtractor_selectTrack(ex, i);
            codec = AMediaCodec_createDecoderByType(mime);//根据媒体类型创建相应的codec
            AMediaCodec_configure(codec, format, d->window, NULL, 0);//codec配置
            d->ex = ex;
            d->codec = codec;
            d->renderstart = -1;
            d->sawInputEOS = false;
            d->sawOutputEOS = false;
            d->isPlaying = false;
            d->renderonce = true;
            AMediaCodec_start(codec);//开始codec
        }
        AMediaFormat_delete(format);删除相应的类型
    }

    mlooper = new mylooper();
    mlooper->post(kMsgCodecBuffer, d);//发送msg

    return JNI_TRUE;
}

setPlayingStreamingMediaPlayer

// set the playing state for the streaming media player
void Java_com_example_nativecodec_NativeCodec_setPlayingStreamingMediaPlayer(JNIEnv* env,
        jclass clazz, jboolean isPlaying)
{
    LOGV("@@@ playpause: %d", isPlaying);
    if (mlooper) {
        if (isPlaying) {
            mlooper->post(kMsgResume, &data);//设置播放的状态
        } else {
            mlooper->post(kMsgPause, &data);
        }
    }
}

shutdown

// shut down the native media system
void Java_com_example_nativecodec_NativeCodec_shutdown(JNIEnv* env, jclass clazz)
{
    LOGV("@@@ shutdown");
    if (mlooper) {
        mlooper->post(kMsgDecodeDone, &data, true /* flush */);//decode完毕
        mlooper->quit();
        delete mlooper;
        mlooper = NULL;
    }
    if (data.window) {
        ANativeWindow_release(data.window);
        data.window = NULL;
    }
}

rewindStreamingMediaPlayer

seek动作

// rewind the streaming media player
void Java_com_example_nativecodec_NativeCodec_rewindStreamingMediaPlayer(JNIEnv *env, jclass clazz)
{
    LOGV("@@@ rewind");
    if (mlooper) {
        mlooper->post(kMsgSeek, &data);
    }
}

looper.h

#include <pthread.h>
#include <semaphore.h>

struct loopermessage;

class looper {
    public:
        looper();
        looper& operator=(const looper& ) = delete;
        looper(looper&) = delete;
        virtual ~looper();

        void post(int what, void *data, bool flush = false);
        void quit();

        virtual void handle(int what, void *data);

    private:
        void addmsg(loopermessage *msg, bool flush);
        static void* trampoline(void* p);
        void loop();
        loopermessage *head;
        pthread_t worker;
        sem_t headwriteprotect;
        sem_t headdataavailable;
        bool running;
};

looper.cpp

// for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");
#include <android/log.h>
#define TAG "NativeCodec-looper"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)

//定义消息结构体

struct loopermessage;
typedef struct loopermessage loopermessage;

struct loopermessage {
    int what;
    void *obj;
    loopermessage *next;
    bool quit;
};
void* looper::trampoline(void* p) {
    ((looper*)p)->loop();
    return NULL;
}
//创建线程和初始化信号量
looper::looper() {
    sem_init(&headdataavailable, 0, 0);
    sem_init(&headwriteprotect, 0, 1);
    pthread_attr_t attr;
    pthread_attr_init(&attr);

    pthread_create(&worker, &attr, trampoline, this);
    running = true;
}

//把消息放到同步队列里面

void looper::post(int what, void *data, bool flush) {
    loopermessage *msg = new loopermessage();
    msg->what = what;
    msg->obj = data;
    msg->next = NULL;
    msg->quit = false;
    addmsg(msg, flush);
}

void looper::addmsg(loopermessage *msg, bool flush) {
    sem_wait(&headwriteprotect);
    loopermessage *h = head;

    if (flush) {
        while(h) {
            loopermessage *next = h->next;
            delete h;
            h = next;
        }
        h = NULL;
    }
    if (h) {
        while (h->next) {
            h = h->next;
        }
        h->next = msg;
    } else {
        head = msg;
    }
    LOGV("post msg %d", msg->what);
    sem_post(&headwriteprotect);
    sem_post(&headdataavailable);
}

//获取并处理消息,读写要同步

void looper::loop() {
    while(true) {
        // wait for available message
        sem_wait(&headdataavailable);

        // get next available message
        sem_wait(&headwriteprotect);
        loopermessage *msg = head;
        if (msg == NULL) {
            LOGV("no msg");
            sem_post(&headwriteprotect);
            continue;
        }
        head = msg->next;
        sem_post(&headwriteprotect);

        if (msg->quit) {
            LOGV("quitting");
            delete msg;
            return;
        }
        LOGV("processing msg %d", msg->what);
        handle(msg->what, msg->obj);
        delete msg;
    }
}

//退出添加退出的消息

void looper::quit() {
    LOGV("quit");
    loopermessage *msg = new loopermessage();
    msg->what = 0;
    msg->obj = NULL;
    msg->next = NULL;
    msg->quit = true;
    addmsg(msg, false);
    void *retval;
    pthread_join(worker, &retval);
    sem_destroy(&headdataavailable);
    sem_destroy(&headwriteprotect);
    running = false;
}

//处理消息

void looper::handle(int what, void* obj) {
    LOGV("dropping msg %d %p", what, obj);
}

 

 

 

 

Android NDK下编译libvpx实现VP8/VP9编解码 视频编解码技术是多媒体处理的核心基础,其中VP8/VP9作为Google主导的开源编码标准,凭借其优异的压缩效率在YouTube等平台广泛应用。本文从视频编码原理出发,重点解析如何在Android NDK环境下编译libvpx开源库。通过配置交叉编译工具链,针对armeabi-v7a、arm64-v8a等不同ABI架构进行优化,实现高性能的VP8/VP9编解码功能。特别针对移动端场景,介绍了如何通过NDK工具链优化编译参数,平衡性能与包体大小,为Android视频应用开发提供可靠的底层支持。 阅读详情

相关推荐

Android NDK之Lame初探

之前在做车载语音微信项目的时候,基于网页版微信原理,同时新增了一些功能,比如可以发送语音消息,由于微信网页版没有提供这个功能,所以在这里现将录音消息传到讯飞的车载服务服务器,然后获取URL,只发送URL就可以了。由于录音数据为pcm格式,pcm转wav比较容易,直接加个头文件就可以了,但是wav转mp3呢,在这里我是用了比较出名的MP3 Encoder库Lame,在使用Lame库的时候重新编译打包

wjk7186912的专栏 3019

NDK中使用 MediaCodec 编解码视频

背景 MediaCodec 作为Android自带的视频编解码工具,可以直接利用底层硬件编解码能力,现在已经逐渐成为主流了。API21已经支持NDK方法了,MediaCodec api设...

音视频开发进阶 6828

Android NDK下libvpx编译与VP8/VP9优化实践

视频编解码技术是多媒体开发的核心组件,其中VP8/VP9凭借其高效的压缩算法在移动端广泛应用。作为开源实现的libvpx库,通过NDK工具链交叉编译可在Android平台实现硬件级优化。编译过程涉及工具链配置、架构参数调优及性能测试,特别需要注意NEON指令集加速和ABI兼容性问题。实际工程中,合理的编译参数可使armeabi-v7a架构性能提升30%以上,同时需平衡编码质量与功耗表现。本文以NDK r18b+libvpx v1.14.1为例,详解从环境搭建到生产集成的完整链路,为视频处理应用开发提供实践参

angou6476的博客 602

[Android] native MediaCodec

见:/frameworks/av/media/ndk/include/media/NdkMediaCodec.h源码:/frameworks/av/media/libstagefright/MediaCodec.cpp。

ykun089的博客 775

EasyPlayer Android音频解码库(第二部分,封装解码器接口)

上一节我们讲了如何基于ffmpeg-android工程编译安卓上的支持音频的ffmpeg静态库,这篇文章我们将介绍如何封装安卓的解码器。首先,为了能让我们的app调用调用,我们需要定义一套Java的native接口,解码器主要有三个接口: create 创建解码器,参数分别为: codec:音频编码格式,参考EasyTypes.h里面的EASY_SDK_AUDIO_CODEC_*宏定义; sam

jyt0551的博客 1473

Android设备搭建AirPlay 2服务器:逆向协议实现跨平台投屏

流媒体传输协议是实现设备间音视频无线投屏的核心技术基础,其中RTSP(实时流协议)和RTP(实时传输协议)构成了数据流转发的信令与传输层。通过逆向工程实现私有协议栈,能够在非原生生态硬件上重建完整的投屏服务,其技术价值在于打破平台壁垒,实现跨生态系统的无缝媒体流转。这一原理在智能家居和影音娱乐场景中尤为重要,例如将闲置的Android电视盒子或基于ESP32的开发板改造为AirPlay 2接收端,支持多房间音频同步和无损音频播放。本文聚焦于如何在Android平台通过交叉编译C语言核心库,部署实现AirPl

601

Android鬼点子-通过Google官方示例学NDK(4)

如果你看遍了网上那些只是在C++里面输出一个 ‘ helloWorld ’ 的NDK教程的话,可以看看本系列的文章,本系列是通过NDK的运用的例子来学习NDK。 如果对这方面感兴趣,可以看看前三篇。 Android鬼点子-通过Google官方示例学NDK(1)——主要说的是如何在NDK使用多线程,还有就是基础的java与c++的相互调用。 Android鬼点子-通过Google官方示例学NDK(2...

weixin_34241036的博客 550

Coze平台智能体开发实战:打造测试工程师的AI助手

智能体是具备特定身份和能力的AI实体,由大语言模型(LLM)驱动,并通过插件、知识库和工作流扩展其能力。明确的角色定位:可以设定特定身份和专业领域扩展能力:通过插件连接外部工具和服务记忆能力:通过知识库存储和检索专属信息流程化处理:通过工作流执行复杂多步任务通过Coze平台创建测试工程师智能体,可以显著提升测试效率和质量。明确的角色定位:聚焦测试工程师的实际需求丰富的知识库:提供准确的领域知识支持合适的工具集成:通过插件连接现有工具链持续迭代优化:根据反馈不断改进智能体行为自动化测试生成。

hogwarts_beibei的博客 1005

2022-12-02 编译Android平台OpenCV,用到读取视频时报错:AMediaXXX

编译安卓平台OpenCV,报错AMediaXXX

悉尼没有雪 1516

海康威视4G摄像头直连实战:不用萤石云,手把手教你用ISUP SDK搞定跨网段视频流

本文详细介绍了如何通过海康威视ISUP SDK实现4G摄像头的直连通信,提供了一套零平台费、自主可控的技术方案。文章涵盖技术选型、设备端配置、服务器端开发及高级应用场景,特别适合中小型安防项目,帮助开发者解决跨网段视频流传输难题,降低长期运营成本。

weixin_27076351的博客 470

立创星火计划:基于STM32H743VIH6的Maverick_H743_MK1.0高性能开源飞控硬件全解析

本文详细解析了基于STM32H743VIH6核心的Maverick_H743_MK1.0高性能开源飞控硬件设计。重点阐述了采用双IMU冗余设计、高精度气压计、多路独立电源管理以及丰富接口(如CAN总线)的考量,旨在为穿越机、科研无人机等平台提供强大、可靠的飞行控制解决方案。

weixin_28787801的博客 198

10分钟跑通ndk-samples的25个官方示例:Android NDK快速上手指南

**ndk-samples** 是 Android 官方维护的 NDK(Native Development Kit)示例仓库,收录了 25 个可直接用 Android Studio 打开运行的官方示例应用,覆盖 JNI、OpenGL、Vulkan、音频、MIDI、神经网络 API 等原生开发场景。无论你是第一次接触 Android 原生开发,还是想为项目寻找 JNI 与 CMake 的参考实现,

gitblog_00315的博客 1070

如何绘制电气绝缘图(GB9706.1-2007和GB9706.1-2020差异)

是用来描述要考虑设备的绝缘状况和它们的值 ,从而便于和相关部门交流和沟通,如权威机构和检测实验室等。本文为绝缘图的实例, 包括所有相关的绝缘路径以及介电强度,爬电距离和电气间隙的值。

学海无涯的专栏 1万+

ndk:Rust开发者的终极Android NDK绑定库,从入门到精通指南

ndk 是一个为 Rust 开发者打造的 Android NDK 绑定库,它提供了安全、高效的 Rust 接口来访问 Android 原生开发工具包(NDK)的功能。对于希望使用 Rust 开发 Android 应用的开发者来说,这个库是连接 Rust 与 Android 原生 API 的桥梁,让你能够充分利用 Rust 的性能优势和内存安全特性来构建高质量的 Android 应用。 ## 为什

gitblog_01132的博客 427

把慢点掰开看:在 SAP HANA 中启用 SQL 优化耗时跟踪 SqlOptTime 的实战指南

摘要:SAP HANA 性能排查中,常出现SQL查询时快时慢的现象,表面看似执行问题,实则是编译阶段耗时过长。通过SQL优化跟踪工具SqlOptStep和SqlOptTime,可精准区分编译与执行时间。SqlOptStep展示优化树变化过程,而SqlOptTime则记录各阶段耗时,帮助定位问题根源。启用SqlOptTime需配置traceprofile,建议限定特定用户以减少系统负载。实际案例显示,报表响应慢常由计算视图编译(CeQo)导致,而非执行阶段问题。解决方案包括调整计算视图展开方式或优化基础视图结

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1009

Oracle EBS Interface/API(29) - AP发票API

续上编文章《Oracle EBS Interface/API(28) - 客制化开发AP付款API》背景,介绍如调用官方提供标准AP发票API调用 用户界面 Invoices(Payables Manager->Invoices->Entry->Invoices) Open Interface Invoices(Payables Manager->Invoices->Entry->Open Interface Invoices) 快速参考 参考点 内容.

陈显平的专栏-Oracle EBS 1150

Android NDK Samples:官方 NDK 示例仓库的构建、运行与架构全解析

> 本文以 [README.md](https://link.gitcode.com/i/a3de4249d0a475d4d4bf466f89a0c670) 为核心,结合 [ARCHITECTURE.md](https://link.gitcode.com/i/9aeb0216e865a9b94810e2d09538f34c)、[settings.gradle](https://link.gitc

gitblog_01087的博客 166

Claude Code 安装指南(Windows / macOS)

本文介绍了Claude Code的安装准备和基本流程。作为开发者工具,安装前需具备命令行操作能力、可用的Claude账号和稳定网络环境。Windows和macOS的安装都需通过终端完成,安装后以命令行形式运行。首次使用建议在熟悉项目中执行简单任务,逐步建立使用习惯。强调安装只是开始,后续需学习如何有效使用工具,包括任务提交流程和上下文提供方法。核心要点是:Claude Code是开发者工具,网络和终端环境是关键,建议先运行再深入理解。

sg_knight的专栏 4486

Android NDK开发:Android.mk配置详解与实战技巧

在移动开发中,NDK(Native Development Kit)是实现高性能计算和复用C/C++代码的关键技术。通过JNI桥接机制,开发者可以在Android应用中调用本地代码,显著提升计算密集型任务的执行效率。Android.mk作为传统的NDK构建脚本,其模块化设计和条件编译特性,特别适合处理多ABI架构适配和第三方库集成等复杂场景。本文以视频解码模块为例,详细解析Android.mk的基础配置、多模块管理技巧,并针对常见符号冲突和头文件路径问题提供解决方案,帮助开发者高效维护遗留项目。内容涵盖NE

weixin_34323858的博客 364

C#上位机之海康相机开发(SDK)实战教程

SDK的接口设计遵循“句柄-操作-释放”模式,掌握→→→的流程,就能完成基础开发;工业场景需重点关注稳定性:断连重连、内存管理、抗干扰(网口相机的IP设置、触发信号的可靠性);进阶功能(如图像处理、多相机同步)需结合视觉库(EmguCV)和硬件特性(GPIO触发),但前提是保证基础采集的稳定。建议新手先跑通SDK自带的C#示例,理解每个函数的作用,再逐步替换为自己的代码。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 2193

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Android 15 16K页对齐实战:NDK r27内存适配全指南

内存页大小是操作系统虚拟内存管理的基础概念,其原理直接影响DMA传输、GPU映射与JNI直接缓冲区等底层行为。在ARM64架构下,16K页通过减少TLB表项显著提升大内存设备的地址翻译效率,已成为Android 15的默认配置。这一变更带来核心的技术价值:更高缓存带宽利用率与更低TLB miss率,但同时也要求所有原生代码放弃‘4K硬编码’思维,转向运行时页大小感知。典型应用场景包括FFmpeg硬件解码、OpenGL纹理上传、NewDirectByteBuffer创建及POSIX内存分配等。本文聚焦NDK

weixin_34082695的博客 439

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Android NDK NN API Basic 示例深度解析:用 C API 构建 (tensor0+tensor1)×(tensor2+tensor3) 计算图

Android Neural Networks API(NN API)是 Android 8.0(API 27)起随 NDK 提供的一套底层推理接口,它允许开发者把训练好的模型以**计算图(graph)**的形式提交给系统,由系统分派到 CPU、GPU 或专用 NPU 等硬件加速器上执行。本篇文章以 ndk-samples 仓库中的 [nn-samples/basic 示例](https://li

gitblog_00249的博客 798

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

pure_live 自建 Android libmpv(mpv 0.41.0 + FFmpeg 9.0.2)的构建、校验与集成指南

pure_live 是一套以 Flutter + media_kit(libmpv)为核心的跨平台直播客户端。由于上游 media_kit 随包附带的 Android `libmpv.so` 基于 FFmpeg 7.1.3,无法解析 Shopee Live 与部分 17LIVE 直播间采用的 **codec-id-12 旧式 HEVC FLV** 流,项目基于 Predidit/libmpv-an

gitblog_00025的博客 414

Android音视频开发进阶:FFmpeg集成与实战指南

音视频处理是移动应用开发中的核心技术领域,其核心在于对多媒体数据的编解码、封装与处理。FFmpeg作为业界公认的开源音视频处理框架,提供了跨平台的完整解决方案,能够突破Android原生媒体API在格式支持、处理效率与跨平台兼容性方面的局限。通过集成FFmpeg,开发者可以实现专业级的视频编辑、格式转换、流媒体处理等高级功能,为应用带来强大的媒体处理能力。本文聚焦于Android平台,详细解析FFmpeg的交叉编译、NDK配置与JNI调用的完整流程,并通过实战演示如何构建一个视频信息提取的Demo应用,帮助

weixin_30535843的博客 399

NDK集成OpenCV与FFmpeg:CMake、JNI与交叉编译实战

移动端视频处理常面临性能瓶颈:纯 Java 操作 Bitmap 内存开销大,实时性差。Android NDK 提供了一条进入 C/C++ 高性能运算的通道,而 OpenCV 与 FFmpeg 的组合则是图像处理和音视频解码领域的经典方案。理解 NDK 的交叉编译原理,是正确集成这两个原生库的关键:通过 CMake 组织第三方预编译库、利用 NDK 工具链生成目标 ABI 的 .so、再以 JNI 实现 Java 与 C++ 的无缝桥接。这套技术栈在视频帧透视矫正、实时滤镜、视频解码与重编码等场景中,能显著降

weixin_33980459的博客 282

VLC Android 3.5.4 源码编译实战:Ubuntu 22.04 下 3 步完成 libvlc.aar 生成

本文详细介绍了在Ubuntu 22.04环境下编译VLC Android 3.5.4源码并生成libvlc.aar的完整流程。通过三步操作:环境准备与工具链配置、编译参数调整、生成libvlc.aar,开发者可以轻松集成强大的多媒体播放功能到Android应用中。文章还提供了常见问题解决方案和性能优化技巧,帮助开发者充分利用VLC的开源优势。

weixin_30855761的博客 523
上一篇: Android-NDK:native-media
下一篇: Android-NDK-audio-echo
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值