static GstFlowReturn gst_vaapidecode_push_all_decoded_frames (GstVaapiDecode * decode) { GstVideoDecoder *const vdec = GST_VIDEO_DECODER (decode); GstVaapiDecoderStatus status; GstVideoCodecFrame *out_frame; GstFlowReturn ret; for (;;) { status = gst_vaapi_decoder_get_frame (decode->decoder, &out_frame); switch (status) { case GST_VAAPI_DECODER_STATUS_SUCCESS: /* GstVaapiDecode's queue adds an extra reference */ gst_video_codec_frame_unref (out_frame); ret = gst_vaapidecode_push_decoded_frame (vdec, out_frame); if (ret != GST_FLOW_OK) return ret; break; case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA: return GST_FLOW_OK; default: GST_VIDEO_DECODER_ERROR (vdec, 1, STREAM, DECODE, ("Decoding failed"), ("Unknown decoding error"), ret); return ret; } } g_assert_not_reached (); }
static GstFlowReturn gst_mpeg2dec_alloc_sized_buf (GstMpeg2dec * mpeg2dec, guint size, GstVideoCodecFrame * frame, GstBuffer ** buffer) { GstFlowReturn ret = GST_FLOW_OK; GstVideoCodecState *state; state = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (mpeg2dec)); if (!mpeg2dec->need_cropping || mpeg2dec->has_cropping) { /* need parsed input, but that might be slightly bogus, * so avoid giving up altogether and mark it as error */ if (frame->output_buffer) { gst_buffer_replace (&frame->output_buffer, NULL); GST_VIDEO_DECODER_ERROR (mpeg2dec, 1, STREAM, DECODE, ("decoding error"), ("Input not correctly parsed"), ret); } ret = gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (mpeg2dec), frame); *buffer = frame->output_buffer; } else { GstAllocationParams params = { 0, 15, 0, 0 }; *buffer = gst_buffer_new_allocate (NULL, size, ¶ms); gst_video_codec_frame_set_user_data (frame, *buffer, (GDestroyNotify) frame_user_data_destroy_notify); } gst_video_codec_state_unref (state); return ret; }
static GstFlowReturn gst_vaapidecode_push_all_decoded_frames (GstVaapiDecode * decode) { GstVideoDecoder *const vdec = GST_VIDEO_DECODER (decode); GstVaapiDecoderStatus status; GstVideoCodecFrame *out_frame; GstFlowReturn ret; for (;;) { status = gst_vaapi_decoder_get_frame (decode->decoder, &out_frame); switch (status) { case GST_VAAPI_DECODER_STATUS_SUCCESS: ret = gst_vaapidecode_push_decoded_frame (vdec, out_frame); if (ret != GST_FLOW_OK) return ret; break; case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA: /* Delayed the pool re-negotiation untill we push all decoded (and queued) * frames downstream. Otherwise for the multi-resolution videos, the * GstVideoVideoMemory will be having wrong resolution. * commit 6eba201f3252eba6a99ab7da7a4c662091a3e884 */ if (!gst_vaapidecode_negotiate (decode)) return GST_FLOW_ERROR; return GST_FLOW_OK; default: GST_VIDEO_DECODER_ERROR (vdec, 1, STREAM, DECODE, ("Decoding failed"), ("Unknown decoding error"), ret); return ret; } } g_assert_not_reached (); }
static GstFlowReturn gst_vaapidecode_handle_frame (GstVideoDecoder * vdec, GstVideoCodecFrame * frame) { GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec); GstVaapiDecoderStatus status; GstFlowReturn ret; if (!decode->input_state) goto not_negotiated; /* Decode current frame */ for (;;) { status = gst_vaapi_decoder_decode (decode->decoder, frame); if (status == GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE) { /* Make sure that there are no decoded frames waiting in the output queue. */ ret = gst_vaapidecode_push_all_decoded_frames (decode); if (ret != GST_FLOW_OK) goto error_push_all_decoded_frames; g_mutex_lock (&decode->surface_ready_mutex); if (gst_vaapi_decoder_check_status (decode->decoder) == GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE) g_cond_wait (&decode->surface_ready, &decode->surface_ready_mutex); g_mutex_unlock (&decode->surface_ready_mutex); continue; } if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) goto error_decode; break; } /* Note that gst_vaapi_decoder_decode cannot return success without completing the decode and pushing all decoded frames into the output queue */ return gst_vaapidecode_push_all_decoded_frames (decode); /* ERRORS */ error_push_all_decoded_frames: { GST_ERROR ("push loop error while decoding %d", ret); gst_video_decoder_drop_frame (vdec, frame); return ret; } error_decode: { GST_ERROR ("decode error %d", status); switch (status) { case GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CODEC: case GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE: case GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CHROMA_FORMAT: ret = GST_FLOW_NOT_SUPPORTED; break; default: GST_VIDEO_DECODER_ERROR (vdec, 1, STREAM, DECODE, ("Decoding error"), ("Decode error %d", status), ret); break; } gst_video_decoder_drop_frame (vdec, frame); return ret; } not_negotiated: { GST_ERROR_OBJECT (decode, "not negotiated"); ret = GST_FLOW_NOT_NEGOTIATED; gst_video_decoder_drop_frame (vdec, frame); return ret; } }
static GstFlowReturn gst_mpeg2dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) { GstMpeg2dec *mpeg2dec = GST_MPEG2DEC (decoder); GstBuffer *buf = frame->input_buffer; GstMapInfo minfo; const mpeg2_info_t *info; mpeg2_state_t state; gboolean done = FALSE; GstFlowReturn ret = GST_FLOW_OK; GST_LOG_OBJECT (mpeg2dec, "received frame %d, timestamp %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, frame->system_frame_number, GST_TIME_ARGS (frame->pts), GST_TIME_ARGS (frame->duration)); gst_buffer_ref (buf); if (!gst_buffer_map (buf, &minfo, GST_MAP_READ)) { GST_ERROR_OBJECT (mpeg2dec, "Failed to map input buffer"); return GST_FLOW_ERROR; } info = mpeg2dec->info; GST_LOG_OBJECT (mpeg2dec, "calling mpeg2_buffer"); mpeg2_buffer (mpeg2dec->decoder, minfo.data, minfo.data + minfo.size); GST_LOG_OBJECT (mpeg2dec, "calling mpeg2_buffer done"); while (!done) { GST_LOG_OBJECT (mpeg2dec, "calling parse"); state = mpeg2_parse (mpeg2dec->decoder); GST_DEBUG_OBJECT (mpeg2dec, "parse state %d", state); switch (state) { #if MPEG2_RELEASE >= MPEG2_VERSION (0, 5, 0) case STATE_SEQUENCE_MODIFIED: GST_DEBUG_OBJECT (mpeg2dec, "sequence modified"); mpeg2dec->discont_state = MPEG2DEC_DISC_NEW_PICTURE; gst_mpeg2dec_clear_buffers (mpeg2dec); /* fall through */ #endif case STATE_SEQUENCE: ret = handle_sequence (mpeg2dec, info); /* if there is an error handling the sequence * reset the decoder, maybe something more elegant * could be done. */ if (ret == GST_FLOW_ERROR) { GST_VIDEO_DECODER_ERROR (decoder, 1, STREAM, DECODE, ("decoding error"), ("Bad sequence header"), ret); gst_video_decoder_drop_frame (decoder, frame); gst_mpeg2dec_flush (decoder); goto done; } break; case STATE_SEQUENCE_REPEATED: GST_DEBUG_OBJECT (mpeg2dec, "sequence repeated"); break; case STATE_GOP: GST_DEBUG_OBJECT (mpeg2dec, "gop"); break; case STATE_PICTURE: ret = handle_picture (mpeg2dec, info, frame); break; case STATE_SLICE_1ST: GST_LOG_OBJECT (mpeg2dec, "1st slice of frame encountered"); break; case STATE_PICTURE_2ND: GST_LOG_OBJECT (mpeg2dec, "Second picture header encountered. Decoding 2nd field"); break; #if MPEG2_RELEASE >= MPEG2_VERSION (0, 4, 0) case STATE_INVALID_END: GST_DEBUG_OBJECT (mpeg2dec, "invalid end"); #endif case STATE_END: GST_DEBUG_OBJECT (mpeg2dec, "end"); case STATE_SLICE: GST_DEBUG_OBJECT (mpeg2dec, "display_fbuf:%p, discard_fbuf:%p", info->display_fbuf, info->discard_fbuf); if (info->display_fbuf && info->display_fbuf->id) { ret = handle_slice (mpeg2dec, info); } else { GST_DEBUG_OBJECT (mpeg2dec, "no picture to display"); } if (info->discard_fbuf && info->discard_fbuf->id) gst_mpeg2dec_discard_buffer (mpeg2dec, GPOINTER_TO_INT (info->discard_fbuf->id) - 1); if (state != STATE_SLICE) { gst_mpeg2dec_clear_buffers (mpeg2dec); } break; case STATE_BUFFER: done = TRUE; break; /* error */ case STATE_INVALID: GST_VIDEO_DECODER_ERROR (decoder, 1, STREAM, DECODE, ("decoding error"), ("Reached libmpeg2 invalid state"), ret); continue; default: GST_ERROR_OBJECT (mpeg2dec, "Unknown libmpeg2 state %d, FIXME", state); ret = GST_FLOW_OK; gst_video_codec_frame_unref (frame); goto done; } if (ret != GST_FLOW_OK) { GST_DEBUG_OBJECT (mpeg2dec, "exit loop, reason %s", gst_flow_get_name (ret)); break; } } gst_video_codec_frame_unref (frame); done: gst_buffer_unmap (buf, &minfo); gst_buffer_unref (buf); return ret; }
static GstFlowReturn handle_picture (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info, GstVideoCodecFrame * frame) { GstFlowReturn ret; gint type; const gchar *type_str = NULL; gboolean key_frame = FALSE; const mpeg2_picture_t *picture = info->current_picture; GstBuffer *buffer; ret = gst_mpeg2dec_alloc_buffer (mpeg2dec, frame, &buffer); if (ret != GST_FLOW_OK) return ret; type = picture->flags & PIC_MASK_CODING_TYPE; switch (type) { case PIC_FLAG_CODING_TYPE_I: key_frame = TRUE; mpeg2_skip (mpeg2dec->decoder, 0); type_str = "I"; break; case PIC_FLAG_CODING_TYPE_P: type_str = "P"; break; case PIC_FLAG_CODING_TYPE_B: type_str = "B"; break; default: gst_video_codec_frame_ref (frame); ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (mpeg2dec), frame); GST_VIDEO_DECODER_ERROR (mpeg2dec, 1, STREAM, DECODE, ("decoding error"), ("Invalid picture type"), ret); return ret; } GST_DEBUG_OBJECT (mpeg2dec, "handle picture type %s", type_str); GST_DEBUG_OBJECT (mpeg2dec, "picture %s, frame %i", key_frame ? ", kf," : " ", frame->system_frame_number); if (GST_VIDEO_INFO_IS_INTERLACED (&mpeg2dec->decoded_info)) { /* This implies SEQ_FLAG_PROGRESSIVE_SEQUENCE is not set */ if (picture->flags & PIC_FLAG_TOP_FIELD_FIRST) { GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF); } if (!(picture->flags & PIC_FLAG_PROGRESSIVE_FRAME)) { GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED); } #if MPEG2_RELEASE >= MPEG2_VERSION(0,5,0) /* repeat field introduced in 0.5.0 */ if (picture->flags & PIC_FLAG_REPEAT_FIRST_FIELD) { GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_FLAG_RFF); } #endif } if (mpeg2dec->discont_state == MPEG2DEC_DISC_NEW_PICTURE && key_frame) { mpeg2dec->discont_state = MPEG2DEC_DISC_NEW_KEYFRAME; } GST_DEBUG_OBJECT (mpeg2dec, "picture: %s %s %s %s %s fields:%d ts:%" GST_TIME_FORMAT, (picture->flags & PIC_FLAG_PROGRESSIVE_FRAME ? "prog" : " "), (picture->flags & PIC_FLAG_TOP_FIELD_FIRST ? "tff" : " "), #if MPEG2_RELEASE >= MPEG2_VERSION(0,5,0) (picture->flags & PIC_FLAG_REPEAT_FIRST_FIELD ? "rff" : " "), #else "unknown rff", #endif (picture->flags & PIC_FLAG_SKIP ? "skip" : " "), (picture->flags & PIC_FLAG_COMPOSITE_DISPLAY ? "composite" : " "), picture->nb_fields, GST_TIME_ARGS (frame->pts)); return ret; }
static GstFlowReturn gst_openjpeg_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) { GstOpenJPEGDec *self = GST_OPENJPEG_DEC (decoder); GstFlowReturn ret = GST_FLOW_OK; gint64 deadline; GstMapInfo map; opj_dinfo_t *dec; opj_event_mgr_t callbacks; opj_cio_t *io; opj_image_t *image; GstVideoFrame vframe; opj_dparameters_t params; GST_DEBUG_OBJECT (self, "Handling frame"); deadline = gst_video_decoder_get_max_decode_time (decoder, frame); if (deadline < 0) { GST_LOG_OBJECT (self, "Dropping too late frame: deadline %" G_GINT64_FORMAT, deadline); ret = gst_video_decoder_drop_frame (decoder, frame); return ret; } dec = opj_create_decompress (self->codec_format); if (!dec) goto initialization_error; if (G_UNLIKELY (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_TRACE)) { callbacks.error_handler = gst_openjpeg_dec_opj_error; callbacks.warning_handler = gst_openjpeg_dec_opj_warning; callbacks.info_handler = gst_openjpeg_dec_opj_info; opj_set_event_mgr ((opj_common_ptr) dec, &callbacks, self); } else { opj_set_event_mgr ((opj_common_ptr) dec, NULL, NULL); } params = self->params; if (self->ncomps) params.jpwl_exp_comps = self->ncomps; opj_setup_decoder (dec, ¶ms); if (!gst_buffer_map (frame->input_buffer, &map, GST_MAP_READ)) goto map_read_error; io = opj_cio_open ((opj_common_ptr) dec, map.data + (self->is_jp2c ? 8 : 0), map.size - (self->is_jp2c ? 8 : 0)); if (!io) goto open_error; image = opj_decode (dec, io); if (!image) goto decode_error; gst_buffer_unmap (frame->input_buffer, &map); ret = gst_openjpeg_dec_negotiate (self, image); if (ret != GST_FLOW_OK) goto negotiate_error; ret = gst_video_decoder_allocate_output_frame (decoder, frame); if (ret != GST_FLOW_OK) goto allocate_error; if (!gst_video_frame_map (&vframe, &self->output_state->info, frame->output_buffer, GST_MAP_WRITE)) goto map_write_error; self->fill_frame (&vframe, image); gst_video_frame_unmap (&vframe); opj_image_destroy (image); opj_cio_close (io); opj_destroy_decompress (dec); ret = gst_video_decoder_finish_frame (decoder, frame); return ret; initialization_error: { gst_video_codec_frame_unref (frame); GST_ELEMENT_ERROR (self, LIBRARY, INIT, ("Failed to initialize OpenJPEG decoder"), (NULL)); return GST_FLOW_ERROR; } map_read_error: { opj_destroy_decompress (dec); gst_video_codec_frame_unref (frame); GST_ELEMENT_ERROR (self, CORE, FAILED, ("Failed to map input buffer"), (NULL)); return GST_FLOW_ERROR; } open_error: { opj_destroy_decompress (dec); gst_buffer_unmap (frame->input_buffer, &map); gst_video_codec_frame_unref (frame); GST_ELEMENT_ERROR (self, LIBRARY, INIT, ("Failed to open OpenJPEG stream"), (NULL)); return GST_FLOW_ERROR; } decode_error: { opj_cio_close (io); opj_destroy_decompress (dec); gst_buffer_unmap (frame->input_buffer, &map); gst_video_codec_frame_unref (frame); GST_VIDEO_DECODER_ERROR (self, 1, STREAM, DECODE, ("Failed to decode OpenJPEG stream"), (NULL), ret); return ret; } negotiate_error: { opj_image_destroy (image); opj_cio_close (io); opj_destroy_decompress (dec); gst_video_codec_frame_unref (frame); GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Failed to negotiate"), (NULL)); return ret; } allocate_error: { opj_image_destroy (image); opj_cio_close (io); opj_destroy_decompress (dec); gst_video_codec_frame_unref (frame); GST_ELEMENT_ERROR (self, CORE, FAILED, ("Failed to allocate output buffer"), (NULL)); return ret; } map_write_error: { opj_image_destroy (image); opj_cio_close (io); opj_destroy_decompress (dec); gst_video_codec_frame_unref (frame); GST_ELEMENT_ERROR (self, CORE, FAILED, ("Failed to map output buffer"), (NULL)); return GST_FLOW_ERROR; } }
static GstFlowReturn handle_picture (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info, GstVideoCodecFrame * frame) { GstVideoDecoder *decoder = (GstVideoDecoder *) mpeg2dec; GstFlowReturn ret; gint type; const gchar *type_str = NULL; gboolean key_frame = FALSE; const mpeg2_picture_t *picture = info->current_picture; GstVideoFrame vframe; guint8 *buf[3]; ret = gst_video_decoder_allocate_output_frame (decoder, frame); if (ret != GST_FLOW_OK) return ret; type = picture->flags & PIC_MASK_CODING_TYPE; switch (type) { case PIC_FLAG_CODING_TYPE_I: key_frame = TRUE; mpeg2_skip (mpeg2dec->decoder, 0); type_str = "I"; break; case PIC_FLAG_CODING_TYPE_P: type_str = "P"; break; case PIC_FLAG_CODING_TYPE_B: type_str = "B"; break; default: gst_video_codec_frame_ref (frame); ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (mpeg2dec), frame); GST_VIDEO_DECODER_ERROR (mpeg2dec, 1, STREAM, DECODE, ("decoding error"), ("Invalid picture type"), ret); return ret; } GST_DEBUG_OBJECT (mpeg2dec, "handle picture type %s", type_str); GST_DEBUG_OBJECT (mpeg2dec, "picture %s, frame %i", key_frame ? ", kf," : " ", frame->system_frame_number); if (GST_VIDEO_INFO_IS_INTERLACED (&mpeg2dec->decoded_info)) { /* This implies SEQ_FLAG_PROGRESSIVE_SEQUENCE is not set */ if (picture->flags & PIC_FLAG_TOP_FIELD_FIRST) { GST_BUFFER_FLAG_SET (frame->output_buffer, GST_VIDEO_BUFFER_FLAG_TFF); } if (!(picture->flags & PIC_FLAG_PROGRESSIVE_FRAME)) { GST_BUFFER_FLAG_SET (frame->output_buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED); } #if MPEG2_RELEASE >= MPEG2_VERSION(0,5,0) /* repeat field introduced in 0.5.0 */ if (picture->flags & PIC_FLAG_REPEAT_FIRST_FIELD) { GST_BUFFER_FLAG_SET (frame->output_buffer, GST_VIDEO_BUFFER_FLAG_RFF); } #endif } if (mpeg2dec->discont_state == MPEG2DEC_DISC_NEW_PICTURE && key_frame) { mpeg2dec->discont_state = MPEG2DEC_DISC_NEW_KEYFRAME; } GST_DEBUG_OBJECT (mpeg2dec, "picture: %s %s %s %s %s fields:%d ts:%" GST_TIME_FORMAT, (picture->flags & PIC_FLAG_PROGRESSIVE_FRAME ? "prog" : " "), (picture->flags & PIC_FLAG_TOP_FIELD_FIRST ? "tff" : " "), #if MPEG2_RELEASE >= MPEG2_VERSION(0,5,0) (picture->flags & PIC_FLAG_REPEAT_FIRST_FIELD ? "rff" : " "), #else "unknown rff", #endif (picture->flags & PIC_FLAG_SKIP ? "skip" : " "), (picture->flags & PIC_FLAG_COMPOSITE_DISPLAY ? "composite" : " "), picture->nb_fields, GST_TIME_ARGS (frame->pts)); if (!gst_video_frame_map (&vframe, &mpeg2dec->decoded_info, frame->output_buffer, GST_MAP_READ | GST_MAP_WRITE)) goto map_fail; buf[0] = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0); buf[1] = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1); buf[2] = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 2); GST_DEBUG_OBJECT (mpeg2dec, "set_buf: %p %p %p, frame %i", buf[0], buf[1], buf[2], frame->system_frame_number); /* Note: We use a non-null 'id' value to make the distinction * between the dummy buffers (which have an id of NULL) and the * ones we did */ mpeg2_stride (mpeg2dec->decoder, vframe.info.stride[0]); mpeg2_set_buf (mpeg2dec->decoder, buf, GINT_TO_POINTER (frame->system_frame_number + 1)); gst_mpeg2dec_save_buffer (mpeg2dec, frame->system_frame_number, &vframe); return ret; map_fail: { GST_ELEMENT_ERROR (mpeg2dec, RESOURCE, WRITE, ("Failed to map frame"), (NULL)); return GST_FLOW_ERROR; } }
static GstFlowReturn gst_jpeg_dec_handle_frame (GstVideoDecoder * bdec, GstVideoCodecFrame * frame) { GstFlowReturn ret = GST_FLOW_OK; GstJpegDec *dec = (GstJpegDec *) bdec; GstVideoFrame vframe; gint width, height; gint r_h, r_v; guint code, hdr_ok; gboolean need_unmap = TRUE; GstVideoCodecState *state = NULL; dec->current_frame = frame; gst_buffer_map (frame->input_buffer, &dec->current_frame_map, GST_MAP_READ); gst_jpeg_dec_fill_input_buffer (&dec->cinfo); if (setjmp (dec->jerr.setjmp_buffer)) { code = dec->jerr.pub.msg_code; if (code == JERR_INPUT_EOF) { GST_DEBUG ("jpeg input EOF error, we probably need more data"); goto need_more_data; } goto decode_error; } /* read header */ hdr_ok = jpeg_read_header (&dec->cinfo, TRUE); if (G_UNLIKELY (hdr_ok != JPEG_HEADER_OK)) { GST_WARNING_OBJECT (dec, "reading the header failed, %d", hdr_ok); } GST_LOG_OBJECT (dec, "num_components=%d", dec->cinfo.num_components); GST_LOG_OBJECT (dec, "jpeg_color_space=%d", dec->cinfo.jpeg_color_space); if (!dec->cinfo.num_components || !dec->cinfo.comp_info) goto components_not_supported; r_h = dec->cinfo.comp_info[0].h_samp_factor; r_v = dec->cinfo.comp_info[0].v_samp_factor; GST_LOG_OBJECT (dec, "r_h = %d, r_v = %d", r_h, r_v); if (dec->cinfo.num_components > 3) goto components_not_supported; /* verify color space expectation to avoid going *boom* or bogus output */ if (dec->cinfo.jpeg_color_space != JCS_YCbCr && dec->cinfo.jpeg_color_space != JCS_GRAYSCALE && dec->cinfo.jpeg_color_space != JCS_RGB) goto unsupported_colorspace; #ifndef GST_DISABLE_GST_DEBUG { gint i; for (i = 0; i < dec->cinfo.num_components; ++i) { GST_LOG_OBJECT (dec, "[%d] h_samp_factor=%d, v_samp_factor=%d, cid=%d", i, dec->cinfo.comp_info[i].h_samp_factor, dec->cinfo.comp_info[i].v_samp_factor, dec->cinfo.comp_info[i].component_id); } } #endif /* prepare for raw output */ dec->cinfo.do_fancy_upsampling = FALSE; dec->cinfo.do_block_smoothing = FALSE; dec->cinfo.out_color_space = dec->cinfo.jpeg_color_space; dec->cinfo.dct_method = dec->idct_method; dec->cinfo.raw_data_out = TRUE; GST_LOG_OBJECT (dec, "starting decompress"); guarantee_huff_tables (&dec->cinfo); if (!jpeg_start_decompress (&dec->cinfo)) { GST_WARNING_OBJECT (dec, "failed to start decompression cycle"); } /* sanity checks to get safe and reasonable output */ switch (dec->cinfo.jpeg_color_space) { case JCS_GRAYSCALE: if (dec->cinfo.num_components != 1) goto invalid_yuvrgbgrayscale; break; case JCS_RGB: if (dec->cinfo.num_components != 3 || dec->cinfo.max_v_samp_factor > 1 || dec->cinfo.max_h_samp_factor > 1) goto invalid_yuvrgbgrayscale; break; case JCS_YCbCr: if (dec->cinfo.num_components != 3 || r_v > 2 || r_v < dec->cinfo.comp_info[0].v_samp_factor || r_v < dec->cinfo.comp_info[1].v_samp_factor || r_h < dec->cinfo.comp_info[0].h_samp_factor || r_h < dec->cinfo.comp_info[1].h_samp_factor) goto invalid_yuvrgbgrayscale; break; default: g_assert_not_reached (); break; } width = dec->cinfo.output_width; height = dec->cinfo.output_height; if (G_UNLIKELY (width < MIN_WIDTH || width > MAX_WIDTH || height < MIN_HEIGHT || height > MAX_HEIGHT)) goto wrong_size; gst_jpeg_dec_negotiate (dec, width, height, dec->cinfo.jpeg_color_space); state = gst_video_decoder_get_output_state (bdec); ret = gst_video_decoder_allocate_output_frame (bdec, frame); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto alloc_failed; if (!gst_video_frame_map (&vframe, &state->info, frame->output_buffer, GST_MAP_READWRITE)) goto alloc_failed; GST_LOG_OBJECT (dec, "width %d, height %d", width, height); if (dec->cinfo.jpeg_color_space == JCS_RGB) { gst_jpeg_dec_decode_rgb (dec, &vframe); } else if (dec->cinfo.jpeg_color_space == JCS_GRAYSCALE) { gst_jpeg_dec_decode_grayscale (dec, &vframe); } else { GST_LOG_OBJECT (dec, "decompressing (reqired scanline buffer height = %u)", dec->cinfo.rec_outbuf_height); /* For some widths jpeglib requires more horizontal padding than I420 * provides. In those cases we need to decode into separate buffers and then * copy over the data into our final picture buffer, otherwise jpeglib might * write over the end of a line into the beginning of the next line, * resulting in blocky artifacts on the left side of the picture. */ if (G_UNLIKELY (width % (dec->cinfo.max_h_samp_factor * DCTSIZE) != 0 || dec->cinfo.comp_info[0].h_samp_factor != 2 || dec->cinfo.comp_info[1].h_samp_factor != 1 || dec->cinfo.comp_info[2].h_samp_factor != 1)) { GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, dec, "indirect decoding using extra buffer copy"); gst_jpeg_dec_decode_indirect (dec, &vframe, r_v, r_h, dec->cinfo.num_components); } else { ret = gst_jpeg_dec_decode_direct (dec, &vframe); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto decode_direct_failed; } } gst_video_frame_unmap (&vframe); GST_LOG_OBJECT (dec, "decompressing finished"); jpeg_finish_decompress (&dec->cinfo); gst_buffer_unmap (frame->input_buffer, &dec->current_frame_map); ret = gst_video_decoder_finish_frame (bdec, frame); need_unmap = FALSE; done: exit: if (need_unmap) gst_buffer_unmap (frame->input_buffer, &dec->current_frame_map); if (state) gst_video_codec_state_unref (state); return ret; /* special cases */ need_more_data: { GST_LOG_OBJECT (dec, "we need more data"); ret = GST_FLOW_OK; goto exit; } /* ERRORS */ wrong_size: { GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Picture is too small or too big (%ux%u)", width, height), ret); ret = GST_FLOW_ERROR; goto done; } decode_error: { gchar err_msg[JMSG_LENGTH_MAX]; dec->jerr.pub.format_message ((j_common_ptr) (&dec->cinfo), err_msg); GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Decode error #%u: %s", code, err_msg), ret); gst_buffer_unmap (frame->input_buffer, &dec->current_frame_map); gst_video_decoder_drop_frame (bdec, frame); need_unmap = FALSE; jpeg_abort_decompress (&dec->cinfo); goto done; } decode_direct_failed: { /* already posted an error message */ jpeg_abort_decompress (&dec->cinfo); goto done; } alloc_failed: { const gchar *reason; reason = gst_flow_get_name (ret); GST_DEBUG_OBJECT (dec, "failed to alloc buffer, reason %s", reason); /* Reset for next time */ jpeg_abort_decompress (&dec->cinfo); if (ret != GST_FLOW_EOS && ret != GST_FLOW_FLUSHING && ret != GST_FLOW_NOT_LINKED) { GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Buffer allocation failed, reason: %s", reason), ret); jpeg_abort_decompress (&dec->cinfo); } goto exit; } components_not_supported: { GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("number of components not supported: %d (max 3)", dec->cinfo.num_components), ret); jpeg_abort_decompress (&dec->cinfo); goto done; } unsupported_colorspace: { GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Picture has unknown or unsupported colourspace"), ret); jpeg_abort_decompress (&dec->cinfo); goto done; } invalid_yuvrgbgrayscale: { GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Picture is corrupt or unhandled YUV/RGB/grayscale layout"), ret); jpeg_abort_decompress (&dec->cinfo); goto done; } }
static GstFlowReturn gst_jpeg_dec_decode_direct (GstJpegDec * dec, GstVideoFrame * frame) { guchar **line[3]; /* the jpeg line buffer */ guchar *y[4 * DCTSIZE] = { NULL, }; /* alloc enough for the lines */ guchar *u[4 * DCTSIZE] = { NULL, }; /* r_v will be <4 */ guchar *v[4 * DCTSIZE] = { NULL, }; gint i, j; gint lines, v_samp[3]; guchar *base[3], *last[3]; gint stride[3]; guint height; line[0] = y; line[1] = u; line[2] = v; v_samp[0] = dec->cinfo.comp_info[0].v_samp_factor; v_samp[1] = dec->cinfo.comp_info[1].v_samp_factor; v_samp[2] = dec->cinfo.comp_info[2].v_samp_factor; if (G_UNLIKELY (v_samp[0] > 2 || v_samp[1] > 2 || v_samp[2] > 2)) goto format_not_supported; height = GST_VIDEO_FRAME_HEIGHT (frame); for (i = 0; i < 3; i++) { base[i] = GST_VIDEO_FRAME_COMP_DATA (frame, i); stride[i] = GST_VIDEO_FRAME_COMP_STRIDE (frame, i); /* make sure we don't make jpeglib write beyond our buffer, * which might happen if (height % (r_v*DCTSIZE)) != 0 */ last[i] = base[i] + (GST_VIDEO_FRAME_COMP_STRIDE (frame, i) * (GST_VIDEO_FRAME_COMP_HEIGHT (frame, i) - 1)); } /* let jpeglib decode directly into our final buffer */ GST_DEBUG_OBJECT (dec, "decoding directly into output buffer"); for (i = 0; i < height; i += v_samp[0] * DCTSIZE) { for (j = 0; j < (v_samp[0] * DCTSIZE); ++j) { /* Y */ line[0][j] = base[0] + (i + j) * stride[0]; if (G_UNLIKELY (line[0][j] > last[0])) line[0][j] = last[0]; /* U */ if (v_samp[1] == v_samp[0]) { line[1][j] = base[1] + ((i + j) / 2) * stride[1]; } else if (j < (v_samp[1] * DCTSIZE)) { line[1][j] = base[1] + ((i / 2) + j) * stride[1]; } if (G_UNLIKELY (line[1][j] > last[1])) line[1][j] = last[1]; /* V */ if (v_samp[2] == v_samp[0]) { line[2][j] = base[2] + ((i + j) / 2) * stride[2]; } else if (j < (v_samp[2] * DCTSIZE)) { line[2][j] = base[2] + ((i / 2) + j) * stride[2]; } if (G_UNLIKELY (line[2][j] > last[2])) line[2][j] = last[2]; } lines = jpeg_read_raw_data (&dec->cinfo, line, v_samp[0] * DCTSIZE); if (G_UNLIKELY (!lines)) { GST_INFO_OBJECT (dec, "jpeg_read_raw_data() returned 0"); } } return GST_FLOW_OK; format_not_supported: { gboolean ret = GST_FLOW_OK; GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Unsupported subsampling schema: v_samp factors: %u %u %u", v_samp[0], v_samp[1], v_samp[2]), ret); return ret; } }
static GstFlowReturn gst_vp9_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) { GstVP9Dec *dec; GstFlowReturn ret = GST_FLOW_OK; vpx_codec_err_t status; vpx_codec_iter_t iter = NULL; vpx_image_t *img; long decoder_deadline = 0; GstClockTimeDiff deadline; GstMapInfo minfo; GST_DEBUG_OBJECT (decoder, "handle_frame"); dec = GST_VP9_DEC (decoder); if (!dec->decoder_inited) { ret = open_codec (dec, frame); if (ret == GST_FLOW_CUSTOM_SUCCESS_1) return GST_FLOW_OK; else if (ret != GST_FLOW_OK) return ret; } deadline = gst_video_decoder_get_max_decode_time (decoder, frame); if (deadline < 0) { decoder_deadline = 1; } else if (deadline == G_MAXINT64) { decoder_deadline = 0; } else { decoder_deadline = MAX (1, deadline / GST_MSECOND); } if (!gst_buffer_map (frame->input_buffer, &minfo, GST_MAP_READ)) { GST_ERROR_OBJECT (dec, "Failed to map input buffer"); return GST_FLOW_ERROR; } status = vpx_codec_decode (&dec->decoder, minfo.data, minfo.size, NULL, decoder_deadline); gst_buffer_unmap (frame->input_buffer, &minfo); if (status) { GST_VIDEO_DECODER_ERROR (decoder, 1, LIBRARY, ENCODE, ("Failed to decode frame"), ("%s", gst_vpx_error_name (status)), ret); return ret; } img = vpx_codec_get_frame (&dec->decoder, &iter); if (img) { GstVideoFormat fmt; switch (img->fmt) { case VPX_IMG_FMT_I420: fmt = GST_VIDEO_FORMAT_I420; break; case VPX_IMG_FMT_YV12: fmt = GST_VIDEO_FORMAT_YV12; break; case VPX_IMG_FMT_I422: fmt = GST_VIDEO_FORMAT_Y42B; break; case VPX_IMG_FMT_I444: fmt = GST_VIDEO_FORMAT_Y444; break; default: vpx_img_free (img); GST_ELEMENT_ERROR (decoder, LIBRARY, ENCODE, ("Failed to decode frame"), ("Unsupported color format %d", img->fmt)); return GST_FLOW_ERROR; break; } if (!dec->output_state || dec->output_state->info.finfo->format != fmt || dec->output_state->info.width != img->d_w || dec->output_state->info.height != img->d_h) { gboolean send_tags = !dec->output_state; if (dec->output_state) gst_video_codec_state_unref (dec->output_state); dec->output_state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (dec), fmt, img->d_w, img->d_h, dec->input_state); gst_video_decoder_negotiate (GST_VIDEO_DECODER (dec)); if (send_tags) gst_vp9_dec_send_tags (dec); } if (deadline < 0) { GST_LOG_OBJECT (dec, "Skipping late frame (%f s past deadline)", (double) -deadline / GST_SECOND); gst_video_decoder_drop_frame (decoder, frame); } else { ret = gst_video_decoder_allocate_output_frame (decoder, frame); if (ret == GST_FLOW_OK) { gst_vp9_dec_image_to_buffer (dec, img, frame->output_buffer); ret = gst_video_decoder_finish_frame (decoder, frame); } else { gst_video_decoder_drop_frame (decoder, frame); } } vpx_img_free (img); while ((img = vpx_codec_get_frame (&dec->decoder, &iter))) { GST_WARNING_OBJECT (decoder, "Multiple decoded frames... dropping"); vpx_img_free (img); } } else { /* Invisible frame */ GST_VIDEO_CODEC_FRAME_SET_DECODE_ONLY (frame); gst_video_decoder_finish_frame (decoder, frame); } return ret; }