PP_Resource ppb_url_loader_get_response_info(PP_Resource loader) { struct pp_url_loader_s *ul = pp_resource_acquire(loader, PP_RESOURCE_URL_LOADER); if (!ul) { trace_error("%s, bad resource\n", __func__); return 0; } PP_Resource response_info = pp_resource_allocate(PP_RESOURCE_URL_RESPONSE_INFO, ul->instance); struct pp_url_response_info_s *ri = pp_resource_acquire(response_info, PP_RESOURCE_URL_RESPONSE_INFO); if (!ri) { trace_error("%s, resource allocation failure\n", __func__); pp_resource_release(loader); return 0; } pp_resource_ref(loader); ri->url_loader_id = loader; ri->url_loader = ul; pp_resource_release(response_info); pp_resource_release(loader); return response_info; }
void ppb_graphics2d_paint_image_data(PP_Resource graphics_2d, PP_Resource image_data, const struct PP_Point *top_left, const struct PP_Rect *src_rect) { struct pp_graphics2d_s *g2d = pp_resource_acquire(graphics_2d, PP_RESOURCE_GRAPHICS2D); if (!g2d) { trace_error("%s, bad resource\n", __func__); return; } struct g2d_paint_task_s *pt = g_slice_alloc(sizeof(*pt)); pt->type = gpt_paint_id; pp_resource_ref(image_data); pt->image_data = image_data; pt->src_is_set = !!src_rect; if (top_left) { memcpy(&pt->ofs, top_left, sizeof(*top_left)); } else { pt->ofs.x = pt->ofs.y = 0; } if (src_rect) memcpy(&pt->src, src_rect, sizeof(*src_rect)); g2d->task_list = g_list_append(g2d->task_list, pt); pp_resource_release(graphics_2d); }
int32_t ppb_flash_message_loop_run(PP_Resource flash_message_loop) { struct pp_flash_message_loop_s *fml = pp_resource_acquire(flash_message_loop, PP_RESOURCE_FLASH_MESSAGE_LOOP); if (!fml) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } PP_Resource message_loop = ppb_message_loop_get_current(); fml->running = 1; fml->message_loop = message_loop; fml->depth = ppb_message_loop_get_depth(message_loop) + 1; pp_resource_ref(flash_message_loop); // prevent destroy of running loop pp_resource_release(flash_message_loop); // launching nested loop without depth increase to prevent hang up of previously pushed tasks ppb_message_loop_run_int(message_loop, ML_NESTED); fml = pp_resource_acquire(flash_message_loop, PP_RESOURCE_FLASH_MESSAGE_LOOP); if (fml) { fml->running = 0; pp_resource_release(flash_message_loop); } pp_resource_unref(flash_message_loop); return PP_OK; }
void ppb_graphics2d_replace_contents(PP_Resource graphics_2d, PP_Resource image_data) { struct pp_graphics2d_s *g2d = pp_resource_acquire(graphics_2d, PP_RESOURCE_GRAPHICS2D); if (!g2d) { trace_error("%s, bad resource\n", __func__); return; } struct g2d_paint_task_s *pt = g_slice_alloc(sizeof(*pt)); pt->type = gpt_replace_contents; pp_resource_ref(image_data); pt->image_data = image_data; g2d->task_list = g_list_append(g2d->task_list, pt); pp_resource_release(graphics_2d); }
void ppb_core_add_ref_resource(PP_Resource resource) { pp_resource_ref(resource); }
int32_t ppb_message_loop_run_int(PP_Resource message_loop, int nested, int increase_depth) { if (this_thread_message_loop != message_loop) { trace_error("%s, not attached to current thread\n", __func__); return PP_ERROR_WRONG_THREAD; } struct pp_message_loop_s *ml = pp_resource_acquire(message_loop, PP_RESOURCE_MESSAGE_LOOP); if (!ml) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } // prevent nested loops if (!nested && ml->running) { trace_error("%s, trying to run nested loop without declaring as nested\n", __func__); pp_resource_release(message_loop); return PP_ERROR_INPROGRESS; } struct { int running; int teardown; } saved_state = { .running = ml->running, .teardown = ml->teardown, }; ml->running = 1; ml->teardown = 0; if (increase_depth) ml->depth++; int teardown = 0; int destroy_ml = 0; int depth = ml->depth; pp_resource_ref(message_loop); GAsyncQueue *async_q = ml->async_q; GQueue *int_q = ml->int_q; pp_resource_release(message_loop); while (1) { struct timespec now; struct message_loop_task_s *task = g_queue_peek_head(int_q); gint64 timeout = 1000 * 1000; if (task) { clock_gettime(CLOCK_REALTIME, &now); timeout = (task->when.tv_sec - now.tv_sec) * 1000 * 1000 + (task->when.tv_nsec - now.tv_nsec) / 1000; if (timeout <= 0) { // remove task from the queue g_queue_pop_head(int_q); // check if depth is correct if (task->depth > 0 && task->depth < depth) { // wrong, reschedule it a bit later task->when = add_ms(now, 10); g_queue_insert_sorted(int_q, task, time_compare_func, NULL); continue; } if (task->terminate) { if (depth > 1) { // exit at once, all remaining task will be processed by outer loop g_slice_free(struct message_loop_task_s, task); break; } // it's the outermost loop, we should wait for all tasks to be run ml = pp_resource_acquire(message_loop, PP_RESOURCE_MESSAGE_LOOP); if (ml) { ml->teardown = 1; teardown = 1; destroy_ml = task->should_destroy_ml; pp_resource_release(message_loop); } g_slice_free(struct message_loop_task_s, task); continue; } // run task const struct PP_CompletionCallback ccb = task->ccb; if (ccb.func) { ccb.func(ccb.user_data, task->result_to_pass); } // free task g_slice_free(struct message_loop_task_s, task); continue; // run cycle again } } else if (teardown) { // teardown, no tasks in queue left break; } task = g_async_queue_timeout_pop(async_q, timeout); if (task) g_queue_insert_sorted(int_q, task, time_compare_func, NULL); } // mark thread as non-running ml = pp_resource_acquire(message_loop, PP_RESOURCE_MESSAGE_LOOP); if (ml) { if (increase_depth) ml->depth--; ml->running = 0; if (nested) { ml->running = saved_state.running; ml->teardown = saved_state.teardown; } pp_resource_release(message_loop); } pp_resource_unref(message_loop); if (destroy_ml) pp_resource_unref(message_loop); return PP_OK; } int32_t ppb_message_loop_post_work_with_result(PP_Resource message_loop, struct PP_CompletionCallback callback, int64_t delay_ms, int32_t result_to_pass, int depth) { if (callback.func == NULL) { trace_error("%s, callback.func == NULL\n", __func__); return PP_ERROR_BADARGUMENT; } struct pp_message_loop_s *ml = pp_resource_acquire(message_loop, PP_RESOURCE_MESSAGE_LOOP); if (!ml) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } if (ml->running && ml->teardown) { // message loop is in a teardown state pp_resource_release(message_loop); trace_error("%s, quit request received, no additional work could be posted\n", __func__); return PP_ERROR_FAILED; } struct message_loop_task_s *task = g_slice_alloc0(sizeof(*task)); task->result_to_pass = result_to_pass; task->ccb = callback; task->depth = depth; // calculate absolute time callback should be run at clock_gettime(CLOCK_REALTIME, &task->when); task->when.tv_sec += delay_ms / 1000; task->when.tv_nsec += (delay_ms % 1000) * 1000 * 1000; while (task->when.tv_nsec >= 1000 * 1000 * 1000) { task->when.tv_sec += 1; task->when.tv_nsec -= 1000 * 1000 * 1000; } g_async_queue_push(ml->async_q, task); pp_resource_release(message_loop); return PP_OK; } int32_t ppb_message_loop_post_work(PP_Resource message_loop, struct PP_CompletionCallback callback, int64_t delay_ms) { return ppb_message_loop_post_work_with_result(message_loop, callback, delay_ms, PP_OK, 0); } int32_t ppb_message_loop_post_quit_depth(PP_Resource message_loop, PP_Bool should_destroy, int depth) { struct pp_message_loop_s *ml = pp_resource_acquire(message_loop, PP_RESOURCE_MESSAGE_LOOP); if (!ml) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } struct message_loop_task_s *task = g_slice_alloc0(sizeof(*task)); task->terminate = 1; task->depth = depth; task->should_destroy_ml = should_destroy; task->result_to_pass = PP_OK; clock_gettime(CLOCK_REALTIME, &task->when); // run as early as possible g_async_queue_push(ml->async_q, task); pp_resource_release(message_loop); return PP_OK; } int32_t ppb_message_loop_post_quit(PP_Resource message_loop, PP_Bool should_destroy) { int depth = ppb_message_loop_get_depth(message_loop); return ppb_message_loop_post_quit_depth(message_loop, should_destroy, depth); }
PP_Resource ppb_video_decoder_create(PP_Instance instance, PP_Resource context, PP_VideoDecoder_Profile profile) { if (!config.enable_hwdec) { trace_info_f(" hardware-accelerated decoding was disabled in config file\n"); return 0; } if (!display.va_available) { trace_info_f(" no hw acceleration available\n"); return 0; } if (!display.glXBindTexImageEXT) { trace_info_f(" no glXBindTexImageEXT available\n"); return 0; } switch (profile) { case PP_VIDEODECODER_H264PROFILE_BASELINE: case PP_VIDEODECODER_H264PROFILE_MAIN: case PP_VIDEODECODER_H264PROFILE_EXTENDED: case PP_VIDEODECODER_H264PROFILE_HIGH: // pass, there is an implementation below break; case PP_VIDEODECODER_H264PROFILE_NONE: case PP_VIDEODECODER_H264PROFILE_HIGH10PROFILE: case PP_VIDEODECODER_H264PROFILE_HIGH422PROFILE: case PP_VIDEODECODER_H264PROFILE_HIGH444PREDICTIVEPROFILE: case PP_VIDEODECODER_H264PROFILE_SCALABLEBASELINE: case PP_VIDEODECODER_H264PROFILE_SCALABLEHIGH: case PP_VIDEODECODER_H264PROFILE_STEREOHIGH: case PP_VIDEODECODER_H264PROFILE_MULTIVIEWHIGH: case PP_VIDEODECODER_VP8PROFILE_ANY: case PP_VIDEODECODER_PROFILE_UNKNOWN: default: trace_error("%s, profile %d is not supported\n", __func__, profile); return 0; } const struct PPP_VideoDecoder_Dev_0_11 *ppp_video_decoder_dev = NULL; struct pp_instance_s *pp_i = tables_get_pp_instance(instance); if (!pp_i) { trace_error("%s, bad instance\n", __func__); return 0; } ppp_video_decoder_dev = ppp_get_interface(PPP_VIDEODECODER_DEV_INTERFACE); if (!ppp_video_decoder_dev) { trace_error("%s, no viable %s\n", __func__, PPP_VIDEODECODER_DEV_INTERFACE); return 0; } if (pp_resource_get_type(context) != PP_RESOURCE_GRAPHICS3D) { trace_error("%s, bad resource\n", __func__); return 0; } PP_Resource video_decoder = pp_resource_allocate(PP_RESOURCE_VIDEO_DECODER, pp_i); struct pp_video_decoder_s *vd = pp_resource_acquire(video_decoder, PP_RESOURCE_VIDEO_DECODER); if (!vd) { trace_error("%s, resource allocation failed\n", __func__); return 0; } vd->orig_graphics3d = pp_resource_ref(context); vd->ppp_video_decoder_dev = ppp_video_decoder_dev; // create auxiliary GL context int32_t attribs[] = { PP_GRAPHICS3DATTRIB_WIDTH, 32, // dimensions can be arbitrary PP_GRAPHICS3DATTRIB_HEIGHT, 32, PP_GRAPHICS3DATTRIB_RED_SIZE, 8, PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8, PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8, PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 16, GLX_Y_INVERTED_EXT, True, GLX_BIND_TO_TEXTURE_RGBA_EXT, True, PP_GRAPHICS3DATTRIB_NONE, }; vd->graphics3d = ppb_graphics3d_create(vd->instance->id, vd->orig_graphics3d, attribs); if (!vd->graphics3d) { trace_error("%s, can't create graphics3d context\n", __func__); goto err_1; } vd->codec_id = AV_CODEC_ID_H264; // TODO: other codecs vd->avcodec = avcodec_find_decoder(vd->codec_id); if (!vd->avcodec) { trace_error("%s, can't create codec\n", __func__); goto err_1; } vd->avparser = av_parser_init(vd->codec_id); if (!vd->avparser) { trace_error("%s, can't create parser\n", __func__); goto err_1; } vd->avctx = avcodec_alloc_context3(vd->avcodec); if (!vd->avctx) { trace_error("%s, can't create codec context\n", __func__); goto err_1; } if (vd->avcodec->capabilities & CODEC_CAP_TRUNCATED) { trace_info("%s, codec have CODEC_CAP_TRUNCATED\n", __func__); vd->avctx->flags |= CODEC_FLAG_TRUNCATED; } vd->avctx->opaque = vd; vd->avctx->thread_count = 1; vd->avctx->get_format = get_format; #if AVCTX_HAVE_REFCOUNTED_BUFFERS vd->avctx->get_buffer2 = get_buffer2; vd->avctx->refcounted_frames = 1; #else vd->avctx->get_buffer = get_buffer; vd->avctx->release_buffer = release_buffer; #endif if (avcodec_open2(vd->avctx, vd->avcodec, NULL) < 0) { trace_error("%s, can't open codec\n", __func__); goto err_1; } vd->avframe = av_frame_alloc(); if (!vd->avframe) { trace_error("%s, can't alloc frame\n", __func__); goto err_1; } pp_resource_release(video_decoder); return video_decoder; err_1: ppb_video_decoder_destroy_priv(vd); pp_resource_release(video_decoder); pp_resource_expunge(video_decoder); return 0; }
int32_t ppb_video_capture_open(PP_Resource video_capture, PP_Resource device_ref, const struct PP_VideoCaptureDeviceInfo_Dev *requested_info, uint32_t buffer_count, struct PP_CompletionCallback callback) { int32_t result; struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } const char *capture_device = default_capture_device; struct PP_Var longname = ppb_device_ref_get_longname(device_ref); if (longname.type == PP_VARTYPE_STRING) capture_device = ppb_var_var_to_utf8(longname, NULL); vc->fd = v4l2_open(capture_device, O_RDWR); ppb_var_release(longname); if (vc->fd < 0) { result = PP_ERROR_NOACCESS; goto point_1; } struct v4l2_capability caps; if (v4l2_ioctl(vc->fd, VIDIOC_QUERYCAP, &caps) != 0) { result = PP_ERROR_FAILED; goto point_2; } #ifdef V4L2_CAP_DEVICE_CAPS const uint32_t device_caps = (caps.capabilities & V4L2_CAP_DEVICE_CAPS) ? caps.device_caps : caps.capabilities; #else const uint32_t device_caps = caps.capabilities; #endif // V4L2_CAP_DEVICE_CAPS if (!(device_caps & V4L2_CAP_VIDEO_CAPTURE)) { trace_error("%s, device can't capture\n", __func__); result = PP_ERROR_FAILED; goto point_2; } if (!(device_caps & V4L2_CAP_READWRITE)) { trace_error("%s, device doesn't support read/write interface\n", __func__); result = PP_ERROR_FAILED; goto point_2; } if (requested_info) { vc->width = requested_info->width; vc->height = requested_info->height; vc->fps = requested_info->frames_per_second; } else { vc->width = 640; vc->height = 480; vc->fps = 15; } struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .fmt.pix.width = vc->width, .fmt.pix.height = vc->height, .fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420, // PPAPI hardcodes format to YUV420 .fmt.pix.field = V4L2_FIELD_INTERLACED, }; if (v4l2_ioctl(vc->fd, VIDIOC_S_FMT, &fmt) != 0) { trace_error("%s, failed to set resolution\n", __func__); result = PP_ERROR_FAILED; goto point_2; } vc->width = fmt.fmt.pix.width; vc->height = fmt.fmt.pix.height; vc->buffer_size = fmt.fmt.pix.sizeimage; // buffer size in bytes vc->buffer_count = MAX(buffer_count, 5); // limit lowest number of buffers, just in case vc->buffers = calloc(sizeof(*vc->buffers), vc->buffer_count); if (!vc->buffers) { trace_error("%s, memory allocation failure (1)\n", __func__); result = PP_ERROR_FAILED; goto point_2; } vc->buffer_is_free = malloc(sizeof(*vc->buffer_is_free) * vc->buffer_count); if (!vc->buffer_is_free) { trace_error("%s, memory allocation failure (2)\n", __func__); result = PP_ERROR_FAILED; goto point_3; } for (unsigned int k = 0; k < vc->buffer_count; k ++) { vc->buffer_is_free[k] = 1; vc->buffers[k] = ppb_buffer_create(vc->instance->id, vc->buffer_size); if (vc->buffers[k] == 0) goto point_4; } struct PP_VideoCaptureDeviceInfo_Dev info = { .width = vc->width, .height = vc->height, .frames_per_second = vc->fps, }; vc->ppp_video_capture_dev->OnDeviceInfo(vc->instance->id, video_capture, &info, vc->buffer_count, vc->buffers); result = PP_OK; goto point_1; point_4: for (unsigned int k = 0; k < vc->buffer_count; k ++) ppb_core_release_resource(vc->buffers[k]); free_and_nullify(vc->buffer_is_free); point_3: free_and_nullify(vc->buffers); point_2: v4l2_close(vc->fd); vc->fd = -1; point_1: pp_resource_release(video_capture); ppb_core_call_on_main_thread2(0, callback, result, __func__); return PP_OK_COMPLETIONPENDING; } struct on_buffer_ready_param_s { PP_Instance instance; PP_Resource video_capture; uint32_t buf_idx; const struct PPP_VideoCapture_Dev_0_1 *ppp_video_capture_dev; }; static void on_buffer_ready_comt(void *user_data, int32_t result) { struct on_buffer_ready_param_s *p = user_data; struct pp_instance_s *pp_i = tables_get_pp_instance(p->instance); if (!pp_i) return; p->ppp_video_capture_dev->OnBufferReady(p->instance, p->video_capture, p->buf_idx); g_slice_free1(sizeof(*p), p); } static void * video_capture_thread(void *param) { struct pp_video_capture_s *vc = param; PP_Resource video_capture = vc->self_id; PP_Instance instance = vc->instance->id; const int fd = vc->fd; const size_t buffer_size = vc->buffer_size; vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) goto gone; while (!vc->terminate_thread) { // find free buffer uint32_t buf_idx = (uint32_t)-1; for (uint32_t k = 0; k < vc->buffer_count; k ++) { if (vc->buffer_is_free[k]) { buf_idx = k; vc->buffer_is_free[k] = 0; break; } } if (buf_idx == (uint32_t)-1) { // all buffers are busy, wait for some to free, with resource unlocked pp_resource_release(video_capture); usleep(10); vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) goto gone; continue; } PP_Resource buffer = vc->buffers[buf_idx]; pp_resource_release(video_capture); // wait on v4l2_read() with resource unlocked void *ptr = ppb_buffer_map(buffer); RETRY_ON_EINTR(v4l2_read(fd, ptr, buffer_size)); ppb_buffer_unmap(buffer); vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) goto gone; struct on_buffer_ready_param_s *p = g_slice_alloc(sizeof(*p)); p->instance = instance; p->video_capture = video_capture; p->buf_idx = buf_idx; p->ppp_video_capture_dev = vc->ppp_video_capture_dev; ppb_core_call_on_main_thread2(0, PP_MakeCCB(on_buffer_ready_comt, p), PP_OK, __func__); } pp_resource_release(video_capture); return NULL; gone: trace_error("%s, resource gone\n", __func__); return NULL; } int32_t ppb_video_capture_start_capture(PP_Resource video_capture) { struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } if (vc->thread_started) goto done; if (vc->fd < 0) { trace_error("%s, device is closed\n", __func__); pp_resource_release(video_capture); return PP_ERROR_FAILED; } vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture, PP_VIDEO_CAPTURE_STATUS_STARTING); pp_resource_ref(video_capture); // prevents freeing while thread is still running pthread_create(&vc->thread, NULL, video_capture_thread, vc); vc->thread_started = 1; vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture, PP_VIDEO_CAPTURE_STATUS_STARTED); done: pp_resource_release(video_capture); return PP_OK; } int32_t ppb_video_capture_reuse_buffer(PP_Resource video_capture, uint32_t buffer) { struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } if (buffer < vc->buffer_count) vc->buffer_is_free[buffer] = 1; pp_resource_release(video_capture); return PP_OK; } int32_t ppb_video_capture_stop_capture(PP_Resource video_capture) { struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, bad resource\n", __func__); return PP_ERROR_BADRESOURCE; } if (!vc->thread_started) goto done; vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture, PP_VIDEO_CAPTURE_STATUS_STOPPING); vc->terminate_thread = 1; pthread_t thread = vc->thread; pp_resource_release(video_capture); pthread_join(thread, NULL); vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, resource gone\n", __func__); return PP_ERROR_BADRESOURCE; } vc->thread_started = 0; vc->terminate_thread = 0; vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture, PP_VIDEO_CAPTURE_STATUS_STOPPED); pp_resource_unref(video_capture); // remove reference made in start_capture() done: pp_resource_release(video_capture); return PP_OK; } void ppb_video_capture_close(PP_Resource video_capture) { ppb_video_capture_stop_capture(video_capture); struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE); if (!vc) { trace_error("%s, bad resource\n", __func__); return; } ppb_video_capture_destroy(vc); pp_resource_release(video_capture); return; } // trace wrappers TRACE_WRAPPER PP_Resource trace_ppb_video_capture_create(PP_Instance instance) { trace_info("[PPB] {full} %s instance=%d\n", __func__+6, instance); return ppb_video_capture_create(instance); }