static struct heif_error ffmpeg_v1_decode_image()

in libheif/plugins/decoder_ffmpeg.cc [330:534]


static struct heif_error ffmpeg_v1_decode_image(void* decoder_raw,
                                                  struct heif_image** out_img)
{
  struct ffmpeg_decoder* decoder = (struct ffmpeg_decoder*) decoder_raw;

  int heif_idrpic_size;
  int heif_vps_size;
  int heif_sps_size;
  int heif_pps_size;
  const unsigned char* heif_vps_data;
  const unsigned char* heif_sps_data;
  const unsigned char* heif_pps_data;
  const unsigned char* heif_idrpic_data;

  if ((decoder->NalMap.count(NAL_UNIT_VPS_NUT) > 0)
      && (decoder->NalMap.count(NAL_UNIT_SPS_NUT) > 0)
      && (decoder->NalMap.count(NAL_UNIT_PPS_NUT) > 0)
      )
  {
      heif_vps_size = decoder->NalMap[NAL_UNIT_VPS_NUT]->size();
      heif_vps_data = decoder->NalMap[NAL_UNIT_VPS_NUT]->data();

      heif_sps_size = decoder->NalMap[NAL_UNIT_SPS_NUT]->size();
      heif_sps_data = decoder->NalMap[NAL_UNIT_SPS_NUT]->data();

      heif_pps_size = decoder->NalMap[NAL_UNIT_PPS_NUT]->size();
      heif_pps_data = decoder->NalMap[NAL_UNIT_PPS_NUT]->data();
  }
  else
  {
      struct heif_error err = { heif_error_Decoder_plugin_error,
                                heif_suberror_End_of_data,
                                "Unexpected end of data" };
      return err;
  }

  if ((decoder->NalMap.count(NAL_UNIT_IDR_W_RADL) > 0) || (decoder->NalMap.count(NAL_UNIT_IDR_N_LP) > 0))
  {
      if (decoder->NalMap.count(NAL_UNIT_IDR_W_RADL) > 0)
      {
          heif_idrpic_data = decoder->NalMap[NAL_UNIT_IDR_W_RADL]->data();
          heif_idrpic_size = decoder->NalMap[NAL_UNIT_IDR_W_RADL]->size();
      }
      else
      {
          heif_idrpic_data = decoder->NalMap[NAL_UNIT_IDR_N_LP]->data();
          heif_idrpic_size = decoder->NalMap[NAL_UNIT_IDR_N_LP]->size();
      }
  }
  else
  {
      struct heif_error err = { heif_error_Decoder_plugin_error,
                                heif_suberror_End_of_data,
                                "Unexpected end of data" };
      return err;
  }

  const char hevc_AnnexB_StartCode[] = { 0x00, 0x00, 0x00, 0x01 };
  int hevc_AnnexB_StartCode_size = 4;

  size_t hevc_data_size = heif_vps_size + heif_sps_size + heif_pps_size + heif_idrpic_size + 4 * hevc_AnnexB_StartCode_size;
  uint8_t* hevc_data = (uint8_t*)malloc(hevc_data_size + AV_INPUT_BUFFER_PADDING_SIZE);

  //Copy hevc pps data
  uint8_t* hevc_data_ptr = hevc_data;
  memcpy(hevc_data_ptr, hevc_AnnexB_StartCode, hevc_AnnexB_StartCode_size);
  hevc_data_ptr += hevc_AnnexB_StartCode_size;
  memcpy(hevc_data_ptr, heif_vps_data, heif_vps_size);
  hevc_data_ptr += heif_vps_size;

  //Copy hevc sps data
  memcpy(hevc_data_ptr, hevc_AnnexB_StartCode, hevc_AnnexB_StartCode_size);
  hevc_data_ptr += hevc_AnnexB_StartCode_size;
  memcpy(hevc_data_ptr, heif_sps_data, heif_sps_size);
  hevc_data_ptr += heif_sps_size;

  //Copy hevc pps data
  memcpy(hevc_data_ptr, hevc_AnnexB_StartCode, hevc_AnnexB_StartCode_size);
  hevc_data_ptr += hevc_AnnexB_StartCode_size;
  memcpy(hevc_data_ptr, heif_pps_data, heif_pps_size);
  hevc_data_ptr += heif_pps_size;

  //Copy hevc idrpic data
  memcpy(hevc_data_ptr, hevc_AnnexB_StartCode, hevc_AnnexB_StartCode_size);
  hevc_data_ptr += hevc_AnnexB_StartCode_size;
  memcpy(hevc_data_ptr, heif_idrpic_data, heif_idrpic_size);

  //decoder->NalMap not needed anymore
  for (auto current = decoder->NalMap.begin(); current != decoder->NalMap.end(); ++current) {
      delete current->second;
  }
  decoder->NalMap.clear();

