android平台下基于ffmpeg和ANativeWindow实现简单的视频播放器

android studio 2.2 cmake ffmpeg 简单的播放器(这里只有视频解码) 背景 在上一次的初次使用cmake编译jni后,这次cmake文件再次升级。利用网上的代码来编译一个简单的播放器。本文参照 Android+FFmpeg+ANativeWindow视频解码播放 一文,在cmake下重新编译。特将过程分享给大家。 gradle 配置 apply plugin: 'com.android.application' android { compi 阅读详情

音视频实践学习

概述

本节内容旨在了解本地视频的解码播放过程,暂时不设计音频的解码同步播放操作。

流程分析

在这里插入图片描述

工程实践

新建工程ffmpeg-single-play

定义java层的播放类:

package com.onzhou.ffmpeg.player;

public class NativePlayer {

    static {
        System.loadLibrary("native-player");
    }

    public native int playVideo(String videoPath, Object surface);

}

定义好本地的播放器头文件:native_play.h

class NativePlayer {

private:

    int width = 0;
    int height = 0;
    int bufferSize = 0;
    int videoIndex = -1;

    AVPacket *vPacket = NULL;
    AVFrame *vFrame = NULL, *pFrameRGBA = NULL;
    AVCodecContext *vCodecCtx = NULL;
    SwsContext *sws_ctx = NULL;
    AVFormatContext *pFormatCtx = NULL;
    uint8_t *out_buffer = NULL;
    ANativeWindow_Buffer windowBuffer;

    AVCodec *vCodec = NULL;

public:

    int PlayVideo(const char *input_str, ANativeWindow *nativeWindow);

};

定义相关的实现类文件:native_play.cpp

int NativePlayer::PlayVideo(const char *input_str, ANativeWindow *nativeWindow) {
    //1.初始化所有组件
    av_register_all();
    //分配一个AVFormatContext结构
    pFormatCtx = avformat_alloc_context();
    //2.打开输入文件
    if (avformat_open_input(&pFormatCtx, input_str, NULL, NULL) != 0) {
        LOGE("Could not open input stream");
        goto end_line;
    }
    //3.查找文件的流信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        LOGE("Could not find stream information");
        goto end_line;
    }
    //4.查找视频轨
    for (int index = 0; index < pFormatCtx->nb_streams; index++) {
        if (pFormatCtx->streams[index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoIndex = index;
            break;
        }
    }
    if (videoIndex == -1) {
        LOGE("Could not find a video stream");
        goto end_line;
    }
    //5.查找解码器
    vCodec = avcodec_find_decoder(pFormatCtx->streams[videoIndex]->codecpar->codec_id);
    if (vCodec == NULL) {
        LOGE("could not find codec");
        goto end_line;
    }
    //6.配置解码器
    vCodecCtx = avcodec_alloc_context3(vCodec);
    avcodec_parameters_to_context(vCodecCtx, pFormatCtx->streams[videoIndex]->codecpar);
    //7.打开解码器
    if (avcodec_open2(vCodecCtx, vCodec, NULL) < 0) {
        LOGE("Could not open codec");
        goto end_line;
    }
    width = vCodecCtx->width;
    height = vCodecCtx->height;
    //分配一个帧指针,指向解码后的原始帧
    vFrame = av_frame_alloc();
    vPacket = (AVPacket *) av_malloc(sizeof(AVPacket));
    pFrameRGBA = av_frame_alloc();
    //绑定输出buffer
    bufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height, 1);
    out_buffer = (uint8_t *) av_malloc(bufferSize * sizeof(uint8_t));
    av_image_fill_arrays(pFrameRGBA->data, pFrameRGBA->linesize, out_buffer, AV_PIX_FMT_RGBA,
                         width, height, 1);
    sws_ctx = sws_getContext(width, height, vCodecCtx->pix_fmt,
                                     width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL);
    //默认为RGB565
    if (ANativeWindow_setBuffersGeometry(nativeWindow, width, height, WINDOW_FORMAT_RGBA_8888) <
        0) {
        LOGE("Could not set buffers geometry");
        ANativeWindow_release(nativeWindow);
        goto end_line;
    }
    //读取帧
    while (av_read_frame(pFormatCtx, vPacket) >= 0) {
        if (vPacket->stream_index == videoIndex) {
            //视频解码
            if (avcodec_send_packet(vCodecCtx, vPacket) != 0) {
                return -1;
            }
            while (avcodec_receive_frame(vCodecCtx, vFrame) == 0) {
                //转化格式
                sws_scale(sws_ctx, (const uint8_t *const *) vFrame->data, vFrame->linesize,
                          0,
                          vCodecCtx->height,
                          pFrameRGBA->data, pFrameRGBA->linesize);
                if (ANativeWindow_lock(nativeWindow, &windowBuffer, NULL) < 0) {
                    LOGE("cannot lock window");
                } else {
                    //逐行复制
                    uint8_t *bufferBits = (uint8_t *) windowBuffer.bits;
                    for (int h = 0; h < height; h++) {
                        memcpy(bufferBits + h * windowBuffer.stride * 4,
                               out_buffer + h * pFrameRGBA->linesize[0],
                               pFrameRGBA->linesize[0]);
                    }
                    ANativeWindow_unlockAndPost(nativeWindow);
                }
            }
        }
        av_packet_unref(vPacket);
    }
    //释放内存
    sws_freeContext(sws_ctx);

    end_line:

    av_free(vPacket);
    av_free(pFrameRGBA);
    avcodec_close(vCodecCtx);
    avformat_close_input(&pFormatCtx);
    return 0;
}

