正在上传图片(0/1)

关于获取图像数据的问题。

 2
手机看帖 17 8463
各位,我想获得H264解码后的YUV数据或者RGB数据,进行一些分析和处理。请问SDK中有相关的API吗?有没有哪位高手指点一下。
评论
上传
你需要登录之后才能回帖    登录 | 注册
楼主   2015-6-8 推荐
通过一番努力,终于把H264的码流解码成YUV数据了。大概思路如下,希望可以帮助有同样问题的朋友。
1.由于DJIReceivedVideoDataCallBack类的onResult方法中获得数据并不是完整一帧,所以需要进行拼接获得完整的一帧数据再送到解码器。
2.通过H264协议可知,在H264码流中并没有帧的概念,只是一个一个的NALU。如果通过解析NALU还获取一帧比较麻烦,但是H264有一条规则就是在一帧结束后插入一个00 00 00 01 09(但并不是强制的,主要看编码器)。通过观察大疆提供的码流,比较幸运,该编码器采用了这条规则。所以我们可以判断一帧的结尾,这样就能获取一帧完整的数据,然后送到解码器。
2.在Android平台上解码器可以选择FFMEPG或者自带的mediacodec。据说后一种为硬件解码,效率较高,而且节能,但是对Android版本有要求,同时不同平台也可能也略有区别,毕竟硬件解码跟CPU有关系的。前一种需要在jni中进行编码,需要编译FFMPEG库。我选择了MediaCodec,使用起来比较方便,最后得到了YUV数据。IOS平台的话,应该也可以使用FFMPEG,IOS还在学习中,视频解码还没接触过,所以其他方法我暂时还不太了解。
3.得到YUV数据后,因为我希望获得RGB的数据进行一些数字图像处理的算法,所以要转换成RGB图像,这个很简单,有很多方法,我选择的是OPENCV中的cvtcolor转换,效果还不错。
下图为在手机用opencv保存的转换后的图像。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
DJI-SDK   2015-6-2 3#
您好,我们提供的是裸的H264码流,但是目前并不提供相关解码的API文档。
楼主   2015-6-3 4#
好吧,既然这样只能自己解码搞了。
1229198277   2015-6-4 5#
DJI-SDK 发表于 2015-6-2 14:47
您好,我们提供的是裸的H264码流,但是目前并不提供相关解码的API文档。
你好,为什么地图和视频集成在一块的时候,视频就会特别卡,延迟特别高,我用的是高得3D地图,请问是什么原因导致的呢?
DJI-SDK   2015-6-5 6#
您好,感谢您对于SDK的关注与支持。地图的渲染和优化以及视频的解码过程都是您自己实现的,建议您把地图和视频做在两个VIEW里面去实现。另外不排除使用的设备的处理器的运算能力的问题。
DJI-SDK   2015-6-5 7#
1229198277 发表于 2015-6-4 10:30
你好,为什么地图和视频集成在一块的时候,视频就会特别卡,延迟特别高,我用的是高得3D地图,请问是什么 ...
您好,感谢您对于SDK的关注与支持。地图的渲染和优化以及视频的解码过程都是您自己实现的,建议您把地图和视频做在两个VIEW里面去实现。另外不排除使用的设备的处理器的运算能力的问题。
LennonShawn  Phantom 3 Advanced认证用户 2015-6-9 9#
txyugood 发表于 2015-6-8 17:48
通过一番努力,终于把H264的码流解码成YUV数据了。大概思路如下,希望可以帮助有同样问题的朋友。
1.由于DJ ...

感谢分享干货!已经mark,或许未来能在我自己的项目里用上,再次感谢!
lanyusea  Inspire 1认证用户 2015-6-11 10#
txyugood 发表于 2015-6-8 17:48
通过一番努力,终于把H264的码流解码成YUV数据了。大概思路如下,希望可以帮助有同样问题的朋友。
1.由于DJ ...
Hi, 有两个地方不是很明白想请教一下。

1. 看到您说 0x00 0x00 0x00 0x01 0x09 意味着一帧的结束,但看别的地方的说明指的是0x00 0x00 0x00 0x01代表开始,中间是内容,0x09代表一帧结束。想问一下楼主是不是这样,以及每一帧都分割一定是buffer的分割吗?同一个buffer里面会不会含有两帧的内容?


2. 解码后的video stream buffer怎么显示在DjiGLSurfaceView上?找了一下没有除了setDataToDecoder之外的API,还是说这个时候需要我们自己新建一个view了?GLSurfaceView?
SDK板块日常维护志愿者。
我没有QQ,我也不接受私信提问。有问题请去论坛发帖,利人利己。

我没有QQ,我没有QQ,我没有QQ。
重要的事情说三遍。
楼主   2015-6-15 11#
lanyusea 发表于 2015-6-11 14:09
Hi, 有两个地方不是很明白想请教一下。

