最后我使用的是FFMPEG软解的,思路大概和官方SDK中的解码应该是一样的。我贴出关键片段。
在Jni中FFMpeg初始化
JNIEXPORT void Java_org_opencv_samples_tutorial2_H264Decoder_nativeInit(JNIEnv* env, jobject thiz, jint color_format) {
DecoderContext *ctx = calloc(1, sizeof(DecoderContext));
int res;
D("Creating native H264 decoder context");
switch (color_format) {
case COLOR_FORMAT_YUV420:
ctx->color_format = PIX_FMT_YUV420P;
break;
case COLOR_FORMAT_RGB565LE:
ctx->color_format = PIX_FMT_RGB565LE;
break;
case COLOR_FORMAT_BGR32:
ctx->color_format = PIX_FMT_BGR32;
break;
}
//2015-6-30
res = mkfifo(FIFO_NAME, 0777);
if (res != 0)
{
E("Could not create fifo %s,error code : %d\n", FIFO_NAME,errno);
}
ctx->videobuff = (unsigned char *)av_mallocz(32768);
ctx->pb = avio_alloc_context(ctx->videobuff,32768,0,NULL,read_video_data,NULL,NULL);
ctx->ic = avformat_alloc_context();
ctx->ic->pb = ctx->pb;
res = avformat_open_input(&ctx->ic, "noting", NULL, NULL);
if ( res < 0) {
E("avformat open failed,err code:%d.\n",res);
return;
} else {
E("avformat_open_input success!\n");
}
//
ctx->codec = avcodec_find_decoder(CODEC_ID_H264);
ctx->codec_ctx = avcodec_alloc_context3(ctx->codec);
ctx->codec_ctx->pix_fmt = PIX_FMT_YUV420P;
ctx->codec_ctx->flags2 |= CODEC_FLAG2_CHUNKS;
ctx->src_frame = avcodec_alloc_frame();
ctx->dst_frame = avcodec_alloc_frame();
avcodec_open2(ctx->codec_ctx, ctx->codec, NULL);
set_ctx(env, thiz, ctx);
g_ctx = ctx;
g_frame_ready = &ctx->frame_ready;
E("H264 decoder init over!");
}
解码
JNIEXPORT jint Java_org_opencv_samples_tutorial2_H264Decoder_decodeVideo(JNIEnv* env, jobject thiz) {
DecoderContext *ctx = get_ctx(env, thiz);
AVPacket packet;
int res = -1;
av_init_packet(&packet);
if (av_read_frame(ctx->ic, &packet) >= 0)
{
// D("av_read_frame success!");
int frameFinished = 0;
res = avcodec_decode_video2(ctx->codec_ctx, ctx->src_frame, &frameFinished, &packet);
if (frameFinished)
{
ctx->frame_ready = 1;
}
av_free_packet(&packet);
}
return res;
}
获得一帧数据:
JNIEXPORT jlong Java_org_opencv_samples_tutorial2_H264Decoder_getFrame(JNIEnv* env, jobject thiz, jbyteArray out_buffer,jint size) {
DecoderContext *ctx = get_ctx(env, thiz);
if (!ctx->frame_ready)
return -1;
jbyte *out_buf=(*env)->GetByteArrayElements(env,out_buffer,0);
long out_buf_len = size;
int pic_buf_size = avpicture_get_size(ctx->color_format, ctx->codec_ctx->width, ctx->codec_ctx->height);
if (out_buf_len < pic_buf_size) {
D("Input buffer too small,pic_buf_size = %d",pic_buf_size);
return -1;
}
if (ctx->color_format == COLOR_FORMAT_YUV420) {
memcpy(ctx->src_frame->data, out_buffer, pic_buf_size);
}
else {
if (ctx->convert_ctx == NULL) {
ctx->convert_ctx = sws_getContext(ctx->codec_ctx->width, ctx->codec_ctx->height, ctx->codec_ctx->pix_fmt,
ctx->codec_ctx->width, ctx->codec_ctx->height, ctx->color_format, SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
avpicture_fill((AVPicture*)ctx->dst_frame, (uint8_t*)out_buf, ctx->color_format, ctx->codec_ctx->width,
ctx->codec_ctx->height);
sws_scale(ctx->convert_ctx, (const uint8_t**)ctx->src_frame->data, ctx->src_frame->linesize, 0, ctx->codec_ctx->height,
ctx->dst_frame->data, ctx->dst_frame->linesize);
//E("send vsync");
memcpy(s_pixels, out_buf, pic_buf_size);
send_vsync();
}
ctx->frame_ready = 0;
// if (ctx->src_frame->pkt_pts == AV_NOPTS_VALUE) {
// D("No PTS was passed from avcodec_decode!");
// }
(*env)->ReleaseByteArrayElements(env, out_buffer, out_buf, 0);
return ctx->src_frame->pkt_pts;
}
这是OnResult中调用的那个方法。
JNICALL Java_org_opencv_samples_tutorial2_H264Decoder_setDataToDecoder
(JNIEnv * env, jobject thiz , jbyteArray array, jint size)
{
jbyte *m_temp=(*env)->GetByteArrayElements(env,array,0);
if(g_write_fd < 0)
{
g_write_fd = open(FIFO_NAME, O_RDWR);
if(g_write_fd < 0)
return;
}
write(g_write_fd, (char *)m_temp, size);
(*env)->ReleaseByteArrayElements(env, array, m_temp, 0);
}
这个是自定义FFmpeg获取数据的方法。
int read_video_data(void *opaque, uint8_t *buf, int buf_size) {
int ret;
//E("buf_size = %d",buf_size);
if(g_read_fd < 0)
{
g_read_fd = open(FIFO_NAME, O_RDWR);
if(g_read_fd <0)
return 0;
}
ret = read(g_read_fd,buf,buf_size);
if(ret < 0)
return 0;
else
return ret;
}
我使用的有名管道来通信,也可以使用其他方法来做。
|