您的位置:首页 > 其它

EasyPlayer支持YUV数据导出功能

2018-02-10 10:44 573 查看
我们可能会遇到这样的功能,播放一个视频的同时,再把这个视频推送出去,或者对视频数据进行智能分析等处理.这样我们就迫切需要得到视频的原始数据.基于这个需求,EasyPlayer增加了获取视频YUV数据的功能.

它的原理是这样的:

Created with Raphaël 2.1.2EasyPlayerClientEasyPlayerClientDecoderDecoderYUVYUV读取媒体流解码,获得YUV数据,导出

一般的播放器在解码之后,直接渲染YUV视频帧,并不做导出处理.因此在实现的时候,我们需要完善第二部分的功能,即解码后的YUV数据导出来.

解码部分需要通过jni来封装给java层调用.

下面代码片段就是解码并返回YUV数据的片段:

nRet = avcodec_decode_video2(pComponent->pCodecCtx, pComponent->pFrame, &got_picture, &packet);
av_free_packet(&packet);
if (nRet < 0)
{
char av_error[AV_ERROR_MAX_STRING_SIZE] = { 0 };
LOGI("avcodec_decode_video2 result %d %s \r\n", nRet,
av_make_error_string(av_error, AV_ERROR_MAX_STRING_SIZE, nRet));
return nRet;
}
if( got_picture)    // 解出一个视频帧了.
{
width = pComponent->pFrame->width;
height = pComponent->pFrame->height;
int size = avpicture_get_size((AVPixelFormat)pComponent->pFrame->format, width, height);
uint8_t* buffer = NULL;
if (*pYUV == NULL){
buffer = (uint8_t*)av_malloc(size);
}else{
buffer = (uint8_t *)*pYUV;
}
unsigned int y = width * pComponent->pFrame->height;
unsigned int u = width/2 * pComponent->pFrame->height / 2;
unsigned int v = width/2 * pComponent->pFrame->height / 2;

for (int l = 0;l<height;l++){
memcpy(buffer + l*width, pComponent->pFrame->data[0] + pComponent->pFrame->linesize[0] * l, width);
}

for (int l = 0;l<height/2;l++){
memcpy(buffer + y +  l*width/2, pComponent->pFrame->data[1] + pComponent->pFrame->linesize[1] * l, width/2);
memcpy(buffer + y + u + l*width/2, pComponent->pFrame->data[2] + pComponent->pFrame->linesize[2] * l, width/2);
}
// 将视频帧付给YUV buffer.
*pYUV = buffer;
}


下面是调用的片段:

unsigned char *pYUV = NULL;
unsigned int width;
unsigned int height;
VIDEO_HANDLE *vh =(VIDEO_HANDLE*) handle;
int r = h264_decoder_decode(vh->handle_h264, (unsigned char *)(pBuffer + offset), size, &pYUV, width, height);
jobject buf = NULL;
if (r > 0){
buf = pEnv->NewDirectByteBuffer(pYUV, width*height * 3/2);
}
return buf;


当解码成功后,我们创建一个DirectBuffer,将yuv数据返回到JAVA层.这样,播放器就可以获取到YUV数据了.

注意这里的pYUV数据,是我们在decoder内部开辟的,我们需要在JAVA层使用结束后,主动释放.该函数传入上个函数返回的DirectBuffer,在底层会获取到其对应的YUV 数据块,然后再释放它.

释放YUV:

JNIEXPORT void JNICALL Java_org_easydarwin_video_VideoCodec_releaseYUV(                                                                             JNIEnv *pEnv, jclass jobj, jobject buffer)
{
void *pYUV = pEnv->GetDirectBufferAddress(buffer);
h264_decoder_release((unsigned char *)pYUV);
}


最后,我们看看上层的调用:

// 解码
ByteBuffer buf = mDecoder.decodeFrameYUV(frameInfo, size);
// 将YUV buffer 回调给上层.
if (i420callback != null && buf != null)        i420callback.onI420Data(buf);
// 释放YUV buffer
if (buf != null) mDecoder.releaseBuffer(buf);


关于EasyPlayer流媒体播放器

An elegant, simple, fast android RTSP/RTMP/HLS/HTTP Player.EasyPlayer support RTSP(RTP over TCP/UDP)version & Pro version,cover all kinds of streaming media!EasyPlayer是一款精炼、高效、稳定的流媒体播放器,分为RTSP版、RTMP版和Pro版三个版本,支持各种各样的流媒体音视频协议和文件的播放,在安防、互联网、教育、录播、IPTV等多个领域大放异彩,广泛应用!

EasyPlayer:https://github.com/EasyDSS/EasyPlayer

点击链接加入群【EasyPlayer】:544917793
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