[FFmpeg + OpenGL + OpenSL ES]OpenGL ES 解码avFrame分离YUV数据分量 回调至java层 - 6

YUV420

YUV420是指:Y : UV = 4 : 1

YUV420P

YUV420P是指:YUV的排列方式,先将Y排列完,再将U排列完,最后将V排列完。如:

YYYYYYYYYYYYYYYY UUUU VVVV


FFmpeg解码出来的视频YUV数据是存储在AVFrame中的data里面,以YUV420P为视频数据给OPenGL渲染。

Y分量:frame->data[0]
U分量:frame->data[1]
V分量:frame->data[2]

绝大多数视频都是YUV420P格式的,对于不是YUV420P格式的,先将其转换(sws_scale)为YUV420P后再给OPenGL渲染。


在获取    avFrame    获取YUV420P数据

在    Video.cpp    中    void WlVideo::play()   的回调方法    void *playVideo(void *data)     里面获取获取YUV420P数据

*注意    导包    在Video.h

extern "C"
{
/* ... */
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
};
LOGE("子线程解码一个avFrame成功");
        if (avFrame->format == AV_PIX_FMT_YUV420P) {
            //渲染
        } else {
            AVFrame *pFrameYUV420 = av_frame_alloc();
            //获取存储空间大小
            //1转换格式
            //2视频宽
            //2视频高
            int num = av_image_get_buffer_size(
                    AV_PIX_FMT_YUV420P,
                    video->avCodecContext->width,
                    video->avCodecContext->height,
                    1);
            uint8_t *buffer = static_cast<uint8_t *>(av_malloc(num * sizeof(uint8_t)));
            //填充数据
            av_image_fill_arrays(
                    pFrameYUV420->data,
                    pFrameYUV420->linesize,
                    buffer,
                    AV_PIX_FMT_YUV420P,
                    video->avCodecContext->width,
                    video->avCodecContext->height,
                    1);
            //转码上下文
            SwsContext *sws_cxt = sws_getContext(
                    video->avCodecContext->width,
                    video->avCodecContext->height,
                    video->avCodecContext->pix_fmt,
                    video->avCodecContext->width,
                    video->avCodecContext->height,
                    AV_PIX_FMT_YUV420P,
                    SWS_BICUBIC, NULL, NULL, NULL);
            if (!sws_cxt) {
                av_frame_free(&pFrameYUV420);
                av_free(pFrameYUV420);
                pFrameYUV420 = NULL;
                av_free(buffer);
                continue;
            }
            //转换
            sws_scale(
                    sws_cxt,
                    avFrame->data,
                    avFrame->linesize,
                    0,
                    avFrame->height,
                    pFrameYUV420->data,
                    pFrameYUV420->linesize);
            //渲染
            //释放
            av_frame_free(&pFrameYUV420);
            av_free(pFrameYUV420);
            pFrameYUV420 = NULL;
            av_free(buffer);
            sws_freeContext(sws_cxt);
     }

在    player.java    写一个回调函数

public void onCallRenderYUV(int width, int height, byte[] y, byte[] u, byte[] v) {
        MyLog.d("获取到yuv数据");
    }

 在    WlCallJava.h    中写方法id 回调方法 

jmethodID jmid_renderyuv;
void onCallRenderYUV(int width, int height, uint8_t *fy, uint8_t *fu, uint8_t *fv);

 在    WlCallJava.cpp    构造函数中初始化方法id    

jmid_renderyuv = env->GetMethodID(jlz, "onCallRenderYUV", "(II[B[B[B)V");

  在    WlCallJava.cpp    中实现    onCallRenderYUV

void WlCallJava::onCallRenderYUV(int width, int height, uint8_t *fy, uint8_t *fu, uint8_t *fv) {
    JNIEnv *jniEnv;
    if (javaVM->AttachCurrentThread(&jniEnv, 0) != JNI_OK) {
        if (LOG_DEBUG) {
            LOGE("call onCallComplete worng");
        }
        return;
    }
    //将uint8_t转成jbyte数组
    //填充y
    jbyteArray y = jniEnv->NewByteArray(width * height);
    jniEnv->SetByteArrayRegion(y, 0, width * height, reinterpret_cast<const jbyte *>(fy));

    //填充u
    jbyteArray u = jniEnv->NewByteArray(width * height / 4);
    jniEnv->SetByteArrayRegion(u, 0, width * height / 4, reinterpret_cast<const jbyte *>(fu));

    //填充v
    jbyteArray v = jniEnv->NewByteArray(width * height / 4);
    jniEnv->SetByteArrayRegion(v, 0, width * height / 4, reinterpret_cast<const jbyte *>(fv));

    jniEnv->CallVoidMethod(jobj, jmid_renderyuv, width, height, y, u, v);
    //释放
    jniEnv->DeleteLocalRef(y);
    jniEnv->DeleteLocalRef(u);
    jniEnv->DeleteLocalRef(v);
    javaVM->DetachCurrentThread();
}

在    Video.cpp    中的    playVideo    回调方法也就是上文标注 //渲染  的部分调用回调函数

//        LOGE("子线程解码一个avFrame成功");
        if (avFrame->format == AV_PIX_FMT_YUV420P) {
            LOGE("AV_PIX_FMT_YUV420P");
            //渲染
            video->wlCallJava->onCallRenderYUV(
                    video->avCodecContext->width,
                    video->avCodecContext->height,
                    avFrame->data[0],
                    avFrame->data[1],
                    avFrame->data[2]);
        } else {
            /* ... */
            //转换
            /* ... */
            //渲染
            video->wlCallJava->onCallRenderYUV(
                    video->avCodecContext->width,
                    video->avCodecContext->height,
                    pFrameYUV420->data[0],
                    pFrameYUV420->data[1],
                    pFrameYUV420->data[2]);
            //释放
            /* ... */
        }

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值