void vp10_frameworker_copy_context(VPxWorker *const dst_worker, VPxWorker *const src_worker) { #if CONFIG_MULTITHREAD FrameWorkerData *const src_worker_data = (FrameWorkerData *)src_worker->data1; FrameWorkerData *const dst_worker_data = (FrameWorkerData *)dst_worker->data1; VP10_COMMON *const src_cm = &src_worker_data->pbi->common; VP10_COMMON *const dst_cm = &dst_worker_data->pbi->common; int i; // Wait until source frame's context is ready. vp10_frameworker_lock_stats(src_worker); while (!src_worker_data->frame_context_ready) { pthread_cond_wait(&src_worker_data->stats_cond, &src_worker_data->stats_mutex); } dst_cm->last_frame_seg_map = src_cm->seg.enabled ? src_cm->current_frame_seg_map : src_cm->last_frame_seg_map; dst_worker_data->pbi->need_resync = src_worker_data->pbi->need_resync; vp10_frameworker_unlock_stats(src_worker); dst_cm->bit_depth = src_cm->bit_depth; #if CONFIG_VPX_HIGHBITDEPTH dst_cm->use_highbitdepth = src_cm->use_highbitdepth; #endif dst_cm->prev_frame = src_cm->show_existing_frame ? src_cm->prev_frame : src_cm->cur_frame; dst_cm->last_width = !src_cm->show_existing_frame ? src_cm->width : src_cm->last_width; dst_cm->last_height = !src_cm->show_existing_frame ? src_cm->height : src_cm->last_height; dst_cm->subsampling_x = src_cm->subsampling_x; dst_cm->subsampling_y = src_cm->subsampling_y; dst_cm->frame_type = src_cm->frame_type; dst_cm->last_show_frame = !src_cm->show_existing_frame ? src_cm->show_frame : src_cm->last_show_frame; for (i = 0; i < REF_FRAMES; ++i) dst_cm->ref_frame_map[i] = src_cm->next_ref_frame_map[i]; memcpy(dst_cm->lf_info.lfthr, src_cm->lf_info.lfthr, (MAX_LOOP_FILTER + 1) * sizeof(loop_filter_thresh)); dst_cm->lf.last_sharpness_level = src_cm->lf.sharpness_level; dst_cm->lf.filter_level = src_cm->lf.filter_level; memcpy(dst_cm->lf.ref_deltas, src_cm->lf.ref_deltas, MAX_REF_FRAMES); memcpy(dst_cm->lf.mode_deltas, src_cm->lf.mode_deltas, MAX_MODE_LF_DELTAS); dst_cm->seg = src_cm->seg; memcpy(dst_cm->frame_contexts, src_cm->frame_contexts, FRAME_CONTEXTS * sizeof(dst_cm->frame_contexts[0])); #else (void)dst_worker; (void)src_worker; #endif // CONFIG_MULTITHREAD }
// TODO(hkuang): Remove worker parameter as it is only used in debug code. void vp10_frameworker_wait(VPxWorker *const worker, RefCntBuffer *const ref_buf, int row) { #if CONFIG_MULTITHREAD if (!ref_buf) return; #ifndef BUILDING_WITH_TSAN // The following line of code will get harmless tsan error but it is the key // to get best performance. if (ref_buf->row >= row && ref_buf->buf.corrupted != 1) return; #endif { // Find the worker thread that owns the reference frame. If the reference // frame has been fully decoded, it may not have owner. VPxWorker *const ref_worker = ref_buf->frame_worker_owner; FrameWorkerData *const ref_worker_data = (FrameWorkerData *)ref_worker->data1; const VP10Decoder *const pbi = ref_worker_data->pbi; #ifdef DEBUG_THREAD { FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1; printf("%d %p worker is waiting for %d %p worker (%d) ref %d \r\n", worker_data->worker_id, worker, ref_worker_data->worker_id, ref_buf->frame_worker_owner, row, ref_buf->row); } #endif vp10_frameworker_lock_stats(ref_worker); while (ref_buf->row < row && pbi->cur_buf == ref_buf && ref_buf->buf.corrupted != 1) { pthread_cond_wait(&ref_worker_data->stats_cond, &ref_worker_data->stats_mutex); } if (ref_buf->buf.corrupted == 1) { FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1; vp10_frameworker_unlock_stats(ref_worker); vpx_internal_error(&worker_data->pbi->common.error, VPX_CODEC_CORRUPT_FRAME, "Worker %p failed to decode frame", worker); } vp10_frameworker_unlock_stats(ref_worker); } #else (void)worker; (void)ref_buf; (void)row; (void)ref_buf; #endif // CONFIG_MULTITHREAD }
void vp10_frameworker_broadcast(RefCntBuffer *const buf, int row) { #if CONFIG_MULTITHREAD VPxWorker *worker = buf->frame_worker_owner; #ifdef DEBUG_THREAD { FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1; printf("%d %p worker decode to (%d) \r\n", worker_data->worker_id, buf->frame_worker_owner, row); } #endif vp10_frameworker_lock_stats(worker); buf->row = row; vp10_frameworker_signal_stats(worker); vp10_frameworker_unlock_stats(worker); #else (void)buf; (void)row; #endif // CONFIG_MULTITHREAD }
static int frame_worker_hook(void *arg1, void *arg2) { FrameWorkerData *const frame_worker_data = (FrameWorkerData *)arg1; const uint8_t *data = frame_worker_data->data; (void)arg2; frame_worker_data->result = vp10_receive_compressed_data(frame_worker_data->pbi, frame_worker_data->data_size, &data); frame_worker_data->data_end = data; if (frame_worker_data->pbi->frame_parallel_decode) { // In frame parallel decoding, a worker thread must successfully decode all // the compressed data. if (frame_worker_data->result != 0 || frame_worker_data->data + frame_worker_data->data_size - 1 > data) { VPxWorker *const worker = frame_worker_data->pbi->frame_worker_owner; BufferPool *const pool = frame_worker_data->pbi->common.buffer_pool; // Signal all the other threads that are waiting for this frame. vp10_frameworker_lock_stats(worker); frame_worker_data->frame_context_ready = 1; lock_buffer_pool(pool); frame_worker_data->pbi->cur_buf->buf.corrupted = 1; unlock_buffer_pool(pool); frame_worker_data->pbi->need_resync = 1; vp10_frameworker_signal_stats(worker); vp10_frameworker_unlock_stats(worker); return 0; } } else if (frame_worker_data->result != 0) { // Check decode result in serial decode. frame_worker_data->pbi->cur_buf->buf.corrupted = 1; frame_worker_data->pbi->need_resync = 1; } return !frame_worker_data->result; }
int vp10_receive_compressed_data(VP10Decoder *pbi, size_t size, const uint8_t **psource) { VP10_COMMON *volatile const cm = &pbi->common; BufferPool *volatile const pool = cm->buffer_pool; RefCntBuffer *volatile const frame_bufs = cm->buffer_pool->frame_bufs; const uint8_t *source = *psource; int retcode = 0; cm->error.error_code = VPX_CODEC_OK; if (size == 0) { // This is used to signal that we are missing frames. // We do not know if the missing frame(s) was supposed to update // any of the reference buffers, but we act conservative and // mark only the last buffer as corrupted. // // TODO(jkoleszar): Error concealment is undefined and non-normative // at this point, but if it becomes so, [0] may not always be the correct // thing to do here. if (cm->frame_refs[0].idx > 0) { assert(cm->frame_refs[0].buf != NULL); cm->frame_refs[0].buf->corrupted = 1; } } pbi->ready_for_new_data = 0; // Check if the previous frame was a frame without any references to it. // Release frame buffer if not decoding in frame parallel mode. if (!pbi->frame_parallel_decode && cm->new_fb_idx >= 0 && frame_bufs[cm->new_fb_idx].ref_count == 0) pool->release_fb_cb(pool->cb_priv, &frame_bufs[cm->new_fb_idx].raw_frame_buffer); // Find a free frame buffer. Return error if can not find any. cm->new_fb_idx = get_free_fb(cm); if (cm->new_fb_idx == INVALID_IDX) return VPX_CODEC_MEM_ERROR; // Assign a MV array to the frame buffer. cm->cur_frame = &pool->frame_bufs[cm->new_fb_idx]; pbi->hold_ref_buf = 0; if (pbi->frame_parallel_decode) { VPxWorker *const worker = pbi->frame_worker_owner; vp10_frameworker_lock_stats(worker); frame_bufs[cm->new_fb_idx].frame_worker_owner = worker; // Reset decoding progress. pbi->cur_buf = &frame_bufs[cm->new_fb_idx]; pbi->cur_buf->row = -1; pbi->cur_buf->col = -1; vp10_frameworker_unlock_stats(worker); } else { pbi->cur_buf = &frame_bufs[cm->new_fb_idx]; } if (setjmp(cm->error.jmp)) { const VPxWorkerInterface *const winterface = vpx_get_worker_interface(); int i; cm->error.setjmp = 0; pbi->ready_for_new_data = 1; // Synchronize all threads immediately as a subsequent decode call may // cause a resize invalidating some allocations. winterface->sync(&pbi->lf_worker); for (i = 0; i < pbi->num_tile_workers; ++i) { winterface->sync(&pbi->tile_workers[i]); } lock_buffer_pool(pool); // Release all the reference buffers if worker thread is holding them. if (pbi->hold_ref_buf == 1) { int ref_index = 0, mask; for (mask = pbi->refresh_frame_flags; mask; mask >>= 1) { const int old_idx = cm->ref_frame_map[ref_index]; // Current thread releases the holding of reference frame. decrease_ref_count(old_idx, frame_bufs, pool); // Release the reference frame in reference map. if ((mask & 1) && old_idx >= 0) { decrease_ref_count(old_idx, frame_bufs, pool); } ++ref_index; } // Current thread releases the holding of reference frame. for (; ref_index < REF_FRAMES && !cm->show_existing_frame; ++ref_index) { const int old_idx = cm->ref_frame_map[ref_index]; decrease_ref_count(old_idx, frame_bufs, pool); } pbi->hold_ref_buf = 0; } // Release current frame. decrease_ref_count(cm->new_fb_idx, frame_bufs, pool); unlock_buffer_pool(pool); vpx_clear_system_state(); return -1; }