最终播放效果如下:

项目地址:ffmpeg-single-play
https://github.com/byhook/ffmpeg4android

构建Android平台的FFmpeg ANativeWindow视频播放器 FFmpeg是一个开源项目,提供了从视频录制、转换、流化到播放的各项功能。它由一系列的库和工具构成,其中libavcodec库负责编解码,libavformat负责容器格式处理,libavfilter则用于视频过滤等高级功能。FFmpeg的集成使得Android应用能够轻松处理复杂的视频操作,比如视频格式转换、帧率调整等。 阅读详情

相关推荐

Android原生视频播放器开发:FFmpeg解码与ANativeWindow渲染实战

htmltable {th, td {th {pre {简介:FFmpegANativeWindow项目展示了在Android平台上使用FFmpeg进行视频解码,并将解码后的帧渲染到ANativeWindow的完整流程。FFmpeg用于处理多媒体文件的解码与格式转换,而ANativeWindow则负责原生图形渲染。本项目涵盖FFmpeg集成、视频解码流程、YUV格式转换、缓冲区管理、线程同步以及性能优化等内容,适合希望深入Android音视频开发的开发者进行实战学习。

weixin_36235398的博客 1096

FFmpeg4.3系列之54:Android+FFmpeg播放器与ij

FFmpeg系列之54【FFmpeg第四季Android音视频专题之4】Android+FFmpeg播放器与ijkplayer二次开发 我将带领大家来学习Android +FFmpeg 实现音视频的解码与播放,并学习ijkplayer二次开发。具体内容包括:NDK与ANativeWindow绘图、bmp格式与压缩原理、OpenGL.ES打造YUV播放器、颜色空间RGB与YUV底层原理详解、Android使用libyuv开源库、FFmpeg命令行处理PCM、Android.OPENSL.ES及PCM实战、Android+FFmpeg解码并播放音频、Android+FFmpeg解码并播放视频、Android播放器之ijkplayer二次开发等。 --------------------------------------------------------作为音视频开发小白,需要从最基本的图片和PCM开始,循序渐进,逐步到后期的编解码、软编硬编、视频特效、直播应用等。大家在学习过程中需要多动手,多思考,建议先系统的看一遍视频,然后第二遍跟着一起练习,然后再总结分析提升。

Android FFmpeg+ANativeWindow视频播放器底层实现指南

ANativeWindow是Android原生渲染的核心接口,用于在C/C++层直接操作Surface缓冲区,避免JNI跨层拷贝与Java层YUV处理开销;FFmpeg作为最成熟的开源音视频框架,提供完整的解封装、解码与时间戳管理能力。二者结合可构建低延迟(<30ms)、高可控性、支持H.265/私有SEI等非常规格式的自研播放器。其技术价值在于绕过MediaCodec黑盒、实现帧级同步与硬实时渲染,在课程设计、车载DVR、工业相机SDK等需‘底层定制’‘跨平台可移植’的场景中具备不可替代性。本文详解ANa

weixin_30036289的博客 276

FFmpeg 和 ANativeWindow 实现视频解码播放

FFmpeg 对一个 Mp4 文件的视频流进行解码,然后使用 libswscale 将解码后的 YUV 帧转换为 RGBA 帧,最后使用 ANativeWindow 进行渲染。

金戈鐡馬 598

Android NDK纯C视频播放器:FFmpeg解码+ANativeWindow渲染

ANativeWindow是Android NDK提供的原生Surface操作接口,用于在C/C++层直接与SurfaceFlinger交互,绕过Java UI组件实现低延迟渲染。其核心原理是通过BufferQueue机制管理图形缓冲区,配合FFmpeg完成解码、色彩空间转换(如YUV420P→RGBA)与帧同步。该技术具备零JNI胶水代码、强生命周期可控、易嵌入跨平台引擎等工程价值,广泛应用于音视频SDK、车载中控、AR/VR底层播放模块及定制化多媒体框架。本文详解ANativeWindow从初始化、缓冲

weixin_34216614的博客 255

纯C Android视频播放器:FFmpeg解码+ANativeWindow渲染

ANativeWindow是Android NDK层原生窗口抽象接口,用于在无Java UI依赖场景下直接操作Surface缓冲区;FFmpeg则是跨平台音视频处理核心库,支持软硬解码、格式转换与流控。其原理是通过解封装获取AVPacket,经解码生成AVFrame,再借助sws_scale完成YUV/RGB颜色空间转换,最终将像素数据提交至ANativeWindow管理的图形缓冲区实现零Java渲染。该技术方案具备低延迟、免GC干扰、易嵌入OpenGL ES等工程优势,广泛应用于车载HMI、工业中控、AR

weixin_30036289的博客 183

Android FFmpeg YUV转RGB及使用SurfaceView+ANativeWindow显示

1、FFmpeg像素格式转换 FFmpeg像素转换一般使用libswscale来进行 接口说明 1、 获取上下文SwsContext 一般我们使用下面两个函数来获取,sws_getCachedContext和sws_getContext略有不同的是,如果输入和输出的宽/高/格式不变,则会返回之前创建的context。 参数说明: 前三个参数分变为原始宽、高、格式(如RGBA8888,YUV420等...

myvest的专栏 2592

FFmpeg与ANativeWindow:Android自研播放器渲染实践

在Android应用开发中,视频播放是高频需求,多数场景依赖MediaPlayer或ExoPlayer即可完成。然而当需要对接特殊封装格式、实现逐帧滤镜或深度定制解码渲染链路时,系统播放器与MediaCodec的封装反而成为瓶颈。理解底层多媒体原理变得尤为关键,FFmpeg作为业界开源的多媒体处理库,能够灵活完成解封装与解码;ANativeWindow是Android NDK提供的原生窗口接口,可直接将像素数据提交至Surface显示,绕开Java层Bitmap与Canvas开销。从概念到工程实践,两者结合

weixin_29185199的博客 287

Android+FFmpeg+ANativeWindow视频解码播放

使用FFmpeg解码视频文件,利用ANativeWindow渲染视频帧的一个建议播放器介绍。

Glouds的专栏 1万+

FFmpeg解码与ANativeWindow渲染:构建Android原生视频播放器内核

在Android音视频开发中,绕开Java层直接操作底层渲染通道,是构建高性能播放器引擎的关键能力。NDK开发中,FFmpeg承担了解封装与解码的重任,能将H.264等压缩码流还原为YUV原始帧;而ANativeWindow作为Surface的Native级窗口句柄,通过锁缓冲、填充像素、提交显示三步即可完成直出渲染。这条纯C/C++链路避免了Java堆拷贝与中间层开销,在低延迟监控预览、自定义播放器、视频处理管线等场景极具工程价值。但实践中需重点处理YUV到RGB的像素格式转换、ANativeWindow

weixin_27076351的博客 240

FFmpeg直写ANativeWindow实现零拷贝硬解渲染

ANativeWindow是Android底层图形缓冲区调度接口,本质为Gralloc HAL的C语言抽象,用于在CPU与GPU间高效共享视频帧数据。其核心原理是绕过传统解码→内存拷贝→OpenGL上传的多步链路,让FFmpeg硬解器(MediaCodec)直接将NV12等YUV格式帧写入由SurfaceFlinger管理的显存级buffer,从而消除libswscale转换和glTexImage2D上传带来的12~18ms隐性开销。该技术显著提升低延迟播放、跨平台SDK及实时音视频场景下的渲染性能,适用于

weixin_30872577的博客 166

Android 直接显示yuv数据 通过Awesomeplayer方式直接显示(三)

研究一段时间Android的surface系统,一直执着地认为所有在surface或者屏幕上显示的画面,必须要转换成RGB才能显示,yuv数据也要通过颜色空间转换成RGB才能显示。可最近在研究stagefright视频显示时发现,根本找不到omx解码后的yuv是怎么转换成RGB的代码,yuv数据在render之后就找不到去向了,可画面确确实实的显示出来了,这从此颠覆了yuv必须要转换成RGB才能显

郭风朴(guofengpu)的Android影音专栏 3072

Android FFmpeg ANativeWindow视频解码播放器

使用FFmpeg和ANativeWindow的一个Android的视频解码播放器

fanplayer跨平台渲染架构:GDI、WaveOut与ANativeWindow对比

fanplayer是一款基于ffmpeg的跨平台视频播放器,支持Windows和Android系统。本文将深入解析其核心渲染架构,对比Windows平台的GDI图形渲染、WaveOut音频输出与Android平台的ANativeWindow显示技术,帮助开发者理解不同系统下的媒体处理机制。 ## 🖥️ Windows平台渲染方案 ### GDI图形渲染:轻量级2D绘图方案 GDI(Grap

gitblog_00037的博客 872

Android 直接播放yuv420p/yuvi420文件

Android 直接播放yuv420p/yuvi420文件,直接读取文件进行播放,请参照源码

FFmpegANativeWindow:视频播放器使用FFmpeg,渲染到ANativeWindow

FFmpegANativeWindow

Android基础--利用ANativeWindow显示视频

Android FFmpeg专题结构 利用Android 写视频显示应用时,经常会用到SurfaceView等控件来显示视频。 在前面的文章Android基础–SurfaceView, Surface, SurfaceHolder中,也简单得提了一下SurfaceView和Surface的关系:SurfaceView提供了一个专门用于绘制的surface。java层实际上是利用SurfaceView将视频数据渲染到Surface上。 而Native层要渲染视频可以通过ANativeWindow来渲染。 Su

yizhongliu的专栏 1958

VFH避障/局部路径规划算法

VFH避障/局部路径规划算法 本文章是我看《The Vector Field Histogram-Fast Obstacle Avoidance for Mobile Robots》论文时候的笔记,感兴趣的欢迎去看原论文。 VFH中有一些概念是从其他算法中借鉴过来的,并且VFH算法本身就是对VFF(Virtural Force Field)的一种改进,其中有很多的相似之处。所以按照原论文的思路,先介绍由CMU提出的Certainty Grid算法中的一些概念,再介绍一下势场法中的一些概念,接着介绍一下VFF

qq_41845878的博客 8118

Android FFmpeg视频播放器二 视频封装格式解码播放

Android FFmpeg视频播放器一解封装视频解封装之后就会得到音频流和视频流,解封状得到的数据是AVPackage类型数据,需要进一步解码成AVFrame一帧一帧数据才能进行播放。 由于是耗时操作,所以先pthread_create创建线程 开启循环从AVPackage队列获取AVPackage数据进行解码操作 frames 是视频帧队列,队列的阈值是AV_MAX_SIZE,这个值可以自己设置,不要太大,否则在队列保存的数据太大 packets.getQueueAndDel(pkt):从队列获取AV

风的轨迹 1265

AMD显卡跑Ollama大模型?手把手教你搞定Windows下GPU识别与加速(以RX 9600XT为例)

本文详细指导如何在Windows系统下使用AMD显卡(如RX 9600XT)运行Ollama大模型,解决GPU识别与加速问题。通过获取非官方Ollama版本、替换ROCm库文件等步骤,实现高效的GPU加速推理,提升大语言模型的运行效率。

weixin_28707365的博客 481

信创迁移实战:在银河麒麟V10-SP1上为Milvus离线部署Docker(附SP1专属避坑指南)

本文详细介绍了在银河麒麟V10-SP1 x86_64环境下离线部署Docker的全流程,特别针对该版本特有的自定义目录安装失败问题提供深度分析和解决方案。通过实战验证的步骤和避坑指南,帮助用户在信创项目中顺利完成Milvus等应用的容器化部署。

weixin_30687051的博客 532
上一篇: android平台下基于ffmpeg采集Camera数据编码成H.264推流到RTMP服务器
下一篇: android平台下基于ffmpeg实现对相机预览截图的功能(NV21数据编码为JPEG文件)
handy周
handy周 领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
博客等级 码龄14年 507粉丝 240原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值