1. 看到您说 0x00 0x00 0x00 0x01 0x09 意味着一帧的结束,但看别 ...
1.第一个问题,我们收到的码流一个一个的NALU组成的,并没有帧的概念,每一个NALU都是0x00 0x00 0x00 0x01开头的,后面有编码的内容。只有在一帧结束后,会单独插入一个0x00 0x00 0x00 0x00 0x09,后面没有编码内容,0x09之后就是下一帧的起始了。SDK中的那个回调函数传过来的BUFFER一般是固定1024个字节的,不是完整一帧,有可能会存着上一帧的后半部分和下一帧的前半部分,所以要自己做个缓冲区,每次取完数据都要判定一下,缓冲区中是否有完整的一帧。
2.第二个问题,调用setDataToDecoder之后,DjiGLSurfaceView就会把数据渲染到Surface上了。DjiGLSurfaceView本身就是个SurfaceView了不需要再建。DjiGLSurfaceView具体怎么做的看不到,大概是在JNI中调用FFMPEG解码后,然后在渲染到Surface上吧。
lanyusea  Inspire 1认证用户 2015-6-15 12#
txyugood 发表于 2015-6-15 09:50
1.第一个问题,我们收到的码流一个一个的NALU组成的,并没有帧的概念,每一个NALU都是0x00 0x00 0x00 0x0 ...
谢谢,第二点还是不太明白。

当我调用MediaCodec进行解码之后怎么把YUV格式的video buffer显示在surfaceview上呢?新的buffer虽然不是H.264但是一样可以调用setDataToDecoder?

SDK板块日常维护志愿者。
我没有QQ,我也不接受私信提问。有问题请去论坛发帖,利人利己。

我没有QQ,我没有QQ,我没有QQ。
重要的事情说三遍。
楼主   2015-6-15 13#
lanyusea 发表于 2015-6-15 13:36
谢谢,第二点还是不太明白。

当我调用MediaCodec进行解码之后怎么把YUV格式的video buffer显示在surface ...
自己的Surface可以在mediacodec的configure配置的时候设置,然后解码后releaseOutputBuffer会显示,文档上是这么写的,我还没试过。
4423839   2015-7-14 14#
txyugood 发表于 2015-6-8 17:48
通过一番努力,终于把H264的码流解码成YUV数据了。大概思路如下,希望可以帮助有同样问题的朋友。
1.由于DJ ...
楼主,请问能否把H264的码流解码成YUV数据这一块的代码贴一下吗?
最近也在弄这块。

楼主   2015-7-24 15#
4423839 发表于 2015-7-14 15:09
楼主,请问能否把H264的码流解码成YUV数据这一块的代码贴一下吗?
最近也在弄这块。
最后我使用的是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;
}

我使用的有名管道来通信,也可以使用其他方法来做。
4423839   2015-8-14 16#
txyugood 发表于 2015-7-24 11:46
最后我使用的是FFMPEG软解的,思路大概和官方SDK中的解码应该是一样的。我贴出关键片段。
在Jni中FFMpeg ...
hi,最近终于把ffmpeg编译好了,我开始试你这个代码了,
你能把DecoderContext *ctx 这个数据结构贴一下吗,谢谢了!
还有就是在初始化里面这一句代码 ctx->pb = avio_alloc_context(ctx->videobuff,32768,0,NULL,read_video_data,NULL,NULL);
这个read_video_data函数可以这么用吗,都不用传参数的吗,c的代码不是很会,见笑了。
楼主   2015-9-24 17#
4423839 发表于 2015-8-14 13:33
hi,最近终于把ffmpeg编译好了,我开始试你这个代码了,
你能把DecoderContext *ctx 这个数据结构贴一下 ...
好久没来了,没看到回复,不知道现在对你还有没有帮助。

typedef struct DecoderContext {
  int color_format;
  struct AVCodec *codec;
  struct AVCodecContext *codec_ctx;
  struct AVFrame *src_frame;
  struct AVFrame *dst_frame;
  struct SwsContext *convert_ctx;
  AVIOContext * pb;
  AVFormatContext *ic;
  unsigned char * videobuff;
  int fifo_rfd;
  int fifo_wfd;
  int frame_ready;
} DecoderContext;

至于那个函数,这是函数指针,当做一个回调函数来用。
eva0564   2015-11-23 18#
楼主,请问下这个对视频的解码是用在安卓平台上的,如果要实现在pc上对h.264码流解码,存储并显示的话也是类似的方面嘛?
我之前看过一些资料,说是Directshow可以进行类似的处理,或者类似VLC的开源视频软件。
另外问一个比较基础的问题,如果要对视频数据进行处理显示的话,是不是都要使用类似ffmpeg的软件解码成数据流然后才能用类似VLC的开源软件进行存储播放。
Qingyan   2016-5-1 19#
txyugood 发表于 2015-7-24 11:46
最后我使用的是FFMPEG软解的,思路大概和官方SDK中的解码应该是一样的。我贴出关键片段。
在Jni中FFMpeg ...
楼主您好 请问您的解码程序直接放进cpp里,然后主程序中直接调用就可以了是吗?
取消 点赞 评论
分享至:
回复:
上传
取消 评论
快速回复 返回顶部 返回列表