static void wait_worker_and_cache_frame(vpx_codec_alg_priv_t *ctx) { YV12_BUFFER_CONFIG sd; vp9_ppflags_t flags = {0, 0, 0}; const VPxWorkerInterface *const winterface = vpx_get_worker_interface(); VPxWorker *const worker = &ctx->frame_workers[ctx->next_output_worker_id]; FrameWorkerData *const frame_worker_data = (FrameWorkerData *)worker->data1; ctx->next_output_worker_id = (ctx->next_output_worker_id + 1) % ctx->num_frame_workers; // TODO(hkuang): Add worker error handling here. winterface->sync(worker); frame_worker_data->received_frame = 0; ++ctx->available_threads; check_resync(ctx, frame_worker_data->pbi); if (vp9_get_raw_frame(frame_worker_data->pbi, &sd, &flags) == 0) { VP9_COMMON *const cm = &frame_worker_data->pbi->common; RefCntBuffer *const frame_bufs = cm->buffer_pool->frame_bufs; ctx->frame_cache[ctx->frame_cache_write].fb_idx = cm->new_fb_idx; yuvconfig2image(&ctx->frame_cache[ctx->frame_cache_write].img, &sd, frame_worker_data->user_priv); ctx->frame_cache[ctx->frame_cache_write].img.fb_priv = frame_bufs[cm->new_fb_idx].raw_frame_buffer.priv; ctx->frame_cache_write = (ctx->frame_cache_write + 1) % FRAME_CACHE_SIZE; ++ctx->num_cache_frames; } }
enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts, long now) { long delay = now - (ts - jb->info.resync_offset); jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb, data, ms, ts, now); if (check_resync(jb, ts, now, ms, type, &delay)) { return JB_DROP; } if (type == JB_TYPE_VOICE) { /* presently, I'm only adding VOICE frames to history and drift calculations; mostly because with the * IAX integrations, I'm sending retransmitted control frames with their awkward timestamps through */ history_put(jb, ts, now, ms, delay); } jb->info.frames_in++; /* if put into head of queue, caller needs to reschedule */ if (queue_put(jb,data,type,ms,ts)) { return JB_SCHED; } return JB_OK; }
static vpx_image_t *decoder_get_frame(vpx_codec_alg_priv_t *ctx, vpx_codec_iter_t *iter) { vpx_image_t *img = NULL; // Only return frame when all the cpu are busy or // application fluhsed the decoder in frame parallel decode. if (ctx->frame_parallel_decode && ctx->available_threads > 0 && !ctx->flushed) { return NULL; } // Output the frames in the cache first. if (ctx->num_cache_frames > 0) { release_last_output_frame(ctx); ctx->last_show_frame = ctx->frame_cache[ctx->frame_cache_read].fb_idx; if (ctx->need_resync) return NULL; img = &ctx->frame_cache[ctx->frame_cache_read].img; ctx->frame_cache_read = (ctx->frame_cache_read + 1) % FRAME_CACHE_SIZE; --ctx->num_cache_frames; return img; } // iter acts as a flip flop, so an image is only returned on the first // call to get_frame. if (*iter == NULL && ctx->frame_workers != NULL) { do { YV12_BUFFER_CONFIG sd; vp9_ppflags_t flags = {0, 0, 0}; const VPxWorkerInterface *const winterface = vpx_get_worker_interface(); VPxWorker *const worker = &ctx->frame_workers[ctx->next_output_worker_id]; FrameWorkerData *const frame_worker_data = (FrameWorkerData *)worker->data1; ctx->next_output_worker_id = (ctx->next_output_worker_id + 1) % ctx->num_frame_workers; if (ctx->base.init_flags & VPX_CODEC_USE_POSTPROC) set_ppflags(ctx, &flags); // Wait for the frame from worker thread. if (winterface->sync(worker)) { // Check if worker has received any frames. if (frame_worker_data->received_frame == 1) { ++ctx->available_threads; frame_worker_data->received_frame = 0; check_resync(ctx, frame_worker_data->pbi); } if (vp9_get_raw_frame(frame_worker_data->pbi, &sd, &flags) == 0) { VP9_COMMON *const cm = &frame_worker_data->pbi->common; RefCntBuffer *const frame_bufs = cm->buffer_pool->frame_bufs; release_last_output_frame(ctx); ctx->last_show_frame = frame_worker_data->pbi->common.new_fb_idx; if (ctx->need_resync) return NULL; yuvconfig2image(&ctx->img, &sd, frame_worker_data->user_priv); ctx->img.fb_priv = frame_bufs[cm->new_fb_idx].raw_frame_buffer.priv; img = &ctx->img; return img; } } else { // Decoding failed. Release the worker thread. frame_worker_data->received_frame = 0; ++ctx->available_threads; ctx->need_resync = 1; if (ctx->flushed != 1) return NULL; } } while (ctx->next_output_worker_id != ctx->next_submit_worker_id); } return NULL; }
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; }