美文网首页音视频及流媒体
Android万能视频播放器03-获取视频AVFrame的YUV

Android万能视频播放器03-获取视频AVFrame的YUV

作者: 张俊峰0613 | 来源:发表于2019-01-11 16:43 被阅读0次

什么是YUV格式

YUV,是一种颜色编码方法。Y表示明亮度,也就是灰度值。U和V则是色度、
浓度,作用是描述影像色彩及饱和度,用于指定像素的颜色。

主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)
分离,没有UV信息一样可以显示完整的图像,显示出来将是黑白效果。

什么是YUV420?

YUV420是指:Y : UV = 4 : 1

什么是YUV420P?

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

YYYYYYYYYYYYYYYY UUUU VVVV

获取YUV数据

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

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

绝大多数视频都是YUV420P格式的,对于不是YUV420P格式的,我们先将
其转换(sws_scale)为YUV420P后再给OPenGL ES渲染,所以C++层要调用Java的OpenGL ES渲染YUV数据。
Java层

/**
 *JfPlayer.java
 * @param width
 * @param height
 * @param y
 * @param u
 * @param v
 */
public void onCallRenderYUV(int width,int height,byte[] y,byte[] u,byte[] v){
    JfLog.d("获取到视频的数据");
}

C++层

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

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;
    }

    jbyteArray y = jniEnv->NewByteArray(width * height);
    jniEnv->SetByteArrayRegion(y, 0, width * height, reinterpret_cast<const jbyte *>(fy));

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

    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();
}

格式转换后渲染

if (avFrame->format == AV_PIX_FMT_YUV420P){
    //直接渲染
    LOGD("YUV420P");
    video->callJava->onCallRenderYUV(
            CHILD_THREAD,
            video->pVCodecCtx->width,
            video->pVCodecCtx->height,
            avFrame->data[0],
            avFrame->data[1],
            avFrame->data[2]);
} else {
    //转成YUV420P
    AVFrame *pFrameYUV420P = av_frame_alloc();
    int num = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,video->pVCodecCtx->width,video->pVCodecCtx->height,1);
    uint8_t *buffer = (uint8_t *)(av_malloc(num * sizeof(uint8_t)));
    av_image_fill_arrays(
            pFrameYUV420P->data,
            pFrameYUV420P->linesize,
            buffer,
            AV_PIX_FMT_YUV420P,
            video->pVCodecCtx->width,
            video->pVCodecCtx->height,
            1);
    SwsContext *sws_ctx = sws_getContext(
            video->pVCodecCtx->width,
            video->pVCodecCtx->height,
            video->pVCodecCtx->pix_fmt,
            video->pVCodecCtx->width,
            video->pVCodecCtx->height,
            AV_PIX_FMT_YUV420P,
            SWS_BICUBIC,
            NULL,NULL,NULL
            );

    if (!sws_ctx){
        av_frame_free(&pFrameYUV420P);
        av_free(pFrameYUV420P);
        av_free(buffer);
        continue;
    }

    sws_scale(
            sws_ctx,
            avFrame->data,
            avFrame->linesize,
            0,
            avFrame->height,
            pFrameYUV420P->data,
            pFrameYUV420P->linesize);//这里得到YUV数据
    LOGD("NO_YUV420P");
    //渲染
    video->callJava->onCallRenderYUV(
            CHILD_THREAD,
            video->pVCodecCtx->width,
            video->pVCodecCtx->height,
            pFrameYUV420P->data[0],
            pFrameYUV420P->data[1],
            pFrameYUV420P->data[2]);

    av_frame_free(&pFrameYUV420P);
    av_free(pFrameYUV420P);
    av_free(buffer);
    sws_freeContext(sws_ctx);
}

到这里就已经得到YUV数据了!

相关文章

网友评论

    本文标题:Android万能视频播放器03-获取视频AVFrame的YUV

    本文链接:https://www.haomeiwen.com/subject/vdwtdqtx.html