什么是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数据了!
网友评论