  const AVCodec* hevc_codec = NULL;
  AVCodecParserContext* hevc_parser = NULL;
  AVCodecContext* hevc_codecContext = NULL;
  AVPacket* hevc_pkt = NULL;
  AVFrame* hevc_frame = NULL;
  AVCodecParameters* hevc_codecParam = NULL;
  struct heif_color_profile_nclx* nclx = NULL;
  int ret = 0;

  struct heif_error err = heif_error_success;

  uint8_t* parse_hevc_data = NULL;
  int parse_hevc_data_size = 0;

  uint8_t video_full_range_flag = 0;
  uint8_t color_primaries = 0;
  uint8_t transfer_characteristics = 0;
  uint8_t matrix_coefficients = 0;

  hevc_pkt = av_packet_alloc();
  if (!hevc_pkt) {
    err = { heif_error_Memory_allocation_error, heif_suberror_Unspecified, "av_packet_alloc returned error" };
    goto errexit;
  }

  // Find HEVC video decoder
  hevc_codec = avcodec_find_decoder(AV_CODEC_ID_HEVC);

  if (!hevc_codec) {
    err = { heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "avcodec_find_decoder(AV_CODEC_ID_HEVC) returned error" };
    goto errexit;
  }

  hevc_parser = av_parser_init(hevc_codec->id);
  if (!hevc_parser) {
    err = { heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "av_parser_init returned error" };
    goto errexit;
  }

  hevc_codecContext = avcodec_alloc_context3(hevc_codec);
  if (!hevc_codecContext) {
    err = { heif_error_Memory_allocation_error, heif_suberror_Unspecified, "avcodec_alloc_context3 returned error" };
    goto errexit;
  }

  /* open it */
  if (avcodec_open2(hevc_codecContext, hevc_codec, NULL) < 0) {
    err = { heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "avcodec_open2 returned error" };
    goto errexit;
  }

  hevc_frame = av_frame_alloc();
  if (!hevc_frame) {
    err = { heif_error_Memory_allocation_error, heif_suberror_Unspecified, "av_frame_alloc returned error" };
    goto errexit;
  }

  parse_hevc_data = hevc_data;
  parse_hevc_data_size = (int)hevc_data_size;
  while (parse_hevc_data_size > 0) {
      hevc_parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
      ret = av_parser_parse2(hevc_parser, hevc_codecContext, &hevc_pkt->data, &hevc_pkt->size, parse_hevc_data, parse_hevc_data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
 
      if (ret < 0) {
	err = { heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "av_parser_parse2 returned error" };
	goto errexit;
      }
      parse_hevc_data += ret;
      parse_hevc_data_size -= ret;

      if (hevc_pkt->size)
      {
	err = hevc_decode(hevc_codecContext, hevc_frame, hevc_pkt, out_img);
	if (err.code != heif_error_Ok)
	  goto errexit;
      }
  }

  hevc_codecParam = avcodec_parameters_alloc();
  if (!hevc_codecParam) {
    err = { heif_error_Memory_allocation_error, heif_suberror_Unspecified, "avcodec_parameters_alloc returned error" };
    goto errexit;
  }
  if (avcodec_parameters_from_context(hevc_codecParam, hevc_codecContext) < 0)
  {
    err = { heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "avcodec_parameters_from_context returned error" };
    goto errexit;
  }

  video_full_range_flag = (hevc_codecParam->color_range == AVCOL_RANGE_JPEG) ? 1 : 0;
  color_primaries = hevc_codecParam->color_primaries;
  transfer_characteristics = hevc_codecParam->color_trc;
  matrix_coefficients = hevc_codecParam->color_space;

  nclx = heif_nclx_color_profile_alloc();
  heif_nclx_color_profile_set_color_primaries(nclx, static_cast<uint16_t>(color_primaries));
  heif_nclx_color_profile_set_transfer_characteristics(nclx, static_cast<uint16_t>(transfer_characteristics));
  heif_nclx_color_profile_set_matrix_coefficients(nclx, static_cast<uint16_t>(matrix_coefficients));
  nclx->full_range_flag = (bool)video_full_range_flag;
  heif_image_set_nclx_color_profile(*out_img, nclx);

errexit:
  if (hevc_codecParam) avcodec_parameters_free(&hevc_codecParam);
  if (hevc_data) free(hevc_data);
  if (hevc_parser) av_parser_close(hevc_parser);
  if (hevc_codecContext) avcodec_free_context(&hevc_codecContext);
  if (hevc_frame) av_frame_free(&hevc_frame);
  if (hevc_pkt) av_packet_free(&hevc_pkt);
  if (nclx) heif_nclx_color_profile_free(nclx);

  return err;
}