예제 #1
0
static vpx_codec_err_t update_error_state(vpx_codec_alg_priv_t *ctx,
                           const struct vpx_internal_error_info *error) {
  if (error->error_code)
    set_error_detail(ctx, error->has_detail ? error->detail : NULL);

  return error->error_code;
}
예제 #2
0
static vpx_codec_err_t ctrl_set_reference(vpx_codec_alg_priv_t *ctx,
                                          va_list args) {
  vpx_ref_frame_t *const data = va_arg(args, vpx_ref_frame_t *);

  // Only support this function in serial decode.
  if (ctx->frame_parallel_decode) {
    set_error_detail(ctx, "Not supported in frame parallel decode");
    return VPX_CODEC_INCAPABLE;
  }

  if (data) {
    vpx_ref_frame_t *const frame = (vpx_ref_frame_t *)data;
    YV12_BUFFER_CONFIG sd;
    VPxWorker *const worker = ctx->frame_workers;
    FrameWorkerData *const frame_worker_data = (FrameWorkerData *)worker->data1;
    image2yuvconfig(&frame->img, &sd);
    return vp9_set_reference_dec(&frame_worker_data->pbi->common,
                                 (VP9_REFFRAME)frame->frame_type, &sd);
  } else {
    return VPX_CODEC_INVALID_PARAM;
  }
}
예제 #3
0
static vpx_codec_err_t decoder_decode(vpx_codec_alg_priv_t *ctx,
                                      const uint8_t *data, unsigned int data_sz,
                                      void *user_priv, long deadline) {
  const uint8_t *data_start = data;
  const uint8_t * const data_end = data + data_sz;
  vpx_codec_err_t res;
  uint32_t frame_sizes[8];
  int frame_count;

  if (data == NULL && data_sz == 0) {
    ctx->flushed = 1;
    return VPX_CODEC_OK;
  }

  // Reset flushed when receiving a valid frame.
  ctx->flushed = 0;

  // Initialize the decoder workers on the first frame.
  if (ctx->frame_workers == NULL) {
    const vpx_codec_err_t res = init_decoder(ctx);
    if (res != VPX_CODEC_OK)
      return res;
  }

  res = vp9_parse_superframe_index(data, data_sz, frame_sizes, &frame_count,
                                   ctx->decrypt_cb, ctx->decrypt_state);
  if (res != VPX_CODEC_OK)
    return res;

  if (ctx->frame_parallel_decode) {
    // Decode in frame parallel mode. When decoding in this mode, the frame
    // passed to the decoder must be either a normal frame or a superframe with
    // superframe index so the decoder could get each frame's start position
    // in the superframe.
    if (frame_count > 0) {
      int i;

      for (i = 0; i < frame_count; ++i) {
        const uint8_t *data_start_copy = data_start;
        const uint32_t frame_size = frame_sizes[i];
        if (data_start < data
            || frame_size > (uint32_t) (data_end - data_start)) {
          set_error_detail(ctx, "Invalid frame size in index");
          return VPX_CODEC_CORRUPT_FRAME;
        }

        if (ctx->available_threads == 0) {
          // No more threads for decoding. Wait until the next output worker
          // finishes decoding. Then copy the decoded frame into cache.
          if (ctx->num_cache_frames < FRAME_CACHE_SIZE) {
            wait_worker_and_cache_frame(ctx);
          } else {
            // TODO(hkuang): Add unit test to test this path.
            set_error_detail(ctx, "Frame output cache is full.");
            return VPX_CODEC_ERROR;
          }
        }

        res = decode_one(ctx, &data_start_copy, frame_size, user_priv,
                         deadline);
        if (res != VPX_CODEC_OK)
          return res;
        data_start += frame_size;
      }
    } else {
      if (ctx->available_threads == 0) {
        // No more threads for decoding. Wait until the next output worker
        // finishes decoding. Then copy the decoded frame into cache.
        if (ctx->num_cache_frames < FRAME_CACHE_SIZE) {
          wait_worker_and_cache_frame(ctx);
        } else {
          // TODO(hkuang): Add unit test to test this path.
          set_error_detail(ctx, "Frame output cache is full.");
          return VPX_CODEC_ERROR;
        }
      }

      res = decode_one(ctx, &data, data_sz, user_priv, deadline);
      if (res != VPX_CODEC_OK)
        return res;
    }
  } else {
    // Decode in serial mode.
    if (frame_count > 0) {
      int i;

      for (i = 0; i < frame_count; ++i) {
        const uint8_t *data_start_copy = data_start;
        const uint32_t frame_size = frame_sizes[i];
        vpx_codec_err_t res;
        if (data_start < data
            || frame_size > (uint32_t) (data_end - data_start)) {
          set_error_detail(ctx, "Invalid frame size in index");
          return VPX_CODEC_CORRUPT_FRAME;
        }

        res = decode_one(ctx, &data_start_copy, frame_size, user_priv,
                         deadline);
        if (res != VPX_CODEC_OK)
          return res;

        data_start += frame_size;
      }
    } else {
      while (data_start < data_end) {
        const uint32_t frame_size = (uint32_t) (data_end - data_start);
        const vpx_codec_err_t res = decode_one(ctx, &data_start, frame_size,
                                               user_priv, deadline);
        if (res != VPX_CODEC_OK)
          return res;

        // Account for suboptimal termination by the encoder.
        while (data_start < data_end) {
          const uint8_t marker = read_marker(ctx->decrypt_cb,
                                             ctx->decrypt_state, data_start);
          if (marker)
            break;
          ++data_start;
        }
      }
    }
  }

  return res;
}
예제 #4
0
static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx,
                                  const uint8_t **data, unsigned int data_sz,
                                  void *user_priv, int64_t deadline) {
  const VPxWorkerInterface *const winterface = vpx_get_worker_interface();
  (void)deadline;

  // Determine the stream parameters. Note that we rely on peek_si to
  // validate that we have a buffer that does not wrap around the top
  // of the heap.
  if (!ctx->si.h) {
    int is_intra_only = 0;
    const vpx_codec_err_t res =
        decoder_peek_si_internal(*data, data_sz, &ctx->si, &is_intra_only,
                                 ctx->decrypt_cb, ctx->decrypt_state);
    if (res != VPX_CODEC_OK)
      return res;

    if (!ctx->si.is_kf && !is_intra_only)
      return VPX_CODEC_ERROR;
  }

  if (!ctx->frame_parallel_decode) {
    VPxWorker *const worker = ctx->frame_workers;
    FrameWorkerData *const frame_worker_data = (FrameWorkerData *)worker->data1;
    frame_worker_data->data = *data;
    frame_worker_data->data_size = data_sz;
    frame_worker_data->user_priv = user_priv;
    frame_worker_data->received_frame = 1;

    // Set these even if already initialized.  The caller may have changed the
    // decrypt config between frames.
    frame_worker_data->pbi->decrypt_cb = ctx->decrypt_cb;
    frame_worker_data->pbi->decrypt_state = ctx->decrypt_state;

    worker->had_error = 0;
    winterface->execute(worker);

    // Update data pointer after decode.
    *data = frame_worker_data->data_end;

    if (worker->had_error)
      return update_error_state(ctx, &frame_worker_data->pbi->common.error);

    check_resync(ctx, frame_worker_data->pbi);
  } else {
    VPxWorker *const worker = &ctx->frame_workers[ctx->next_submit_worker_id];
    FrameWorkerData *const frame_worker_data = (FrameWorkerData *)worker->data1;
    // Copy context from last worker thread to next worker thread.
    if (ctx->next_submit_worker_id != ctx->last_submit_worker_id)
      vp9_frameworker_copy_context(
          &ctx->frame_workers[ctx->next_submit_worker_id],
          &ctx->frame_workers[ctx->last_submit_worker_id]);

    frame_worker_data->pbi->ready_for_new_data = 0;
    // Copy the compressed data into worker's internal buffer.
    // TODO(hkuang): Will all the workers allocate the same size
    // as the size of the first intra frame be better? This will
    // avoid too many deallocate and allocate.
    if (frame_worker_data->scratch_buffer_size < data_sz) {
      frame_worker_data->scratch_buffer =
          (uint8_t *)vpx_realloc(frame_worker_data->scratch_buffer, data_sz);
      if (frame_worker_data->scratch_buffer == NULL) {
        set_error_detail(ctx, "Failed to reallocate scratch buffer");
        return VPX_CODEC_MEM_ERROR;
      }
      frame_worker_data->scratch_buffer_size = data_sz;
    }
    frame_worker_data->data_size = data_sz;
    memcpy(frame_worker_data->scratch_buffer, *data, data_sz);

    frame_worker_data->frame_decoded = 0;
    frame_worker_data->frame_context_ready = 0;
    frame_worker_data->received_frame = 1;
    frame_worker_data->data = frame_worker_data->scratch_buffer;
    frame_worker_data->user_priv = user_priv;

    if (ctx->next_submit_worker_id != ctx->last_submit_worker_id)
      ctx->last_submit_worker_id =
          (ctx->last_submit_worker_id + 1) % ctx->num_frame_workers;

    ctx->next_submit_worker_id =
        (ctx->next_submit_worker_id + 1) % ctx->num_frame_workers;
    --ctx->available_threads;
    worker->had_error = 0;
    winterface->launch(worker);
  }

  return VPX_CODEC_OK;
}
예제 #5
0
static vpx_codec_err_t init_decoder(vpx_codec_alg_priv_t *ctx) {
  int i;
  const VPxWorkerInterface *const winterface = vpx_get_worker_interface();

  ctx->last_show_frame = -1;
  ctx->next_submit_worker_id = 0;
  ctx->last_submit_worker_id = 0;
  ctx->next_output_worker_id = 0;
  ctx->frame_cache_read = 0;
  ctx->frame_cache_write = 0;
  ctx->num_cache_frames = 0;
  ctx->need_resync = 1;
  ctx->num_frame_workers =
      (ctx->frame_parallel_decode == 1) ? ctx->cfg.threads: 1;
  if (ctx->num_frame_workers > MAX_DECODE_THREADS)
    ctx->num_frame_workers = MAX_DECODE_THREADS;
  ctx->available_threads = ctx->num_frame_workers;
  ctx->flushed = 0;

  ctx->buffer_pool = (BufferPool *)vpx_calloc(1, sizeof(BufferPool));
  if (ctx->buffer_pool == NULL)
    return VPX_CODEC_MEM_ERROR;

#if CONFIG_MULTITHREAD
    if (pthread_mutex_init(&ctx->buffer_pool->pool_mutex, NULL)) {
      set_error_detail(ctx, "Failed to allocate buffer pool mutex");
      return VPX_CODEC_MEM_ERROR;
    }
#endif

  ctx->frame_workers = (VPxWorker *)
      vpx_malloc(ctx->num_frame_workers * sizeof(*ctx->frame_workers));
  if (ctx->frame_workers == NULL) {
    set_error_detail(ctx, "Failed to allocate frame_workers");
    return VPX_CODEC_MEM_ERROR;
  }

  for (i = 0; i < ctx->num_frame_workers; ++i) {
    VPxWorker *const worker = &ctx->frame_workers[i];
    FrameWorkerData *frame_worker_data = NULL;
    winterface->init(worker);
    worker->data1 = vpx_memalign(32, sizeof(FrameWorkerData));
    if (worker->data1 == NULL) {
      set_error_detail(ctx, "Failed to allocate frame_worker_data");
      return VPX_CODEC_MEM_ERROR;
    }
    frame_worker_data = (FrameWorkerData *)worker->data1;
    frame_worker_data->pbi = vp9_decoder_create(ctx->buffer_pool);
    if (frame_worker_data->pbi == NULL) {
      set_error_detail(ctx, "Failed to allocate frame_worker_data");
      return VPX_CODEC_MEM_ERROR;
    }
    frame_worker_data->pbi->frame_worker_owner = worker;
    frame_worker_data->worker_id = i;
    frame_worker_data->scratch_buffer = NULL;
    frame_worker_data->scratch_buffer_size = 0;
    frame_worker_data->frame_context_ready = 0;
    frame_worker_data->received_frame = 0;
#if CONFIG_MULTITHREAD
    if (pthread_mutex_init(&frame_worker_data->stats_mutex, NULL)) {
      set_error_detail(ctx, "Failed to allocate frame_worker_data mutex");
      return VPX_CODEC_MEM_ERROR;
    }

    if (pthread_cond_init(&frame_worker_data->stats_cond, NULL)) {
      set_error_detail(ctx, "Failed to allocate frame_worker_data cond");
      return VPX_CODEC_MEM_ERROR;
    }
#endif
    // If decoding in serial mode, FrameWorker thread could create tile worker
    // thread or loopfilter thread.
    frame_worker_data->pbi->max_threads =
        (ctx->frame_parallel_decode == 0) ? ctx->cfg.threads : 0;

    frame_worker_data->pbi->inv_tile_order = ctx->invert_tile_order;
    frame_worker_data->pbi->frame_parallel_decode = ctx->frame_parallel_decode;
    frame_worker_data->pbi->common.frame_parallel_decode =
        ctx->frame_parallel_decode;
    worker->hook = (VPxWorkerHook)frame_worker_hook;
    if (!winterface->reset(worker)) {
      set_error_detail(ctx, "Frame Worker thread creation failed");
      return VPX_CODEC_MEM_ERROR;
    }
  }

  // If postprocessing was enabled by the application and a
  // configuration has not been provided, default it.
  if (!ctx->postproc_cfg_set &&
      (ctx->base.init_flags & VPX_CODEC_USE_POSTPROC))
    set_default_ppflags(&ctx->postproc_cfg);

  init_buffer_callbacks(ctx);

  return VPX_CODEC_OK;
}
예제 #6
0
static int run_command(struct Parser *parser, const char *cmd, const char *argline)
{
	union Argument args[MAX_ARGS];
	struct TableEnt *ent = NULL;
	int err = 0;
	ID id = SI_BADID;

	if (strcmp(cmd, "OpenPlugin") == 0) {
		err = parse_args("s", argline, args, MAX_ARGS);
		if (err)
			return -1;

		printf(PROMPT"%s: [%s]\n", cmd, args[0].str);
		err = SiOpenPlugin(args[0].str);
		if (err == SI_FAIL) {
			set_error_message(SiGetErrorNo());
			set_errno(PSR_ERR_PLUGINNOTFOUND);
			return -1;
		}
	}
	else if (strcmp(cmd, "RenderScene") == 0) {
		err = parse_args("s", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}

		printf(PROMPT"%s: [%s]\n", cmd, args[0].str);
		err = SiRenderScene(EntGetID(ent));
		if (err == SI_FAIL) {
			set_errno(PSR_ERR_FAILRENDER);
			return -1;
		}
	}
	else if (strcmp(cmd, "SaveFrameBuffer") == 0) {
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		err = SiSaveFrameBuffer(EntGetID(ent), args[1].str);
		if (err == SI_FAIL) {
			set_errno(PSR_ERR_FAILRENDER);
			return -1;
		}
	}
	else if (strcmp(cmd, "RunProcedure") == 0) {
		err = parse_args("s", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}

		printf(PROMPT"%s: [%s]\n", cmd, args[0].str);
		err = SiRunProcedure(EntGetID(ent));
		if (err == SI_FAIL) {
			set_errno(PSR_ERR_FAILRENDER);
			return -1;
		}
	}
	else if (strcmp(cmd, "NewTurbulence") == 0) {
		err = parse_args("s", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: [%s]\n", cmd, args[0].str);
		id = SiNewTurbulence();
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewObjectInstance") == 0) {
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		ent = TblLookup(parser->table, args[1].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		id = SiNewObjectInstance(EntGetID(ent));
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewFrameBuffer") == 0) {
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		id = SiNewFrameBuffer(args[1].str);
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewProcedure") == 0) {
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		id = SiNewProcedure(args[1].str);
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewRenderer") == 0) {
		err = parse_args("s", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: [%s]\n", cmd, args[0].str);
		id = SiNewRenderer();
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewTexture") == 0) {
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		id = SiNewTexture(args[1].str);
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewCamera") == 0) {
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: %s: %s\n", cmd, args[0].str, args[1].str);
		id = SiNewCamera(args[1].str);
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewShader") == 0) {
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		id = SiNewShader(args[1].str);
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewVolume") == 0) {
		err = parse_args("s", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: [%s]\n", cmd, args[0].str);
		id = SiNewVolume();
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewCurve") == 0) {
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		id = SiNewCurve(args[1].str);
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewLight") == 0) {
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		id = SiNewLight(args[1].str);
		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "NewMesh") == 0) {
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		if (TblLookup(parser->table, args[0].str)) {
			set_errno(PSR_ERR_NAMEEXISTS);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		id = SiNewMesh(args[1].str);

		if (id == SI_BADID) {
			set_errno(PSR_ERR_FAILNEW);
			/* TODO better error message */
#if 0
			set_error_detail(SiGetErrorMessage(SiGetErrorNo()));
			append_error_detail(": ");
			append_error_detail(args[1].str);
#endif
			return -1;
		}

		TblAdd(parser->table, args[0].str, id);
	}
	else if (strcmp(cmd, "AssignShader") == 0) {
		ID obj_id, shader_id;
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		obj_id = EntGetID(ent);

		ent = TblLookup(parser->table, args[1].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		shader_id = EntGetID(ent);

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		err = SiAssignShader(obj_id, shader_id);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else if (strcmp(cmd, "AssignTexture") == 0) {
		ID shader_id, tex_id;
		err = parse_args("sss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		shader_id = EntGetID(ent);

		ent = TblLookup(parser->table, args[2].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		tex_id = EntGetID(ent);

		printf(PROMPT"%s: [%s] [%s] [%s]\n", cmd, args[0].str, args[1].str, args[2].str);
		err = SiAssignTexture(shader_id, args[1].str, tex_id);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else if (strcmp(cmd, "AssignCamera") == 0) {
		ID renderer_id, camera_id;
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		renderer_id = EntGetID(ent);

		ent = TblLookup(parser->table, args[1].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		camera_id = EntGetID(ent);

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		err = SiAssignCamera(renderer_id, camera_id);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else if (strcmp(cmd, "AssignFrameBuffer") == 0) {
		ID renderer_id, framebuffer_id;
		err = parse_args("ss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		renderer_id = EntGetID(ent);

		ent = TblLookup(parser->table, args[1].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		framebuffer_id = EntGetID(ent);

		printf(PROMPT"%s: [%s] [%s]\n", cmd, args[0].str, args[1].str);
		err = SiAssignFrameBuffer(renderer_id, framebuffer_id);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else if (strcmp(cmd, "AssignTurbulence") == 0) {
		ID id, turbulence_id;
		err = parse_args("sss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		id = EntGetID(ent);

		ent = TblLookup(parser->table, args[2].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		turbulence_id = EntGetID(ent);

		printf(PROMPT"%s: [%s] [%s] [%s]\n", cmd, args[0].str, args[1].str, args[2].str);
		err = SiAssignTurbulence(id, args[1].str, turbulence_id);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else if (strcmp(cmd, "AssignVolume") == 0) {
		ID id, volume_id;
		err = parse_args("sss", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		id = EntGetID(ent);

		ent = TblLookup(parser->table, args[2].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}
		volume_id = EntGetID(ent);

		printf(PROMPT"%s: [%s] [%s] [%s]\n", cmd, args[0].str, args[1].str, args[2].str);
		err = SiAssignVolume(id, args[1].str, volume_id);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else if (strcmp(cmd, "SetProperty1") == 0) {
		err = parse_args("ssf", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s] [%g]\n", cmd, args[0].str, args[1].str, args[2].dbl);
		err = SiSetProperty1(EntGetID(ent), args[1].str, args[2].dbl);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else if (strcmp(cmd, "SetProperty2") == 0) {
		err = parse_args("ssff", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s] [%g] [%g]\n", cmd, args[0].str, args[1].str,
				args[2].dbl, args[3].dbl);
		err = SiSetProperty2(EntGetID(ent), args[1].str, args[2].dbl, args[3].dbl);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else if (strcmp(cmd, "SetProperty3") == 0) {
		err = parse_args("ssfff", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s] [%g] [%g] [%g]\n", cmd, args[0].str, args[1].str,
				args[2].dbl, args[3].dbl, args[4].dbl);
		/* TODO check set property error */
		err = SiSetProperty3(EntGetID(ent), args[1].str, args[2].dbl, args[3].dbl, args[4].dbl);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else if (strcmp(cmd, "SetProperty4") == 0) {
		err = parse_args("ssffff", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s] [%g] [%g] [%g] [%g]\n", cmd, args[0].str, args[1].str,
				args[2].dbl, args[3].dbl, args[4].dbl, args[5].dbl);
		err = SiSetProperty4(EntGetID(ent),
				args[1].str, args[2].dbl, args[3].dbl, args[4].dbl, args[5].dbl);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else if (strcmp(cmd, "ShowPropertyList") == 0) {
		err = parse_args("s", argline, args, MAX_ARGS);
		if (err)
			return -1;

		printf(PROMPT"%s: [%s]\n", cmd, args[0].str);
		{
			const char **prop_types = NULL;
			const char **prop_names = NULL;
			const char *type_name = args[0].str;
			int nprops = 0;
			int err = 0;
			int i = 0;

			err = SiGetPropertyList(type_name, &prop_types, &prop_names, &nprops);
			if (err) {
				printf("#   No property is available for %s\n", type_name);
				printf("#   Make sure the type name is correct.\n");
				printf("#   If you requested properties for a plugin,\n");
				printf("#   make sure it is opened before calling this command.\n");
				return -1;
			}

			printf("#   %s Properties\n", type_name);
			for (i = 0; i < nprops; i++) {
				printf("#   %15.15s : %-20.20s\n", prop_types[i], prop_names[i]);
			}
		}
	}
	/* TODO TEST SetSampleProperty3 */
	else if (strcmp(cmd, "SetSampleProperty3") == 0) {
		err = parse_args("ssffff", argline, args, MAX_ARGS);
		if (err)
			return -1;

		ent = TblLookup(parser->table, args[0].str);
		if (ent == NULL) {
			set_errno(PSR_ERR_NAMENOTFOUND);
			return -1;
		}

		printf(PROMPT"%s: [%s] [%s] [%g] [%g] [%g] [%g]\n", cmd, args[0].str, args[1].str,
				args[2].dbl, args[3].dbl, args[4].dbl, args[5].dbl);
		/* TODO check set property error */
		err = SiSetSampleProperty3(EntGetID(ent),
				args[1].str, args[2].dbl, args[3].dbl, args[4].dbl, args[5].dbl);
		if (err) {
			set_errno(PSR_ERR_FAILSETPROP);
			return -1;
		}
	}
	else {
		set_errno(PSR_ERR_UNKNOWNCMD);
		return -1;
	}

	return 0;
}