static GstBuffer * create_output_buffer (GstMsdkVPP * thiz) { GstBuffer *outbuf; GstFlowReturn ret; GstBufferPool *pool = thiz->srcpad_buffer_pool; g_return_val_if_fail (pool != NULL, NULL); if (!gst_buffer_pool_is_active (pool) && !gst_buffer_pool_set_active (pool, TRUE)) goto error_activate_pool; outbuf = NULL; ret = gst_buffer_pool_acquire_buffer (pool, &outbuf, NULL); if (ret != GST_FLOW_OK || !outbuf) goto error_create_buffer; return outbuf; /* ERRORS */ error_activate_pool: { GST_ERROR_OBJECT (thiz, "failed to activate output video buffer pool"); return NULL; } error_create_buffer: { GST_ERROR_OBJECT (thiz, "failed to create output video buffer"); return NULL; } }
static MsdkSurface * get_surface_from_pool (GstMsdkVPP * thiz, GstBufferPool * pool, GstBufferPoolAcquireParams * params) { GstBuffer *new_buffer; mfxFrameSurface1 *new_surface; MsdkSurface *msdk_surface; if (!gst_buffer_pool_is_active (pool) && !gst_buffer_pool_set_active (pool, TRUE)) { GST_ERROR_OBJECT (pool, "failed to activate buffer pool"); return NULL; } if (gst_buffer_pool_acquire_buffer (pool, &new_buffer, params) != GST_FLOW_OK) { GST_ERROR_OBJECT (pool, "failed to acquire a buffer from pool"); return NULL; } if (gst_msdk_is_msdk_buffer (new_buffer)) new_surface = gst_msdk_get_surface_from_buffer (new_buffer); else { GST_ERROR_OBJECT (pool, "the acquired memory is not MSDK memory"); return NULL; } msdk_surface = g_slice_new0 (MsdkSurface); msdk_surface->surface = new_surface; msdk_surface->buf = new_buffer; return msdk_surface; }
void gst_v4l2_buffer_pool_set_other_pool (GstV4l2BufferPool * pool, GstBufferPool * other_pool) { g_return_if_fail (!gst_buffer_pool_is_active (GST_BUFFER_POOL (pool))); if (pool->other_pool) gst_object_unref (pool->other_pool); pool->other_pool = gst_object_ref (other_pool); }
static gboolean gst_v4l2_video_dec_negotiate (GstVideoDecoder * decoder) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); /* We don't allow renegotiation without carefull disabling the pool */ if (self->v4l2capture->pool && gst_buffer_pool_is_active (GST_BUFFER_POOL (self->v4l2capture->pool))) return TRUE; return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder); }
static GstFlowReturn gst_imx_compositor_get_output_buffer(GstImxBPVideoAggregator *videoaggregator, GstBuffer **outbuffer) { GstImxCompositor *compositor = GST_IMX_COMPOSITOR(videoaggregator); GstBufferPool *pool = compositor->dma_bufferpool; /* Return a DMA buffer from the pool. The output buffers produced by * the video aggregator base class will use this function to allocate. */ if (!gst_buffer_pool_is_active(pool)) gst_buffer_pool_set_active(pool, TRUE); return gst_buffer_pool_acquire_buffer(pool, outbuffer, NULL); }
static MsdkSurface * get_surface (GstMsdkDec * thiz, GstBuffer * buffer) { MsdkSurface *i; i = g_slice_new0 (MsdkSurface); if (gst_msdk_is_msdk_buffer (buffer)) { i->surface = gst_msdk_get_surface_from_buffer (buffer); i->buf = buffer; } else { /* Confirm to activate the side pool */ if (!gst_buffer_pool_is_active (thiz->pool) && !gst_buffer_pool_set_active (thiz->pool, TRUE)) { g_slice_free (MsdkSurface, i); return NULL; } if (!gst_video_frame_map (&i->copy, &thiz->non_msdk_pool_info, buffer, GST_MAP_WRITE)) goto failed_unref_buffer; if (gst_buffer_pool_acquire_buffer (thiz->pool, &buffer, NULL) != GST_FLOW_OK) goto failed_unmap_copy; i->surface = gst_msdk_get_surface_from_buffer (buffer); i->buf = buffer; if (!gst_video_frame_map (&i->data, &thiz->output_info, buffer, GST_MAP_READWRITE)) goto failed_unref_buffer2; } thiz->decoded_msdk_surfaces = g_list_append (thiz->decoded_msdk_surfaces, i); return i; failed_unref_buffer2: gst_buffer_unref (buffer); buffer = i->data.buffer; failed_unmap_copy: gst_video_frame_unmap (&i->copy); failed_unref_buffer: gst_buffer_unref (buffer); g_slice_free (MsdkSurface, i); GST_ERROR_OBJECT (thiz, "failed to handle buffer"); return NULL; }
static GstFlowReturn gst_v4l2_transform_prepare_output_buffer (GstBaseTransform * trans, GstBuffer * inbuf, GstBuffer ** outbuf) { GstV4l2Transform *self = GST_V4L2_TRANSFORM (trans); GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool); GstFlowReturn ret = GST_FLOW_OK; GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_CLASS (parent_class); if (gst_base_transform_is_passthrough (trans)) { GST_DEBUG_OBJECT (self, "Passthrough, no need to do anything"); *outbuf = inbuf; goto beach; } /* Ensure input internal pool is active */ if (!gst_buffer_pool_is_active (pool)) { GstStructure *config = gst_buffer_pool_get_config (pool); gst_buffer_pool_config_set_params (config, self->incaps, self->v4l2output->info.size, 2, 2); /* There is no reason to refuse this config */ if (!gst_buffer_pool_set_config (pool, config)) goto activate_failed; if (!gst_buffer_pool_set_active (pool, TRUE)) goto activate_failed; } GST_DEBUG_OBJECT (self, "Queue input buffer"); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (pool), &inbuf); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto beach; do { pool = gst_base_transform_get_buffer_pool (trans); if (!gst_buffer_pool_set_active (pool, TRUE)) goto activate_failed; GST_DEBUG_OBJECT (self, "Dequeue output buffer"); ret = gst_buffer_pool_acquire_buffer (pool, outbuf, NULL); g_object_unref (pool); if (ret != GST_FLOW_OK) goto alloc_failed; pool = self->v4l2capture->pool; ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (pool), outbuf); } while (ret == GST_V4L2_FLOW_CORRUPTED_BUFFER); if (ret != GST_FLOW_OK) { gst_buffer_unref (*outbuf); *outbuf = NULL; } if (bclass->copy_metadata) if (!bclass->copy_metadata (trans, inbuf, *outbuf)) { /* something failed, post a warning */ GST_ELEMENT_WARNING (self, STREAM, NOT_IMPLEMENTED, ("could not copy metadata"), (NULL)); } beach: return ret; activate_failed: GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, ("failed to activate bufferpool"), ("failed to activate bufferpool")); g_object_unref (pool); return GST_FLOW_ERROR; alloc_failed: GST_DEBUG_OBJECT (self, "could not allocate buffer from pool"); return ret; }
/** * gst_v4l2_buffer_pool_process: * @bpool: a #GstBufferPool * @buf: a #GstBuffer * * Process @buf in @bpool. For capture devices, this functions fills @buf with * data from the device. For output devices, this functions send the contents of * @buf to the device for playback. * * Returns: %GST_FLOW_OK on success. */ GstFlowReturn gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer * buf) { GstFlowReturn ret = GST_FLOW_OK; GstBufferPool *bpool = GST_BUFFER_POOL_CAST (pool); GstV4l2Object *obj = pool->obj; GST_DEBUG_OBJECT (pool, "process buffer %p", buf); switch (obj->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: /* capture */ switch (obj->mode) { case GST_V4L2_IO_RW: /* capture into the buffer */ ret = gst_v4l2_do_read (pool, buf); break; case GST_V4L2_IO_MMAP: { GstBuffer *tmp; if (buf->pool == bpool) /* nothing, data was inside the buffer when we did _acquire() */ goto done; /* buffer not from our pool, grab a frame and copy it into the target */ if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp)) != GST_FLOW_OK) goto done; if (!gst_v4l2_object_copy (obj, buf, tmp)) goto copy_failed; /* an queue the buffer again after the copy */ if ((ret = gst_v4l2_buffer_pool_qbuf (pool, tmp)) != GST_FLOW_OK) goto done; break; } case GST_V4L2_IO_USERPTR: default: g_assert_not_reached (); break; } break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: /* playback */ switch (obj->mode) { case GST_V4L2_IO_RW: /* FIXME, do write() */ GST_WARNING_OBJECT (pool, "implement write()"); break; case GST_V4L2_IO_MMAP: { GstBuffer *to_queue; if (buf->pool == bpool) { /* nothing, we can queue directly */ to_queue = buf; GST_LOG_OBJECT (pool, "processing buffer from our pool"); } else { GST_LOG_OBJECT (pool, "alloc buffer from our pool"); if (!gst_buffer_pool_is_active (bpool)) { GstStructure *config; /* this pool was not activated, configure and activate */ GST_DEBUG_OBJECT (pool, "activating pool"); config = gst_buffer_pool_get_config (bpool); gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); gst_buffer_pool_set_config (bpool, config); if (!gst_buffer_pool_set_active (bpool, TRUE)) goto activate_failed; } /* this can block if all buffers are outstanding which would be * strange because we would expect the upstream element to have * allocated them and returned to us.. */ ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, &to_queue, NULL); if (ret != GST_FLOW_OK) goto acquire_failed; /* copy into it and queue */ if (!gst_v4l2_object_copy (obj, to_queue, buf)) goto copy_failed; } if ((ret = gst_v4l2_buffer_pool_qbuf (pool, to_queue)) != GST_FLOW_OK) goto done; /* if we are not streaming yet (this is the first buffer, start * streaming now */ if (!pool->streaming) if (!start_streaming (pool)) goto start_failed; if (pool->num_queued == pool->num_allocated) { /* all buffers are queued, try to dequeue one and release it back * into the pool so that _acquire can get to it again. */ ret = gst_v4l2_buffer_pool_dqbuf (pool, &to_queue); if (ret != GST_FLOW_OK) goto done; /* release the rendered buffer back into the pool. This wakes up any * thread waiting for a buffer in _acquire() */ gst_v4l2_buffer_pool_release_buffer (bpool, to_queue); } break; } case GST_V4L2_IO_USERPTR: default: g_assert_not_reached (); break; } break; default: g_assert_not_reached (); break; } done: return ret; /* ERRORS */ activate_failed: { GST_ERROR_OBJECT (obj->element, "failed to activate pool"); return GST_FLOW_ERROR; } acquire_failed: { GST_WARNING_OBJECT (obj->element, "failed to acquire a buffer: %s", gst_flow_get_name (ret)); return ret; } copy_failed: { GST_ERROR_OBJECT (obj->element, "failed to copy data"); return GST_FLOW_ERROR; } start_failed: { GST_ERROR_OBJECT (obj->element, "failed to start streaming"); return GST_FLOW_ERROR; } }
static GstFlowReturn gst_imx_vpu_base_enc_handle_frame(GstVideoEncoder *encoder, GstVideoCodecFrame *frame) { VpuEncRetCode enc_ret; VpuEncEncParam enc_enc_param; GstImxPhysMemMeta *phys_mem_meta; GstImxVpuBaseEncClass *klass; GstImxVpuBaseEnc *vpu_base_enc; VpuFrameBuffer input_framebuf; GstBuffer *input_buffer; gint src_stride; vpu_base_enc = GST_IMX_VPU_BASE_ENC(encoder); klass = GST_IMX_VPU_BASE_ENC_CLASS(G_OBJECT_GET_CLASS(vpu_base_enc)); g_assert(klass->set_frame_enc_params != NULL); memset(&enc_enc_param, 0, sizeof(enc_enc_param)); memset(&input_framebuf, 0, sizeof(input_framebuf)); phys_mem_meta = GST_IMX_PHYS_MEM_META_GET(frame->input_buffer); /* If the incoming frame's buffer is not using physically contiguous memory, * it needs to be copied to the internal input buffer, otherwise the VPU * encoder cannot read the frame */ if (phys_mem_meta == NULL) { /* No physical memory metadata found -> buffer is not physically contiguous */ GstVideoFrame temp_input_video_frame, temp_incoming_video_frame; GST_LOG_OBJECT(vpu_base_enc, "input buffer not physically contiguous - frame copy is necessary"); if (vpu_base_enc->internal_input_buffer == NULL) { /* The internal input buffer is the temp input frame's DMA memory. * If it does not exist yet, it needs to be created here. The temp input * frame is then mapped. */ GstFlowReturn flow_ret; if (vpu_base_enc->internal_bufferpool == NULL) { /* Internal bufferpool does not exist yet - create it now, * so that it can in turn create the internal input buffer */ GstStructure *config; GstCaps *caps; GstAllocator *allocator; GST_DEBUG_OBJECT(vpu_base_enc, "creating internal bufferpool"); caps = gst_video_info_to_caps(&(vpu_base_enc->video_info)); vpu_base_enc->internal_bufferpool = gst_imx_phys_mem_buffer_pool_new(FALSE); allocator = gst_imx_vpu_enc_allocator_obtain(); config = gst_buffer_pool_get_config(vpu_base_enc->internal_bufferpool); gst_buffer_pool_config_set_params(config, caps, vpu_base_enc->video_info.size, 2, 0); gst_buffer_pool_config_set_allocator(config, allocator, NULL); gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_IMX_PHYS_MEM); gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META); gst_buffer_pool_set_config(vpu_base_enc->internal_bufferpool, config); gst_caps_unref(caps); if (vpu_base_enc->internal_bufferpool == NULL) { GST_ERROR_OBJECT(vpu_base_enc, "failed to create internal bufferpool"); return GST_FLOW_ERROR; } } /* Future versions of this code may propose the internal bufferpool upstream; * hence the is_active check */ if (!gst_buffer_pool_is_active(vpu_base_enc->internal_bufferpool)) gst_buffer_pool_set_active(vpu_base_enc->internal_bufferpool, TRUE); /* Create the internal input buffer */ flow_ret = gst_buffer_pool_acquire_buffer(vpu_base_enc->internal_bufferpool, &(vpu_base_enc->internal_input_buffer), NULL); if (flow_ret != GST_FLOW_OK) { GST_ERROR_OBJECT(vpu_base_enc, "error acquiring input frame buffer: %s", gst_pad_mode_get_name(flow_ret)); return flow_ret; } } /* The internal input buffer exists at this point. Since the incoming frame * is not stored in physical memory, copy its pixels to the internal * input buffer, so the encoder can read them. */ gst_video_frame_map(&temp_incoming_video_frame, &(vpu_base_enc->video_info), frame->input_buffer, GST_MAP_READ); gst_video_frame_map(&temp_input_video_frame, &(vpu_base_enc->video_info), vpu_base_enc->internal_input_buffer, GST_MAP_WRITE); gst_video_frame_copy(&temp_input_video_frame, &temp_incoming_video_frame); gst_video_frame_unmap(&temp_incoming_video_frame); gst_video_frame_unmap(&temp_input_video_frame); /* Set the internal input buffer as the encoder's input */ input_buffer = vpu_base_enc->internal_input_buffer; /* And use the internal input buffer's physical memory metadata */ phys_mem_meta = GST_IMX_PHYS_MEM_META_GET(vpu_base_enc->internal_input_buffer); } else { /* Physical memory metadata found -> buffer is physically contiguous * It can be used directly as input for the VPU encoder */ input_buffer = frame->input_buffer; } /* Set up physical addresses for the input framebuffer */ { gsize *plane_offsets; gint *plane_strides; GstVideoMeta *video_meta; unsigned char *phys_ptr; /* Try to use plane offset and stride information from the video * metadata if present, since these can be more accurate than * the information from the video info */ video_meta = gst_buffer_get_video_meta(input_buffer); if (video_meta != NULL) { plane_offsets = video_meta->offset; plane_strides = video_meta->stride; } else { plane_offsets = vpu_base_enc->video_info.offset; plane_strides = vpu_base_enc->video_info.stride; } phys_ptr = (unsigned char*)(phys_mem_meta->phys_addr); input_framebuf.pbufY = phys_ptr; input_framebuf.pbufCb = phys_ptr + plane_offsets[1]; input_framebuf.pbufCr = phys_ptr + plane_offsets[2]; input_framebuf.pbufMvCol = NULL; /* not used by the VPU encoder */ input_framebuf.nStrideY = plane_strides[0]; input_framebuf.nStrideC = plane_strides[1]; /* this is needed for framebuffers registration below */ src_stride = plane_strides[0]; GST_TRACE_OBJECT(vpu_base_enc, "width: %d height: %d stride 0: %d stride 1: %d offset 0: %d offset 1: %d offset 2: %d", GST_VIDEO_INFO_WIDTH(&(vpu_base_enc->video_info)), GST_VIDEO_INFO_HEIGHT(&(vpu_base_enc->video_info)), plane_strides[0], plane_strides[1], plane_offsets[0], plane_offsets[1], plane_offsets[2]); } /* Create framebuffers structure (if not already present) */ if (vpu_base_enc->framebuffers == NULL) { GstImxVpuFramebufferParams fbparams; gst_imx_vpu_framebuffers_enc_init_info_to_params(&(vpu_base_enc->init_info), &fbparams); fbparams.pic_width = vpu_base_enc->open_param.nPicWidth; fbparams.pic_height = vpu_base_enc->open_param.nPicHeight; vpu_base_enc->framebuffers = gst_imx_vpu_framebuffers_new(&fbparams, gst_imx_vpu_enc_allocator_obtain()); if (vpu_base_enc->framebuffers == NULL) { GST_ELEMENT_ERROR(vpu_base_enc, RESOURCE, NO_SPACE_LEFT, ("could not create framebuffers structure"), (NULL)); return GST_FLOW_ERROR; } gst_imx_vpu_framebuffers_register_with_encoder(vpu_base_enc->framebuffers, vpu_base_enc->handle, src_stride); } /* Allocate physical buffer for output data (if not already present) */ if (vpu_base_enc->output_phys_buffer == NULL) { vpu_base_enc->output_phys_buffer = (GstImxPhysMemory *)gst_allocator_alloc(gst_imx_vpu_enc_allocator_obtain(), vpu_base_enc->framebuffers->total_size, NULL); if (vpu_base_enc->output_phys_buffer == NULL) { GST_ERROR_OBJECT(vpu_base_enc, "could not allocate physical buffer for output data"); return GST_FLOW_ERROR; } } /* Set up encoding parameters */ enc_enc_param.nInVirtOutput = (unsigned int)(vpu_base_enc->output_phys_buffer->mapped_virt_addr); /* TODO */ enc_enc_param.nInPhyOutput = (unsigned int)(vpu_base_enc->output_phys_buffer->phys_addr); enc_enc_param.nInOutputBufLen = vpu_base_enc->output_phys_buffer->mem.size; enc_enc_param.nPicWidth = vpu_base_enc->framebuffers->pic_width; enc_enc_param.nPicHeight = vpu_base_enc->framebuffers->pic_height; enc_enc_param.nFrameRate = vpu_base_enc->open_param.nFrameRate; enc_enc_param.pInFrame = &input_framebuf; enc_enc_param.nForceIPicture = 0; /* Force I-frame if either IS_FORCE_KEYFRAME or IS_FORCE_KEYFRAME_HEADERS is set for the current frame. */ if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME(frame) || GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME_HEADERS(frame)) { enc_enc_param.nForceIPicture = 1; GST_LOG_OBJECT(vpu_base_enc, "got request to make this a keyframe - forcing I frame"); GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT(frame); } /* Give the derived class a chance to set up encoding parameters too */ if (!klass->set_frame_enc_params(vpu_base_enc, &enc_enc_param, &(vpu_base_enc->open_param))) { GST_ERROR_OBJECT(vpu_base_enc, "derived class could not frame enc params"); return GST_FLOW_ERROR; } /* Main encoding block */ { GstBuffer *output_buffer = NULL; gsize output_buffer_offset = 0; gboolean frame_finished = FALSE; frame->output_buffer = NULL; /* Run in a loop until the VPU reports the input as used */ do { /* Feed input data */ enc_ret = VPU_EncEncodeFrame(vpu_base_enc->handle, &enc_enc_param); if (enc_ret != VPU_ENC_RET_SUCCESS) { GST_ERROR_OBJECT(vpu_base_enc, "failed to encode frame: %s", gst_imx_vpu_strerror(enc_ret)); VPU_EncReset(vpu_base_enc->handle); return GST_FLOW_ERROR; } if (frame_finished) { GST_WARNING_OBJECT(vpu_base_enc, "frame was already finished for the current input, but input not yet marked as used"); continue; } if (enc_enc_param.eOutRetCode & (VPU_ENC_OUTPUT_DIS | VPU_ENC_OUTPUT_SEQHEADER)) { /* Create an output buffer on demand */ if (output_buffer == NULL) { output_buffer = gst_video_encoder_allocate_output_buffer( encoder, vpu_base_enc->output_phys_buffer->mem.size ); frame->output_buffer = output_buffer; } GST_LOG_OBJECT(vpu_base_enc, "processing output data: %u bytes, output buffer offset %u", enc_enc_param.nOutOutputSize, output_buffer_offset); if (klass->fill_output_buffer != NULL) { /* Derived class fills data on its own */ gsize cur_offset = output_buffer_offset; output_buffer_offset += klass->fill_output_buffer( vpu_base_enc, frame, cur_offset, vpu_base_enc->output_phys_buffer->mapped_virt_addr, enc_enc_param.nOutOutputSize, enc_enc_param.eOutRetCode & VPU_ENC_OUTPUT_SEQHEADER ); } else { /* Use default data filling (= copy input to output) */ gst_buffer_fill( output_buffer, output_buffer_offset, vpu_base_enc->output_phys_buffer->mapped_virt_addr, enc_enc_param.nOutOutputSize ); output_buffer_offset += enc_enc_param.nOutOutputSize; } if (enc_enc_param.eOutRetCode & VPU_ENC_OUTPUT_DIS) { g_assert(output_buffer != NULL); /* Set the output buffer's size to the actual number of bytes * filled by the derived class */ gst_buffer_set_size(output_buffer, output_buffer_offset); /* Set the frame DTS */ frame->dts = frame->pts; /* And finish the frame, handing the output data over to the base class */ gst_video_encoder_finish_frame(encoder, frame); output_buffer = NULL; frame_finished = TRUE; if (!(enc_enc_param.eOutRetCode & VPU_ENC_INPUT_USED)) GST_WARNING_OBJECT(vpu_base_enc, "frame finished, but VPU did not report the input as used"); break; } } } while (!(enc_enc_param.eOutRetCode & VPU_ENC_INPUT_USED)); /* VPU_ENC_INPUT_NOT_USED has value 0x0 - cannot use it for flag checks */ /* If output_buffer is NULL at this point, it means VPU_ENC_OUTPUT_DIS was never communicated * by the VPU, and the buffer is unfinished. -> Drop it. */ if (output_buffer != NULL) { GST_WARNING_OBJECT(vpu_base_enc, "frame unfinished ; dropping"); gst_buffer_unref(output_buffer); frame->output_buffer = NULL; /* necessary to make finish_frame() drop the frame */ gst_video_encoder_finish_frame(encoder, frame); } } return GST_FLOW_OK; }
static GstFlowReturn gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer) { GstWaylandSink *sink = GST_WAYLAND_SINK (bsink); GstBuffer *to_render; GstWlBuffer *wlbuffer; GstFlowReturn ret = GST_FLOW_OK; g_mutex_lock (&sink->render_lock); GST_LOG_OBJECT (sink, "render buffer %p", buffer); if (G_UNLIKELY (!sink->window)) { /* ask for window handle. Unlock render_lock while doing that because * set_window_handle & friends will lock it in this context */ g_mutex_unlock (&sink->render_lock); gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink)); g_mutex_lock (&sink->render_lock); if (!sink->window) { /* if we were not provided a window, create one ourselves */ sink->window = gst_wl_window_new_toplevel (sink->display, &sink->video_info); } } /* drop buffers until we get a frame callback */ if (g_atomic_int_get (&sink->redraw_pending) == TRUE) goto done; /* make sure that the application has called set_render_rectangle() */ if (G_UNLIKELY (sink->window->render_rectangle.w == 0)) goto no_window_size; wlbuffer = gst_buffer_get_wl_buffer (buffer); if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) { GST_LOG_OBJECT (sink, "buffer %p has a wl_buffer from our display, " "writing directly", buffer); to_render = buffer; } else { GstMemory *mem; struct wl_buffer *wbuf = NULL; GST_LOG_OBJECT (sink, "buffer %p does not have a wl_buffer from our " "display, creating it", buffer); mem = gst_buffer_peek_memory (buffer, 0); if (gst_is_wl_shm_memory (mem)) { wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display, &sink->video_info); } if (wbuf) { gst_buffer_add_wl_buffer (buffer, wbuf, sink->display); to_render = buffer; } else { GstMapInfo src; /* we don't know how to create a wl_buffer directly from the provided * memory, so we have to copy the data to a memory that we know how * to handle... */ GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying to wl_shm memory", buffer); /* sink->pool always exists (created in set_caps), but it may not * be active if upstream is not using it */ if (!gst_buffer_pool_is_active (sink->pool) && !gst_buffer_pool_set_active (sink->pool, TRUE)) goto activate_failed; ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL); if (ret != GST_FLOW_OK) goto no_buffer; /* the first time we acquire a buffer, * we need to attach a wl_buffer on it */ wlbuffer = gst_buffer_get_wl_buffer (to_render); if (G_UNLIKELY (!wlbuffer)) { mem = gst_buffer_peek_memory (to_render, 0); wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display, &sink->video_info); if (G_UNLIKELY (!wbuf)) goto no_wl_buffer; gst_buffer_add_wl_buffer (to_render, wbuf, sink->display); } gst_buffer_map (buffer, &src, GST_MAP_READ); gst_buffer_fill (to_render, 0, src.data, src.size); gst_buffer_unmap (buffer, &src); } } /* drop double rendering */ if (G_UNLIKELY (to_render == sink->last_buffer)) { GST_LOG_OBJECT (sink, "Buffer already being rendered"); goto done; } gst_buffer_replace (&sink->last_buffer, to_render); render_last_buffer (sink); if (buffer != to_render) gst_buffer_unref (to_render); goto done; no_window_size: { GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, ("Window has no size set"), ("Make sure you set the size after calling set_window_handle")); ret = GST_FLOW_ERROR; goto done; } no_buffer: { GST_WARNING_OBJECT (sink, "could not create buffer"); goto done; } no_wl_buffer: { GST_ERROR_OBJECT (sink, "could not create wl_buffer out of wl_shm memory"); ret = GST_FLOW_ERROR; goto done; } activate_failed: { GST_ERROR_OBJECT (sink, "failed to activate bufferpool."); ret = GST_FLOW_ERROR; goto done; } done: { g_mutex_unlock (&sink->render_lock); return ret; } }
/** * gst_vaapi_plugin_base_get_input_buffer: * @plugin: a #GstVaapiPluginBase * @inbuf: the sink pad (input) buffer * @outbuf_ptr: the pointer to location to the VA surface backed buffer * * Acquires the sink pad (input) buffer as a VA surface backed * buffer. This is mostly useful for raw YUV buffers, as source * buffers that are already backed as a VA surface are passed * verbatim. * * Returns: #GST_FLOW_OK if the buffer could be acquired */ GstFlowReturn gst_vaapi_plugin_base_get_input_buffer (GstVaapiPluginBase * plugin, GstBuffer * inbuf, GstBuffer ** outbuf_ptr) { GstVaapiVideoMeta *meta; GstBuffer *outbuf; GstVideoFrame src_frame, out_frame; gboolean success; g_return_val_if_fail (inbuf != NULL, GST_FLOW_ERROR); g_return_val_if_fail (outbuf_ptr != NULL, GST_FLOW_ERROR); meta = gst_buffer_get_vaapi_video_meta (inbuf); if (meta) { *outbuf_ptr = gst_buffer_ref (inbuf); return GST_FLOW_OK; } if (!plugin->sinkpad_caps_is_raw) goto error_invalid_buffer; if (!plugin->sinkpad_buffer_pool) goto error_no_pool; if (!gst_buffer_pool_is_active (plugin->sinkpad_buffer_pool) && !gst_buffer_pool_set_active (plugin->sinkpad_buffer_pool, TRUE)) goto error_active_pool; outbuf = NULL; if (gst_buffer_pool_acquire_buffer (plugin->sinkpad_buffer_pool, &outbuf, NULL) != GST_FLOW_OK) goto error_create_buffer; if (is_dma_buffer (inbuf)) { if (!plugin_bind_dma_to_vaapi_buffer (plugin, inbuf, outbuf)) goto error_bind_dma_buffer; goto done; } if (!gst_video_frame_map (&src_frame, &plugin->sinkpad_info, inbuf, GST_MAP_READ)) goto error_map_src_buffer; if (!gst_video_frame_map (&out_frame, &plugin->sinkpad_info, outbuf, GST_MAP_WRITE)) goto error_map_dst_buffer; success = gst_video_frame_copy (&out_frame, &src_frame); gst_video_frame_unmap (&out_frame); gst_video_frame_unmap (&src_frame); if (!success) goto error_copy_buffer; done: gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1); *outbuf_ptr = outbuf; return GST_FLOW_OK; /* ERRORS */ error_no_pool: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("no buffer pool was negotiated"), ("no buffer pool was negotiated")); return GST_FLOW_ERROR; } error_active_pool: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("failed to activate buffer pool"), ("failed to activate buffer pool")); return GST_FLOW_ERROR; } error_map_dst_buffer: { gst_video_frame_unmap (&src_frame); // fall-through } error_map_src_buffer: { GST_WARNING ("failed to map buffer"); gst_buffer_unref (outbuf); return GST_FLOW_NOT_SUPPORTED; } /* ERRORS */ error_invalid_buffer: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("failed to validate source buffer"), ("failed to validate source buffer")); return GST_FLOW_ERROR; } error_create_buffer: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("Allocation failed"), ("failed to create buffer")); return GST_FLOW_ERROR; } error_bind_dma_buffer: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("Allocation failed"), ("failed to bind dma_buf to VA surface buffer")); gst_buffer_unref (outbuf); return GST_FLOW_ERROR; } error_copy_buffer: { GST_WARNING_OBJECT (plugin, "failed to upload buffer to VA surface"); gst_buffer_unref (outbuf); return GST_FLOW_NOT_SUPPORTED; } }
gboolean gst_imx_ipu_blitter_set_input_buffer(GstImxIpuBlitter *ipu_blitter, GstBuffer *input_buffer) { GstImxPhysMemMeta *phys_mem_meta; g_assert(input_buffer != NULL); phys_mem_meta = GST_IMX_PHYS_MEM_META_GET(input_buffer); /* Test if the input buffer uses DMA memory */ if ((phys_mem_meta != NULL) && (phys_mem_meta->phys_addr != 0)) { /* DMA memory present - the input buffer can be used as an actual input buffer */ gst_imx_ipu_blitter_set_actual_input_buffer(ipu_blitter, gst_buffer_ref(input_buffer)); GST_TRACE_OBJECT(ipu_blitter, "input buffer uses DMA memory - setting it as actual input buffer"); } else { /* No DMA memory present; the input buffer needs to be copied to an internal * temporary input buffer */ GstBuffer *temp_input_buffer; GstFlowReturn flow_ret; GST_TRACE_OBJECT(ipu_blitter, "input buffer does not use DMA memory - need to copy it to an internal input DMA buffer"); { /* The internal input buffer is the temp input frame's DMA memory. * If it does not exist yet, it needs to be created here. The temp input * frame is then mapped. */ if (ipu_blitter->internal_bufferpool == NULL) { /* Internal bufferpool does not exist yet - create it now, * so that it can in turn create the internal input buffer */ GstCaps *caps = gst_video_info_to_caps(&(ipu_blitter->input_video_info)); ipu_blitter->internal_bufferpool = gst_imx_ipu_blitter_create_bufferpool( ipu_blitter, caps, ipu_blitter->input_video_info.size, 2, 0, NULL, NULL ); gst_caps_unref(caps); if (ipu_blitter->internal_bufferpool == NULL) { GST_ERROR_OBJECT(ipu_blitter, "failed to create internal bufferpool"); return FALSE; } } /* Future versions of this code may propose the internal bufferpool upstream; * hence the is_active check */ if (!gst_buffer_pool_is_active(ipu_blitter->internal_bufferpool)) gst_buffer_pool_set_active(ipu_blitter->internal_bufferpool, TRUE); } /* Create the internal input buffer */ flow_ret = gst_buffer_pool_acquire_buffer(ipu_blitter->internal_bufferpool, &temp_input_buffer, NULL); if (flow_ret != GST_FLOW_OK) { GST_ERROR_OBJECT(ipu_blitter, "error acquiring input frame buffer: %s", gst_pad_mode_get_name(flow_ret)); return FALSE; } { GstVideoFrame input_frame, temp_input_frame; gst_video_frame_map(&input_frame, &(ipu_blitter->input_video_info), input_buffer, GST_MAP_READ); gst_video_frame_map(&temp_input_frame, &(ipu_blitter->input_video_info), temp_input_buffer, GST_MAP_WRITE); /* Copy the input buffer's pixels to the temp input frame * The gst_video_frame_copy() makes sure stride and plane offset values from both * frames are respected */ gst_video_frame_copy(&temp_input_frame, &input_frame); gst_video_frame_unmap(&temp_input_frame); gst_video_frame_unmap(&input_frame); } /* Finally, set the temp input buffer as the actual input buffer */ gst_imx_ipu_blitter_set_actual_input_buffer(ipu_blitter, temp_input_buffer); } /* Configure interlacing */ ipu_blitter->priv->task.input.deinterlace.enable = 0; if (ipu_blitter->deinterlace_mode != GST_IMX_IPU_BLITTER_DEINTERLACE_NONE) { switch (ipu_blitter->input_video_info.interlace_mode) { case GST_VIDEO_INTERLACE_MODE_INTERLEAVED: GST_TRACE_OBJECT(ipu_blitter, "input stream uses interlacing -> deinterlacing enabled"); ipu_blitter->priv->task.input.deinterlace.enable = 1; break; case GST_VIDEO_INTERLACE_MODE_MIXED: { GstVideoMeta *video_meta; GST_TRACE_OBJECT(ipu_blitter, "input stream uses mixed interlacing -> need to check video metadata deinterlacing flag"); video_meta = gst_buffer_get_video_meta(input_buffer); if (video_meta != NULL) { if (video_meta->flags & GST_VIDEO_FRAME_FLAG_INTERLACED) { GST_TRACE_OBJECT(ipu_blitter, "frame has video metadata and deinterlacing flag"); ipu_blitter->priv->task.input.deinterlace.enable = 1; } else GST_TRACE_OBJECT(ipu_blitter, "frame has video metadata but no deinterlacing flag"); } else GST_TRACE_OBJECT(ipu_blitter, "frame has no video metadata -> no deinterlacing done"); break; } case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE: { GST_TRACE_OBJECT(ipu_blitter, "input stream is progressive -> no deinterlacing necessary"); break; } case GST_VIDEO_INTERLACE_MODE_FIELDS: GST_FIXME_OBJECT(ipu_blitter, "2-fields deinterlacing not supported (yet)"); break; default: break; } } return TRUE; }
gboolean gst_imx_blitter_set_input_frame(GstImxBlitter *blitter, GstBuffer *frame) { gboolean ret; GstImxPhysMemMeta *phys_mem_meta; GstImxBlitterClass *klass; g_assert(blitter != NULL); klass = GST_IMX_BLITTER_CLASS(G_OBJECT_GET_CLASS(blitter)); g_assert(klass->set_input_frame != NULL); if (frame == NULL) return klass->set_input_frame(blitter, NULL); phys_mem_meta = GST_IMX_PHYS_MEM_META_GET(frame); if ((phys_mem_meta == NULL) || (phys_mem_meta->phys_addr == 0)) { GstFlowReturn flow_ret; GstBuffer *internal_input_frame; /* No DMA memory present; the input frame needs to be copied to an internal input frame */ GST_TRACE_OBJECT(blitter, "input frame does not use DMA memory - copying input frame to internal frame"); { if (blitter->dma_bufferpool == NULL) { GST_TRACE_OBJECT(blitter, "need to create internal bufferpool"); /* DMA bufferpool does not exist yet - create it now, * so that it can in turn create the internal input frame */ GstCaps *caps = gst_video_info_to_caps(&(blitter->input_video_info)); blitter->dma_bufferpool = gst_imx_blitter_create_bufferpool( blitter, caps, blitter->input_video_info.size, 0, 0, NULL, NULL ); gst_caps_unref(caps); if (blitter->dma_bufferpool == NULL) { GST_ERROR_OBJECT(blitter, "failed to create internal bufferpool"); return FALSE; } } /* Future versions of this code may propose the internal bufferpool upstream; * hence the is_active check */ if (!gst_buffer_pool_is_active(blitter->dma_bufferpool)) gst_buffer_pool_set_active(blitter->dma_bufferpool, TRUE); } /* Create new internal input frame */ GST_TRACE_OBJECT(blitter, "acquiring buffer for internal input frame"); internal_input_frame = NULL; flow_ret = gst_buffer_pool_acquire_buffer(blitter->dma_bufferpool, &internal_input_frame, NULL); if (flow_ret != GST_FLOW_OK) { if (internal_input_frame != NULL) gst_buffer_unref(internal_input_frame); GST_ERROR_OBJECT(blitter, "error acquiring input frame buffer: %s", gst_pad_mode_get_name(flow_ret)); return FALSE; } /* Copy the input buffer's pixels to the internal input frame */ { GstVideoFrame input_vidframe, internal_input_vidframe; gst_video_frame_map(&input_vidframe, &(blitter->input_video_info), frame, GST_MAP_READ); gst_video_frame_map(&internal_input_vidframe, &(blitter->input_video_info), internal_input_frame, GST_MAP_WRITE); /* gst_video_frame_copy() makes sure stride and plane offset values from both frames are respected */ gst_video_frame_copy(&internal_input_vidframe, &input_vidframe); /* copy interlace flags */ GST_BUFFER_FLAGS(internal_input_frame) |= (GST_BUFFER_FLAGS(frame) & (GST_VIDEO_BUFFER_FLAG_INTERLACED | GST_VIDEO_BUFFER_FLAG_TFF | GST_VIDEO_BUFFER_FLAG_RFF | GST_VIDEO_BUFFER_FLAG_ONEFIELD)); gst_video_frame_unmap(&internal_input_vidframe); gst_video_frame_unmap(&input_vidframe); } ret = klass->set_input_frame(blitter, internal_input_frame); gst_buffer_unref(internal_input_frame); } else { GST_TRACE_OBJECT(blitter, "input frame uses DMA memory - setting it directly as input frame"); ret = klass->set_input_frame(blitter, frame); } return ret; }
static GstFlowReturn gst_fsl_vpu_base_enc_handle_frame(GstVideoEncoder *encoder, GstVideoCodecFrame *frame) { VpuEncRetCode enc_ret; VpuEncEncParam enc_enc_param; GstFslPhysMemMeta *phys_mem_meta; GstFslVpuBaseEncClass *klass; GstFslVpuBaseEnc *vpu_base_enc; VpuFrameBuffer input_framebuf; GstBuffer *input_buffer; vpu_base_enc = GST_FSL_VPU_BASE_ENC(encoder); klass = GST_FSL_VPU_BASE_ENC_CLASS(G_OBJECT_GET_CLASS(vpu_base_enc)); g_assert(klass->set_frame_enc_params != NULL); memset(&enc_enc_param, 0, sizeof(enc_enc_param)); memset(&input_framebuf, 0, sizeof(input_framebuf)); phys_mem_meta = GST_FSL_PHYS_MEM_META_GET(frame->input_buffer); if (phys_mem_meta == NULL) { GstVideoFrame temp_input_video_frame, temp_incoming_video_frame; if (vpu_base_enc->internal_input_buffer == NULL) { /* The internal input buffer is the temp input frame's DMA memory. * If it does not exist yet, it needs to be created here. The temp input * frame is then mapped. */ GstFlowReturn flow_ret; if (vpu_base_enc->internal_bufferpool == NULL) { /* Internal bufferpool does not exist yet - create it now, * so that it can in turn create the internal input buffer */ GstStructure *config; GstCaps *caps; GstAllocator *allocator; caps = gst_video_info_to_caps(&(vpu_base_enc->video_info)); vpu_base_enc->internal_bufferpool = gst_fsl_phys_mem_buffer_pool_new(FALSE); allocator = gst_fsl_vpu_enc_allocator_obtain(); config = gst_buffer_pool_get_config(vpu_base_enc->internal_bufferpool); gst_buffer_pool_config_set_params(config, caps, vpu_base_enc->video_info.size, 2, 0); gst_buffer_pool_config_set_allocator(config, allocator, NULL); gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_FSL_PHYS_MEM); gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META); gst_buffer_pool_set_config(vpu_base_enc->internal_bufferpool, config); gst_caps_unref(caps); if (vpu_base_enc->internal_bufferpool == NULL) { GST_ERROR_OBJECT(vpu_base_enc, "failed to create internal bufferpool"); return FALSE; } } /* Future versions of this code may propose the internal bufferpool upstream; * hence the is_active check */ if (!gst_buffer_pool_is_active(vpu_base_enc->internal_bufferpool)) gst_buffer_pool_set_active(vpu_base_enc->internal_bufferpool, TRUE); /* Create the internal input buffer */ flow_ret = gst_buffer_pool_acquire_buffer(vpu_base_enc->internal_bufferpool, &(vpu_base_enc->internal_input_buffer), NULL); if (flow_ret != GST_FLOW_OK) { GST_ERROR_OBJECT(vpu_base_enc, "error acquiring input frame buffer: %s", gst_pad_mode_get_name(flow_ret)); return FALSE; } } gst_video_frame_map(&temp_incoming_video_frame, &(vpu_base_enc->video_info), frame->input_buffer, GST_MAP_READ); gst_video_frame_map(&temp_input_video_frame, &(vpu_base_enc->video_info), vpu_base_enc->internal_input_buffer, GST_MAP_WRITE); gst_video_frame_copy(&temp_input_video_frame, &temp_incoming_video_frame); gst_video_frame_unmap(&temp_incoming_video_frame); gst_video_frame_unmap(&temp_input_video_frame); input_buffer = vpu_base_enc->internal_input_buffer; phys_mem_meta = GST_FSL_PHYS_MEM_META_GET(vpu_base_enc->internal_input_buffer); } else input_buffer = frame->input_buffer; { gsize *plane_offsets; gint *plane_strides; GstVideoMeta *video_meta; unsigned char *phys_ptr; video_meta = gst_buffer_get_video_meta(input_buffer); if (video_meta != NULL) { plane_offsets = video_meta->offset; plane_strides = video_meta->stride; } else { plane_offsets = vpu_base_enc->video_info.offset; plane_strides = vpu_base_enc->video_info.stride; } phys_ptr = (unsigned char*)(phys_mem_meta->phys_addr); input_framebuf.pbufY = phys_ptr; input_framebuf.pbufCb = phys_ptr + plane_offsets[1]; input_framebuf.pbufCr = phys_ptr + plane_offsets[2]; input_framebuf.pbufMvCol = NULL; input_framebuf.nStrideY = plane_strides[0]; input_framebuf.nStrideC = plane_strides[1]; GST_TRACE_OBJECT(vpu_base_enc, "width: %d height: %d stride 0: %d stride 1: %d offset 0: %d offset 1: %d offset 2: %d", GST_VIDEO_INFO_WIDTH(&(vpu_base_enc->video_info)), GST_VIDEO_INFO_HEIGHT(&(vpu_base_enc->video_info)), plane_strides[0], plane_strides[1], plane_offsets[0], plane_offsets[1], plane_offsets[2]); if (vpu_base_enc->framebuffers == NULL) { GstFslVpuFramebufferParams fbparams; gst_fsl_vpu_framebuffers_enc_init_info_to_params(&(vpu_base_enc->init_info), &fbparams); fbparams.pic_width = vpu_base_enc->open_param.nPicWidth; fbparams.pic_height = vpu_base_enc->open_param.nPicHeight; vpu_base_enc->framebuffers = gst_fsl_vpu_framebuffers_new(&fbparams, gst_fsl_vpu_enc_allocator_obtain()); gst_fsl_vpu_framebuffers_register_with_encoder(vpu_base_enc->framebuffers, vpu_base_enc->handle, plane_strides[0]); } if (vpu_base_enc->output_phys_buffer == NULL) { vpu_base_enc->output_phys_buffer = (GstFslPhysMemory *)gst_allocator_alloc(gst_fsl_vpu_enc_allocator_obtain(), vpu_base_enc->framebuffers->total_size, NULL); if (vpu_base_enc->output_phys_buffer == NULL) { GST_ERROR_OBJECT(vpu_base_enc, "could not allocate physical buffer for output data"); return GST_FLOW_ERROR; } } } enc_enc_param.nInVirtOutput = (unsigned int)(vpu_base_enc->output_phys_buffer->mapped_virt_addr); /* TODO */ enc_enc_param.nInPhyOutput = (unsigned int)(vpu_base_enc->output_phys_buffer->phys_addr); enc_enc_param.nInOutputBufLen = vpu_base_enc->output_phys_buffer->mem.size; enc_enc_param.nPicWidth = vpu_base_enc->framebuffers->pic_width; enc_enc_param.nPicHeight = vpu_base_enc->framebuffers->pic_height; enc_enc_param.nFrameRate = vpu_base_enc->open_param.nFrameRate; enc_enc_param.pInFrame = &input_framebuf; if (!klass->set_frame_enc_params(vpu_base_enc, &enc_enc_param, &(vpu_base_enc->open_param))) { GST_ERROR_OBJECT(vpu_base_enc, "derived class could not frame enc params"); return GST_FLOW_ERROR; } enc_ret = VPU_EncEncodeFrame(vpu_base_enc->handle, &enc_enc_param); if (enc_ret != VPU_ENC_RET_SUCCESS) { GST_ERROR_OBJECT(vpu_base_enc, "failed to encode frame: %s", gst_fsl_vpu_strerror(enc_ret)); VPU_EncReset(vpu_base_enc->handle); return GST_FLOW_ERROR; } GST_LOG_OBJECT(vpu_base_enc, "out ret code: 0x%x out size: %u", enc_enc_param.eOutRetCode, enc_enc_param.nOutOutputSize); if ((enc_enc_param.eOutRetCode & VPU_ENC_OUTPUT_DIS) || (enc_enc_param.eOutRetCode & VPU_ENC_OUTPUT_SEQHEADER)) { gst_video_encoder_allocate_output_frame(encoder, frame, enc_enc_param.nOutOutputSize); gst_buffer_fill(frame->output_buffer, 0, vpu_base_enc->output_phys_buffer->mapped_virt_addr, enc_enc_param.nOutOutputSize); gst_video_encoder_finish_frame(encoder, frame); } return GST_FLOW_OK; }
static GstFlowReturn gst_imx_vpu_encoder_base_handle_frame(GstVideoEncoder *encoder, GstVideoCodecFrame *input_frame) { GstImxPhysMemMeta *phys_mem_meta; GstImxVpuEncoderBaseClass *klass; GstImxVpuEncoderBase *vpu_encoder_base; GstBuffer *input_buffer; ImxVpuEncParams enc_params; vpu_encoder_base = GST_IMX_VPU_ENCODER_BASE(encoder); klass = GST_IMX_VPU_ENCODER_BASE_CLASS(G_OBJECT_GET_CLASS(vpu_encoder_base)); if (vpu_encoder_base->drop) { input_frame->output_buffer = NULL; /* necessary to make finish_frame() drop the frame */ gst_video_encoder_finish_frame(encoder, input_frame); return GST_FLOW_OK; } /* Get access to the input buffer's physical address */ phys_mem_meta = GST_IMX_PHYS_MEM_META_GET(input_frame->input_buffer); /* If the incoming frame's buffer is not using physically contiguous memory, * it needs to be copied to the internal input buffer, otherwise the VPU * encoder cannot read the frame */ if (phys_mem_meta == NULL) { /* No physical memory metadata found -> buffer is not physically contiguous */ GstVideoFrame temp_input_video_frame, temp_incoming_video_frame; GST_LOG_OBJECT(vpu_encoder_base, "input buffer not physically contiguous - frame copy is necessary"); if (vpu_encoder_base->internal_input_buffer == NULL) { /* The internal input buffer is the temp input frame's DMA memory. * If it does not exist yet, it needs to be created here. The temp input * frame is then mapped. */ GstFlowReturn flow_ret; if (vpu_encoder_base->internal_input_bufferpool == NULL) { /* Internal bufferpool does not exist yet - create it now, * so that it can in turn create the internal input buffer */ GstStructure *config; GstCaps *caps; GST_DEBUG_OBJECT(vpu_encoder_base, "creating internal bufferpool"); caps = gst_video_info_to_caps(&(vpu_encoder_base->video_info)); vpu_encoder_base->internal_input_bufferpool = gst_imx_phys_mem_buffer_pool_new(FALSE); gst_object_ref(vpu_encoder_base->phys_mem_allocator); config = gst_buffer_pool_get_config(vpu_encoder_base->internal_input_bufferpool); gst_buffer_pool_config_set_params(config, caps, vpu_encoder_base->video_info.size, 2, 0); gst_buffer_pool_config_set_allocator(config, vpu_encoder_base->phys_mem_allocator, NULL); gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_IMX_PHYS_MEM); gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META); gst_buffer_pool_set_config(vpu_encoder_base->internal_input_bufferpool, config); gst_caps_unref(caps); if (vpu_encoder_base->internal_input_bufferpool == NULL) { GST_ERROR_OBJECT(vpu_encoder_base, "failed to create internal bufferpool"); return GST_FLOW_ERROR; } } /* Future versions of this code may propose the internal bufferpool upstream; * hence the is_active check */ if (!gst_buffer_pool_is_active(vpu_encoder_base->internal_input_bufferpool)) gst_buffer_pool_set_active(vpu_encoder_base->internal_input_bufferpool, TRUE); /* Create the internal input buffer */ flow_ret = gst_buffer_pool_acquire_buffer(vpu_encoder_base->internal_input_bufferpool, &(vpu_encoder_base->internal_input_buffer), NULL); if (flow_ret != GST_FLOW_OK) { GST_ERROR_OBJECT(vpu_encoder_base, "error acquiring input frame buffer: %s", gst_pad_mode_get_name(flow_ret)); return flow_ret; } } /* The internal input buffer exists at this point. Since the incoming frame * is not stored in physical memory, copy its pixels to the internal * input buffer, so the encoder can read them. */ gst_video_frame_map(&temp_incoming_video_frame, &(vpu_encoder_base->video_info), input_frame->input_buffer, GST_MAP_READ); gst_video_frame_map(&temp_input_video_frame, &(vpu_encoder_base->video_info), vpu_encoder_base->internal_input_buffer, GST_MAP_WRITE); gst_video_frame_copy(&temp_input_video_frame, &temp_incoming_video_frame); gst_video_frame_unmap(&temp_incoming_video_frame); gst_video_frame_unmap(&temp_input_video_frame); /* Set the input buffer as the encoder's input */ input_buffer = vpu_encoder_base->internal_input_buffer; /* And use the input buffer's physical memory metadata */ phys_mem_meta = GST_IMX_PHYS_MEM_META_GET(vpu_encoder_base->internal_input_buffer); } else { /* Physical memory metadata found -> buffer is physically contiguous * It can be used directly as input for the VPU encoder */ input_buffer = input_frame->input_buffer; } /* Prepare the input buffer's information (strides, plane offsets ..) for encoding */ { GstVideoMeta *video_meta; /* Try to use plane offset and stride information from the video * metadata if present, since these can be more accurate than * the information from the video info */ video_meta = gst_buffer_get_video_meta(input_buffer); if (video_meta != NULL) { vpu_encoder_base->input_framebuffer.y_stride = video_meta->stride[0]; vpu_encoder_base->input_framebuffer.cbcr_stride = video_meta->stride[1]; vpu_encoder_base->input_framebuffer.y_offset = video_meta->offset[0]; vpu_encoder_base->input_framebuffer.cb_offset = video_meta->offset[1]; vpu_encoder_base->input_framebuffer.cr_offset = video_meta->offset[2]; } else { vpu_encoder_base->input_framebuffer.y_stride = GST_VIDEO_INFO_PLANE_STRIDE(&(vpu_encoder_base->video_info), 0); vpu_encoder_base->input_framebuffer.cbcr_stride = GST_VIDEO_INFO_PLANE_STRIDE(&(vpu_encoder_base->video_info), 1); vpu_encoder_base->input_framebuffer.y_offset = GST_VIDEO_INFO_PLANE_OFFSET(&(vpu_encoder_base->video_info), 0); vpu_encoder_base->input_framebuffer.cb_offset = GST_VIDEO_INFO_PLANE_OFFSET(&(vpu_encoder_base->video_info), 1); vpu_encoder_base->input_framebuffer.cr_offset = GST_VIDEO_INFO_PLANE_OFFSET(&(vpu_encoder_base->video_info), 2); } vpu_encoder_base->input_framebuffer.mvcol_offset = 0; /* this is not used by the encoder */ vpu_encoder_base->input_framebuffer.context = (void *)(input_frame->system_frame_number); vpu_encoder_base->input_dmabuffer.fd = -1; vpu_encoder_base->input_dmabuffer.physical_address = phys_mem_meta->phys_addr; vpu_encoder_base->input_dmabuffer.size = gst_buffer_get_size(input_buffer); } /* Prepare the encoding parameters */ memset(&enc_params, 0, sizeof(enc_params)); imx_vpu_enc_set_default_encoding_params(vpu_encoder_base->encoder, &enc_params); enc_params.force_I_frame = 0; enc_params.acquire_output_buffer = gst_imx_vpu_encoder_base_acquire_output_buffer; enc_params.finish_output_buffer = gst_imx_vpu_encoder_base_finish_output_buffer; enc_params.output_buffer_context = vpu_encoder_base; /* Force I-frame if either IS_FORCE_KEYFRAME or IS_FORCE_KEYFRAME_HEADERS is set for the current frame. */ if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME(input_frame) || GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME_HEADERS(input_frame)) { enc_params.force_I_frame = 1; GST_LOG_OBJECT(vpu_encoder_base, "got request to make this a keyframe - forcing I frame"); GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT(input_frame); } /* Give the derived class a chance to set up encoding parameters too */ if ((klass->set_frame_enc_params != NULL) && !klass->set_frame_enc_params(vpu_encoder_base, &enc_params)) { GST_ERROR_OBJECT(vpu_encoder_base, "derived class could not frame enc params"); return GST_FLOW_ERROR; } /* Main encoding block */ { ImxVpuEncReturnCodes enc_ret; unsigned int output_code = 0; ImxVpuEncodedFrame encoded_data_frame; vpu_encoder_base->output_buffer = NULL; /* The actual encoding call */ memset(&encoded_data_frame, 0, sizeof(ImxVpuEncodedFrame)); enc_ret = imx_vpu_enc_encode(vpu_encoder_base->encoder, &(vpu_encoder_base->input_frame), &encoded_data_frame, &enc_params, &output_code); if (enc_ret != IMX_VPU_ENC_RETURN_CODE_OK) { GST_ERROR_OBJECT(vpu_encoder_base, "failed to encode frame: %s", imx_vpu_enc_error_string(enc_ret)); if (vpu_encoder_base->output_buffer != NULL) gst_buffer_unref(vpu_encoder_base->output_buffer); return GST_FLOW_ERROR; } /* Give the derived class a chance to process the output_block_buffer */ if ((klass->process_output_buffer != NULL) && !klass->process_output_buffer(vpu_encoder_base, input_frame, &(vpu_encoder_base->output_buffer))) { GST_ERROR_OBJECT(vpu_encoder_base, "derived class reports failure while processing encoded output"); if (vpu_encoder_base->output_buffer != NULL) gst_buffer_unref(vpu_encoder_base->output_buffer); return GST_FLOW_ERROR; } if (output_code & IMX_VPU_ENC_OUTPUT_CODE_ENCODED_FRAME_AVAILABLE) { GST_LOG_OBJECT(vpu_encoder_base, "VPU outputs encoded frame"); /* TODO: make use of the frame context that is retrieved with get_frame(i) * This is not strictly necessary, since the VPU encoder does not * do frame reordering, nor does it produce delays, but it would * be a bit cleaner. */ input_frame->dts = input_frame->pts; /* Take all of the encoded bits. The adapter contains an encoded frame * at this point. */ input_frame->output_buffer = vpu_encoder_base->output_buffer; /* And finish the frame, handing the output data over to the base class */ gst_video_encoder_finish_frame(encoder, input_frame); } else { /* If at this point IMX_VPU_ENC_OUTPUT_CODE_ENCODED_FRAME_AVAILABLE is not set * in the output_code, it means the input was used up before a frame could be * encoded. Therefore, no output frame can be pushed downstream. Note that this * should not happen during normal operation, so a warning is logged. */ if (vpu_encoder_base->output_buffer != NULL) gst_buffer_unref(vpu_encoder_base->output_buffer); GST_WARNING_OBJECT(vpu_encoder_base, "frame unfinished ; dropping"); input_frame->output_buffer = NULL; /* necessary to make finish_frame() drop the frame */ gst_video_encoder_finish_frame(encoder, input_frame); } } return GST_FLOW_OK; }
static GstFlowReturn gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); GstFlowReturn ret = GST_FLOW_OK; GST_DEBUG_OBJECT (self, "Handling frame %d", frame->system_frame_number); if (G_UNLIKELY (!g_atomic_int_get (&self->active))) goto flushing; if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2output))) { if (!self->input_state) goto not_negotiated; if (!gst_v4l2_object_set_format (self->v4l2output, self->input_state->caps)) goto not_negotiated; } if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) { GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool); GstVideoInfo info; GstVideoCodecState *output_state; GstBuffer *codec_data; GST_DEBUG_OBJECT (self, "Sending header"); codec_data = self->input_state->codec_data; /* We are running in byte-stream mode, so we don't know the headers, but * we need to send something, otherwise the decoder will refuse to * intialize. */ if (codec_data) { gst_buffer_ref (codec_data); } else { codec_data = frame->input_buffer; frame->input_buffer = NULL; } /* Ensure input internal pool is active */ if (!gst_buffer_pool_is_active (pool)) { GstStructure *config = gst_buffer_pool_get_config (pool); gst_buffer_pool_config_set_params (config, self->input_state->caps, self->v4l2output->info.size, 2, 2); /* There is no reason to refuse this config */ if (!gst_buffer_pool_set_config (pool, config)) goto activate_failed; if (!gst_buffer_pool_set_active (pool, TRUE)) goto activate_failed; } GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self-> v4l2output->pool), &codec_data); GST_VIDEO_DECODER_STREAM_LOCK (decoder); gst_buffer_unref (codec_data); if (!gst_v4l2_object_acquire_format (self->v4l2capture, &info)) goto not_negotiated; output_state = gst_video_decoder_set_output_state (decoder, info.finfo->format, info.width, info.height, self->input_state); /* Copy the rest of the information, there might be more in the future */ output_state->info.interlace_mode = info.interlace_mode; gst_video_codec_state_unref (output_state); if (!gst_video_decoder_negotiate (decoder)) { if (GST_PAD_IS_FLUSHING (decoder->srcpad)) goto flushing; else goto not_negotiated; } /* Ensure our internal pool is activated */ if (!gst_buffer_pool_set_active (GST_BUFFER_POOL (self->v4l2capture->pool), TRUE)) goto activate_failed; } if (g_atomic_int_get (&self->processing) == FALSE) { /* It's possible that the processing thread stopped due to an error */ if (self->output_flow != GST_FLOW_OK && self->output_flow != GST_FLOW_FLUSHING) { GST_DEBUG_OBJECT (self, "Processing loop stopped with error, leaving"); ret = self->output_flow; goto drop; } GST_DEBUG_OBJECT (self, "Starting decoding thread"); /* Start the processing task, when it quits, the task will disable input * processing to unlock input if draining, or prevent potential block */ g_atomic_int_set (&self->processing, TRUE); if (!gst_pad_start_task (decoder->srcpad, (GstTaskFunction) gst_v4l2_video_dec_loop, self, (GDestroyNotify) gst_v4l2_video_dec_loop_stopped)) goto start_task_failed; } if (frame->input_buffer) { GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self->v4l2output-> pool), &frame->input_buffer); GST_VIDEO_DECODER_STREAM_LOCK (decoder); if (ret == GST_FLOW_FLUSHING) { if (g_atomic_int_get (&self->processing) == FALSE) ret = self->output_flow; goto drop; } else if (ret != GST_FLOW_OK) { goto process_failed; } /* No need to keep input arround */ gst_buffer_replace (&frame->input_buffer, NULL); } gst_video_codec_frame_unref (frame); return ret; /* ERRORS */ not_negotiated: { GST_ERROR_OBJECT (self, "not negotiated"); ret = GST_FLOW_NOT_NEGOTIATED; goto drop; } activate_failed: { GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, (_("Failed to allocate required memory.")), ("Buffer pool activation failed")); ret = GST_FLOW_ERROR; goto drop; } flushing: { ret = GST_FLOW_FLUSHING; goto drop; } start_task_failed: { GST_ELEMENT_ERROR (self, RESOURCE, FAILED, (_("Failed to start decoding thread.")), (NULL)); g_atomic_int_set (&self->processing, FALSE); ret = GST_FLOW_ERROR; goto drop; } process_failed: { GST_ELEMENT_ERROR (self, RESOURCE, FAILED, (_("Failed to process frame.")), ("Maybe be due to not enough memory or failing driver")); ret = GST_FLOW_ERROR; goto drop; } drop: { gst_video_decoder_drop_frame (decoder, frame); return ret; } }
static GstFlowReturn gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) { GstV4l2Error error = GST_V4L2_ERROR_INIT; GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); GstFlowReturn ret = GST_FLOW_OK; gboolean processed = FALSE; GstBuffer *tmp; GST_DEBUG_OBJECT (self, "Handling frame %d", frame->system_frame_number); if (G_UNLIKELY (!g_atomic_int_get (&self->active))) goto flushing; if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2output))) { if (!self->input_state) goto not_negotiated; if (!gst_v4l2_object_set_format (self->v4l2output, self->input_state->caps, &error)) goto not_negotiated; } if (G_UNLIKELY (!GST_V4L2_IS_ACTIVE (self->v4l2capture))) { GstBufferPool *pool = GST_BUFFER_POOL (self->v4l2output->pool); GstVideoInfo info; GstVideoCodecState *output_state; GstBuffer *codec_data; GstCaps *acquired_caps, *available_caps, *caps, *filter; GstStructure *st; GST_DEBUG_OBJECT (self, "Sending header"); codec_data = self->input_state->codec_data; /* We are running in byte-stream mode, so we don't know the headers, but * we need to send something, otherwise the decoder will refuse to * intialize. */ if (codec_data) { gst_buffer_ref (codec_data); } else { codec_data = gst_buffer_ref (frame->input_buffer); processed = TRUE; } /* Ensure input internal pool is active */ if (!gst_buffer_pool_is_active (pool)) { GstStructure *config = gst_buffer_pool_get_config (pool); gst_buffer_pool_config_set_params (config, self->input_state->caps, self->v4l2output->info.size, 2, 2); /* There is no reason to refuse this config */ if (!gst_buffer_pool_set_config (pool, config)) goto activate_failed; if (!gst_buffer_pool_set_active (pool, TRUE)) goto activate_failed; } GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self-> v4l2output->pool), &codec_data); GST_VIDEO_DECODER_STREAM_LOCK (decoder); gst_buffer_unref (codec_data); /* For decoders G_FMT returns coded size, G_SELECTION returns visible size * in the compose rectangle. gst_v4l2_object_acquire_format() checks both * and returns the visible size as with/height and the coded size as * padding. */ if (!gst_v4l2_object_acquire_format (self->v4l2capture, &info)) goto not_negotiated; /* Create caps from the acquired format, remove the format field */ acquired_caps = gst_video_info_to_caps (&info); st = gst_caps_get_structure (acquired_caps, 0); gst_structure_remove_field (st, "format"); /* Probe currently available pixel formats */ available_caps = gst_v4l2_object_probe_caps (self->v4l2capture, NULL); available_caps = gst_caps_make_writable (available_caps); /* Replace coded size with visible size, we want to negotiate visible size * with downstream, not coded size. */ gst_caps_map_in_place (available_caps, gst_v4l2_video_remove_padding, self); filter = gst_caps_intersect_full (available_caps, acquired_caps, GST_CAPS_INTERSECT_FIRST); gst_caps_unref (acquired_caps); gst_caps_unref (available_caps); caps = gst_pad_peer_query_caps (decoder->srcpad, filter); gst_caps_unref (filter); GST_DEBUG_OBJECT (self, "Possible decoded caps: %" GST_PTR_FORMAT, caps); if (gst_caps_is_empty (caps)) { gst_caps_unref (caps); goto not_negotiated; } /* Fixate pixel format */ caps = gst_caps_fixate (caps); GST_DEBUG_OBJECT (self, "Chosen decoded caps: %" GST_PTR_FORMAT, caps); /* Try to set negotiated format, on success replace acquired format */ if (gst_v4l2_object_set_format (self->v4l2capture, caps, &error)) gst_video_info_from_caps (&info, caps); else gst_v4l2_clear_error (&error); gst_caps_unref (caps); output_state = gst_video_decoder_set_output_state (decoder, info.finfo->format, info.width, info.height, self->input_state); /* Copy the rest of the information, there might be more in the future */ output_state->info.interlace_mode = info.interlace_mode; gst_video_codec_state_unref (output_state); if (!gst_video_decoder_negotiate (decoder)) { if (GST_PAD_IS_FLUSHING (decoder->srcpad)) goto flushing; else goto not_negotiated; } /* Ensure our internal pool is activated */ if (!gst_buffer_pool_set_active (GST_BUFFER_POOL (self->v4l2capture->pool), TRUE)) goto activate_failed; } if (g_atomic_int_get (&self->processing) == FALSE) { /* It's possible that the processing thread stopped due to an error */ if (self->output_flow != GST_FLOW_OK && self->output_flow != GST_FLOW_FLUSHING) { GST_DEBUG_OBJECT (self, "Processing loop stopped with error, leaving"); ret = self->output_flow; goto drop; } GST_DEBUG_OBJECT (self, "Starting decoding thread"); /* Start the processing task, when it quits, the task will disable input * processing to unlock input if draining, or prevent potential block */ g_atomic_int_set (&self->processing, TRUE); if (!gst_pad_start_task (decoder->srcpad, (GstTaskFunction) gst_v4l2_video_dec_loop, self, (GDestroyNotify) gst_v4l2_video_dec_loop_stopped)) goto start_task_failed; } if (!processed) { GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self->v4l2output-> pool), &frame->input_buffer); GST_VIDEO_DECODER_STREAM_LOCK (decoder); if (ret == GST_FLOW_FLUSHING) { if (g_atomic_int_get (&self->processing) == FALSE) ret = self->output_flow; goto drop; } else if (ret != GST_FLOW_OK) { goto process_failed; } } /* No need to keep input arround */ tmp = frame->input_buffer; frame->input_buffer = gst_buffer_new (); gst_buffer_copy_into (frame->input_buffer, tmp, GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_META, 0, 0); gst_buffer_unref (tmp); gst_video_codec_frame_unref (frame); return ret; /* ERRORS */ not_negotiated: { GST_ERROR_OBJECT (self, "not negotiated"); ret = GST_FLOW_NOT_NEGOTIATED; gst_v4l2_error (self, &error); goto drop; } activate_failed: { GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, (_("Failed to allocate required memory.")), ("Buffer pool activation failed")); ret = GST_FLOW_ERROR; goto drop; } flushing: { ret = GST_FLOW_FLUSHING; goto drop; } start_task_failed: { GST_ELEMENT_ERROR (self, RESOURCE, FAILED, (_("Failed to start decoding thread.")), (NULL)); g_atomic_int_set (&self->processing, FALSE); ret = GST_FLOW_ERROR; goto drop; } process_failed: { GST_ELEMENT_ERROR (self, RESOURCE, FAILED, (_("Failed to process frame.")), ("Maybe be due to not enough memory or failing driver")); ret = GST_FLOW_ERROR; goto drop; } drop: { gst_video_decoder_drop_frame (decoder, frame); return ret; } }
/** * gst_v4l2_buffer_pool_process: * @bpool: a #GstBufferPool * @buf: a #GstBuffer, maybe be replaced * * Process @buf in @bpool. For capture devices, this functions fills @buf with * data from the device. For output devices, this functions send the contents of * @buf to the device for playback. * * Returns: %GST_FLOW_OK on success. */ GstFlowReturn gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) { GstFlowReturn ret = GST_FLOW_OK; GstBufferPool *bpool = GST_BUFFER_POOL_CAST (pool); GstV4l2Object *obj = pool->obj; GST_DEBUG_OBJECT (pool, "process buffer %p", buf); g_return_val_if_fail (gst_buffer_pool_is_active (bpool), GST_FLOW_ERROR); if (GST_BUFFER_POOL_IS_FLUSHING (pool)) return GST_FLOW_FLUSHING; switch (obj->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: /* capture */ switch (obj->mode) { case GST_V4L2_IO_RW: /* capture into the buffer */ ret = gst_v4l2_do_read (pool, *buf); break; case GST_V4L2_IO_MMAP: case GST_V4L2_IO_DMABUF: { GstBuffer *tmp; if ((*buf)->pool == bpool) { if (gst_buffer_get_size (*buf) == 0) goto eos; /* start copying buffers when we are running low on buffers */ if (g_atomic_int_get (&pool->num_queued) < pool->copy_threshold) { GstBuffer *copy; if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, MMAP)) { if (gst_buffer_pool_acquire_buffer (bpool, ©, NULL) == GST_FLOW_OK) { gst_v4l2_buffer_pool_release_buffer (bpool, copy); goto done; } } /* copy the buffer */ copy = gst_buffer_copy_region (*buf, GST_BUFFER_COPY_ALL | GST_BUFFER_COPY_DEEP, 0, -1); GST_LOG_OBJECT (pool, "copy buffer %p->%p", *buf, copy); /* and requeue so that we can continue capturing */ gst_buffer_unref (*buf); *buf = copy; } /* nothing, data was inside the buffer when we did _acquire() */ goto done; } /* buffer not from our pool, grab a frame and copy it into the target */ if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp)) != GST_FLOW_OK) goto done; /* An empty buffer on capture indicates the end of stream */ if (gst_buffer_get_size (tmp) == 0) { gst_v4l2_buffer_pool_release_buffer (bpool, tmp); goto eos; } ret = gst_v4l2_buffer_pool_copy_buffer (pool, *buf, tmp); /* an queue the buffer again after the copy */ gst_v4l2_buffer_pool_release_buffer (bpool, tmp); if (ret != GST_FLOW_OK) goto copy_failed; break; } case GST_V4L2_IO_USERPTR: { struct UserPtrData *data; /* Replace our buffer with downstream allocated buffer */ data = gst_mini_object_steal_qdata (GST_MINI_OBJECT (*buf), GST_V4L2_IMPORT_QUARK); gst_buffer_replace (buf, data->buffer); _unmap_userptr_frame (data); break; } case GST_V4L2_IO_DMABUF_IMPORT: { GstBuffer *tmp; /* Replace our buffer with downstream allocated buffer */ tmp = gst_mini_object_steal_qdata (GST_MINI_OBJECT (*buf), GST_V4L2_IMPORT_QUARK); gst_buffer_replace (buf, tmp); gst_buffer_unref (tmp); break; } default: g_assert_not_reached (); break; } break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: /* playback */ switch (obj->mode) { case GST_V4L2_IO_RW: /* FIXME, do write() */ GST_WARNING_OBJECT (pool, "implement write()"); break; case GST_V4L2_IO_USERPTR: case GST_V4L2_IO_DMABUF_IMPORT: case GST_V4L2_IO_DMABUF: case GST_V4L2_IO_MMAP: { GstBuffer *to_queue = NULL; GstV4l2MemoryGroup *group; gint index; if ((*buf)->pool != bpool) goto copying; if (!gst_v4l2_is_buffer_valid (*buf, &group)) goto copying; index = group->buffer.index; GST_LOG_OBJECT (pool, "processing buffer %i from our pool", index); index = group->buffer.index; if (pool->buffers[index] != NULL) { GST_LOG_OBJECT (pool, "buffer %i already queued, copying", index); goto copying; } /* we can queue directly */ to_queue = gst_buffer_ref (*buf); copying: if (to_queue == NULL) { GstBufferPoolAcquireParams params = { 0 }; GST_LOG_OBJECT (pool, "alloc buffer from our pool"); /* this can return EOS if all buffers are outstanding which would * be strange because we would expect the upstream element to have * allocated them and returned to us.. */ params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT; ret = gst_buffer_pool_acquire_buffer (bpool, &to_queue, ¶ms); if (ret != GST_FLOW_OK) goto acquire_failed; ret = gst_v4l2_buffer_pool_prepare_buffer (pool, to_queue, *buf); if (ret != GST_FLOW_OK) { gst_buffer_unref (to_queue); goto prepare_failed; } } if ((ret = gst_v4l2_buffer_pool_qbuf (pool, to_queue)) != GST_FLOW_OK) goto queue_failed; /* if we are not streaming yet (this is the first buffer, start * streaming now */ if (!gst_v4l2_buffer_pool_streamon (pool)) { /* don't check return value because qbuf would have failed */ gst_v4l2_is_buffer_valid (to_queue, &group); /* qbuf has taken the ref of the to_queue buffer but we are no in * streaming state, so the flush logic won't be performed. * To avoid leaks, flush the allocator and restore the queued * buffer as non-queued */ gst_v4l2_allocator_flush (pool->vallocator); pool->buffers[group->buffer.index] = NULL; gst_mini_object_set_qdata (GST_MINI_OBJECT (to_queue), GST_V4L2_IMPORT_QUARK, NULL, NULL); gst_buffer_unref (to_queue); g_atomic_int_add (&pool->num_queued, -1); goto start_failed; } if (g_atomic_int_get (&pool->num_queued) >= pool->min_latency) { GstBuffer *out; /* all buffers are queued, try to dequeue one and release it back * into the pool so that _acquire can get to it again. */ ret = gst_v4l2_buffer_pool_dqbuf (pool, &out); if (ret == GST_FLOW_OK) /* release the rendered buffer back into the pool. This wakes up any * thread waiting for a buffer in _acquire(). */ gst_buffer_unref (out); } break; } default: g_assert_not_reached (); break; } break; default: g_assert_not_reached (); break; } done: return ret; /* ERRORS */ copy_failed: { GST_ERROR_OBJECT (pool, "failed to copy buffer"); return ret; } eos: { GST_DEBUG_OBJECT (pool, "end of stream reached"); return GST_FLOW_EOS; } acquire_failed: { if (ret == GST_FLOW_FLUSHING) GST_DEBUG_OBJECT (pool, "flushing"); else GST_WARNING_OBJECT (pool, "failed to acquire a buffer: %s", gst_flow_get_name (ret)); return ret; } prepare_failed: { GST_ERROR_OBJECT (pool, "failed to prepare data"); return ret; } queue_failed: { GST_ERROR_OBJECT (pool, "failed to queue buffer"); return ret; } start_failed: { GST_ERROR_OBJECT (pool, "failed to start streaming"); return GST_FLOW_ERROR; } }