static int DecodeAsync(decoder_t *p_dec, block_t *p_block) { decoder_sys_t *p_sys = p_dec->p_sys; HRESULT hr; if (!p_block) /* No Drain */ return VLCDEC_SUCCESS; if (p_block->i_flags & (BLOCK_FLAG_CORRUPTED)) { block_Release(p_block); return VLCDEC_SUCCESS; } /* Dequeue all pending media events. */ while ((hr = DequeueMediaEvent(p_dec)) == S_OK) continue; if (hr != MF_E_NO_EVENTS_AVAILABLE && FAILED(hr)) goto error; /* Drain the output stream of the MFT before sending the input packet. */ if (p_sys->pending_output_events > 0) { p_sys->pending_output_events -= 1; if (ProcessOutputStream(p_dec, p_sys->output_stream_id)) goto error; } /* Poll the MFT and return decoded frames until the input stream is ready. */ while (p_sys->pending_input_events == 0) { hr = DequeueMediaEvent(p_dec); if (hr == MF_E_NO_EVENTS_AVAILABLE) { /* Sleep for 1 ms to avoid excessive polling. */ Sleep(1); continue; } if (FAILED(hr)) goto error; if (p_sys->pending_output_events > 0) { p_sys->pending_output_events -= 1; if (ProcessOutputStream(p_dec, p_sys->output_stream_id)) goto error; break; } } p_sys->pending_input_events -= 1; if (ProcessInputStream(p_dec, p_sys->input_stream_id, p_block)) goto error; block_Release(p_block); return VLCDEC_SUCCESS; error: msg_Err(p_dec, "Error in DecodeAsync()"); block_Release(p_block); return VLCDEC_SUCCESS; }
/***************************************************************************** * DecodeVideo: Called to decode one or more frames *****************************************************************************/ picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; AVCodecContext *p_context = p_sys->p_context; int b_drawpicture; int b_null_size = false; block_t *p_block; if( !pp_block || !*pp_block ) return NULL; if( !p_context->extradata_size && p_dec->fmt_in.i_extra ) { ffmpeg_InitCodec( p_dec ); if( p_sys->b_delayed_open ) { if( ffmpeg_OpenCodec( p_dec ) ) msg_Err( p_dec, "cannot open codec (%s)", p_sys->psz_namecodec ); } } p_block = *pp_block; if( p_sys->b_delayed_open ) { block_Release( p_block ); return NULL; } if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { p_sys->i_pts = VLC_TS_INVALID; /* To make sure we recover properly */ p_sys->i_late_frames = 0; if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY ) avcodec_flush_buffers( p_context ); block_Release( p_block ); return NULL; } if( p_block->i_flags & BLOCK_FLAG_PREROLL ) { /* Do not care about late frames when prerolling * TODO avoid decoding of non reference frame * (ie all B except for H264 where it depends only on nal_ref_idc) */ p_sys->i_late_frames = 0; } if( !p_dec->b_pace_control && (p_sys->i_late_frames > 0) && (mdate() - p_sys->i_late_frames_start > INT64_C(5000000)) ) { if( p_sys->i_pts > VLC_TS_INVALID ) { msg_Err( p_dec, "more than 5 seconds of late video -> " "dropping frame (computer too slow ?)" ); p_sys->i_pts = VLC_TS_INVALID; /* To make sure we recover properly */ } block_Release( p_block ); p_sys->i_late_frames--; return NULL; } /* A good idea could be to decode all I pictures and see for the other */ if( !p_dec->b_pace_control && p_sys->b_hurry_up && (p_sys->i_late_frames > 4) ) { b_drawpicture = 0; if( p_sys->i_late_frames < 12 ) { p_context->skip_frame = (p_sys->i_skip_frame <= AVDISCARD_NONREF) ? AVDISCARD_NONREF : p_sys->i_skip_frame; } else { /* picture too late, won't decode * but break picture until a new I, and for mpeg4 ...*/ p_sys->i_late_frames--; /* needed else it will never be decrease */ block_Release( p_block ); return NULL; } } else { if( p_sys->b_hurry_up ) p_context->skip_frame = p_sys->i_skip_frame; if( !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) b_drawpicture = 1; else b_drawpicture = 0; } if( p_context->width <= 0 || p_context->height <= 0 ) { if( p_sys->b_hurry_up ) p_context->skip_frame = p_sys->i_skip_frame; b_null_size = true; } else if( !b_drawpicture ) { /* It creates broken picture * FIXME either our parser or ffmpeg is broken */ #if 0 if( p_sys->b_hurry_up ) p_context->skip_frame = __MAX( p_context->skip_frame, AVDISCARD_NONREF ); #endif } /* * Do the actual decoding now */ /* Don't forget that ffmpeg requires a little more bytes * that the real frame size */ if( p_block->i_buffer > 0 ) { p_sys->b_flush = ( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) != 0; p_block = block_Realloc( p_block, 0, p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE ); if( !p_block ) return NULL; p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE; *pp_block = p_block; memset( p_block->p_buffer + p_block->i_buffer, 0, FF_INPUT_BUFFER_PADDING_SIZE ); } while( p_block->i_buffer > 0 || p_sys->b_flush ) { int i_used, b_gotpicture; picture_t *p_pic; AVPacket pkt; /* Set the PTS/DTS in the context reordered_opaque field */ if( p_block->i_pts > VLC_TS_INVALID ) p_context->reordered_opaque = (p_block->i_pts << 1) | 0; else if( p_block->i_dts > VLC_TS_INVALID ) p_context->reordered_opaque = (p_block->i_dts << 1) | 1; else p_context->reordered_opaque = INT64_MIN; p_sys->p_ff_pic->reordered_opaque = p_context->reordered_opaque; /* Make sure we don't reuse the same timestamps twice */ p_block->i_pts = p_block->i_dts = VLC_TS_INVALID; post_mt( p_sys ); av_init_packet( &pkt ); pkt.data = p_block->p_buffer; pkt.size = p_block->i_buffer; i_used = avcodec_decode_video2( p_context, p_sys->p_ff_pic, &b_gotpicture, &pkt ); if( b_null_size && !p_sys->b_flush && p_context->width > 0 && p_context->height > 0 ) { /* Reparse it to not drop the I frame */ b_null_size = false; if( p_sys->b_hurry_up ) p_context->skip_frame = p_sys->i_skip_frame; i_used = avcodec_decode_video2( p_context, p_sys->p_ff_pic, &b_gotpicture, &pkt ); } wait_mt( p_sys ); if( p_sys->b_flush ) p_sys->b_first_frame = true; if( p_block->i_buffer <= 0 ) p_sys->b_flush = false; if( i_used < 0 ) { if( b_drawpicture ) msg_Warn( p_dec, "cannot decode one frame (%zu bytes)", p_block->i_buffer ); block_Release( p_block ); return NULL; } else if( i_used > p_block->i_buffer || p_context->thread_count > 1 ) { i_used = p_block->i_buffer; } /* Consumed bytes */ p_block->i_buffer -= i_used; p_block->p_buffer += i_used; /* Nothing to display */ if( !b_gotpicture ) { if( i_used == 0 ) break; continue; } /* Sanity check (seems to be needed for some streams) */ if( p_sys->p_ff_pic->pict_type == AV_PICTURE_TYPE_B) { p_sys->b_has_b_frames = true; } /* Compute the PTS */ mtime_t i_pts = VLC_TS_INVALID; if( p_sys->p_ff_pic->reordered_opaque != INT64_MIN ) { mtime_t i_ts = p_sys->p_ff_pic->reordered_opaque >> 1; bool b_dts = p_sys->p_ff_pic->reordered_opaque & 1; if( b_dts ) { if( !p_context->has_b_frames || !p_sys->b_has_b_frames || !p_sys->p_ff_pic->reference || p_sys->i_pts <= VLC_TS_INVALID ) i_pts = i_ts; /* Guess what ? The rules are different for Real Video :( */ if( (p_dec->fmt_in.i_codec == VLC_CODEC_RV30 || p_dec->fmt_in.i_codec == VLC_CODEC_RV40) && p_sys->b_has_b_frames ) { i_pts = VLC_TS_INVALID; if(p_sys->p_ff_pic->reference) i_pts = i_ts; } } else { i_pts = i_ts; } } if( i_pts <= VLC_TS_INVALID ) i_pts = p_sys->i_pts; /* Interpolate the next PTS */ if( i_pts > VLC_TS_INVALID ) p_sys->i_pts = i_pts; if( p_sys->i_pts > VLC_TS_INVALID ) { /* interpolate the next PTS */ if( p_dec->fmt_in.video.i_frame_rate > 0 && p_dec->fmt_in.video.i_frame_rate_base > 0 ) { p_sys->i_pts += INT64_C(1000000) * (2 + p_sys->p_ff_pic->repeat_pict) * p_dec->fmt_in.video.i_frame_rate_base / (2 * p_dec->fmt_in.video.i_frame_rate); } else if( p_context->time_base.den > 0 ) { int i_tick = p_context->ticks_per_frame; if( i_tick <= 0 ) i_tick = 1; p_sys->i_pts += INT64_C(1000000) * (2 + p_sys->p_ff_pic->repeat_pict) * i_tick * p_context->time_base.num / (2 * p_context->time_base.den); } } /* Update frame late count (except when doing preroll) */ mtime_t i_display_date = 0; if( !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) i_display_date = decoder_GetDisplayDate( p_dec, i_pts ); if( i_display_date > 0 && i_display_date <= mdate() ) { p_sys->i_late_frames++; if( p_sys->i_late_frames == 1 ) p_sys->i_late_frames_start = mdate(); } else { p_sys->i_late_frames = 0; } if( !b_drawpicture || ( !p_sys->p_va && !p_sys->p_ff_pic->linesize[0] ) ) continue; if( !p_sys->p_ff_pic->opaque ) { /* Get a new picture */ p_pic = ffmpeg_NewPictBuf( p_dec, p_context ); if( !p_pic ) { block_Release( p_block ); return NULL; } /* Fill p_picture_t from AVVideoFrame and do chroma conversion * if needed */ ffmpeg_CopyPicture( p_dec, p_pic, p_sys->p_ff_pic ); } else { p_pic = (picture_t *)p_sys->p_ff_pic->opaque; decoder_LinkPicture( p_dec, p_pic ); } if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den ) { /* Fetch again the aspect ratio in case it changed */ p_dec->fmt_out.video.i_sar_num = p_context->sample_aspect_ratio.num; p_dec->fmt_out.video.i_sar_den = p_context->sample_aspect_ratio.den; if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den ) { p_dec->fmt_out.video.i_sar_num = 1; p_dec->fmt_out.video.i_sar_den = 1; } } /* Send decoded frame to vout */ if( i_pts > VLC_TS_INVALID) { p_pic->date = i_pts; if( p_sys->b_first_frame ) { /* Hack to force display of still pictures */ p_sys->b_first_frame = false; p_pic->b_force = true; } p_pic->i_nb_fields = 2 + p_sys->p_ff_pic->repeat_pict; p_pic->b_progressive = !p_sys->p_ff_pic->interlaced_frame; p_pic->b_top_field_first = p_sys->p_ff_pic->top_field_first; p_pic->i_qstride = p_sys->p_ff_pic->qstride; int i_mb_h = ( p_pic->format.i_height + 15 ) / 16; p_pic->p_q = malloc( p_pic->i_qstride * i_mb_h ); memcpy( p_pic->p_q, p_sys->p_ff_pic->qscale_table, p_pic->i_qstride * i_mb_h ); switch( p_sys->p_ff_pic->qscale_type ) { case FF_QSCALE_TYPE_MPEG1: p_pic->i_qtype = QTYPE_MPEG1; break; case FF_QSCALE_TYPE_MPEG2: p_pic->i_qtype = QTYPE_MPEG2; break; case FF_QSCALE_TYPE_H264: p_pic->i_qtype = QTYPE_H264; break; } return p_pic; } else { decoder_DeletePicture( p_dec, p_pic ); } }
/**************************************************************************** * DecodeBlock: the whole thing ****************************************************************************/ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_block = NULL; BC_DTS_PROC_OUT proc_out; BC_DTS_STATUS driver_stat; /* First check the status of the decode to produce pictures */ if( BC_FUNC_PSYS(DtsGetDriverStatus)( p_sys->bcm_handle, &driver_stat ) != BC_STS_SUCCESS ) return NULL; if( pp_block ) p_block = *pp_block; if( p_block ) { if( ( p_block->i_flags & (BLOCK_FLAG_CORRUPTED) ) == 0 ) { /* Valid input block, so we can send to HW to decode */ BC_STATUS status = BC_FUNC_PSYS(DtsProcInput)( p_sys->bcm_handle, p_block->p_buffer, p_block->i_buffer, p_block->i_pts >= VLC_TS_INVALID ? TO_BC_PTS(p_block->i_pts) : 0, false ); block_Release( p_block ); *pp_block = NULL; if( status != BC_STS_SUCCESS ) return NULL; } } #ifdef DEBUG_CRYSTALHD else { if( driver_stat.ReadyListCount != 0 ) msg_Err( p_dec, " Input NULL but have pictures %u", driver_stat.ReadyListCount ); } #endif if( driver_stat.ReadyListCount == 0 ) return NULL; /* Prepare the Output structure */ /* We always expect and use YUY2 */ memset( &proc_out, 0, sizeof(BC_DTS_PROC_OUT) ); proc_out.PicInfo.width = p_dec->fmt_out.video.i_width; proc_out.PicInfo.height = p_dec->fmt_out.video.i_height; proc_out.PoutFlags = BC_POUT_FLAGS_SIZE; proc_out.AppCallBack = ourCallback; proc_out.hnd = p_dec; p_sys->proc_out = &proc_out; /* */ BC_STATUS sts = BC_FUNC_PSYS(DtsProcOutput)( p_sys->bcm_handle, 128, &proc_out ); #ifdef DEBUG_CRYSTALHD if( sts != BC_STS_SUCCESS ) msg_Err( p_dec, "DtsProcOutput returned %i", sts ); #endif uint8_t b_eos; picture_t *p_pic = p_sys->p_pic; switch( sts ) { case BC_STS_SUCCESS: if( !(proc_out.PoutFlags & BC_POUT_FLAGS_PIB_VALID) ) { msg_Dbg( p_dec, "Invalid PIB" ); break; } if( !p_pic ) break; /* In interlaced mode, do not push the first field in the pipeline */ if( (proc_out.PicInfo.flags & VDEC_FLAG_INTERLACED_SRC) && !(proc_out.PicInfo.flags & VDEC_FLAG_FIELDPAIR) ) return NULL; // crystal_CopyPicture( p_pic, &proc_out ); p_pic->date = proc_out.PicInfo.timeStamp > 0 ? FROM_BC_PTS(proc_out.PicInfo.timeStamp) : VLC_TS_INVALID; //p_pic->date += 100 * 1000; #ifdef DEBUG_CRYSTALHD msg_Dbg( p_dec, "TS Output is %"PRIu64, p_pic->date); #endif return p_pic; case BC_STS_DEC_NOT_OPEN: case BC_STS_DEC_NOT_STARTED: msg_Err( p_dec, "Decoder not opened or started" ); break; case BC_STS_INV_ARG: msg_Warn( p_dec, "Invalid arguments. Please report" ); break; case BC_STS_FMT_CHANGE: /* Format change */ /* if( !(proc_out.PoutFlags & BC_POUT_FLAGS_PIB_VALID) ) break; */ p_dec->fmt_out.video.i_width = proc_out.PicInfo.width; p_dec->fmt_out.video.i_height = proc_out.PicInfo.height; if( proc_out.PicInfo.height == 1088 ) p_dec->fmt_out.video.i_height = 1080; #define setAR( a, b, c ) case a: p_dec->fmt_out.video.i_sar_num = b; \ p_dec->fmt_out.video.i_sar_den = c; break; switch( proc_out.PicInfo.aspect_ratio ) { setAR( vdecAspectRatioSquare, 1, 1 ) setAR( vdecAspectRatio12_11, 12, 11 ) setAR( vdecAspectRatio10_11, 10, 11 ) setAR( vdecAspectRatio16_11, 16, 11 ) setAR( vdecAspectRatio40_33, 40, 33 ) setAR( vdecAspectRatio24_11, 24, 11 ) setAR( vdecAspectRatio20_11, 20, 11 ) setAR( vdecAspectRatio32_11, 32, 11 ) setAR( vdecAspectRatio80_33, 80, 33 ) setAR( vdecAspectRatio18_11, 18, 11 ) setAR( vdecAspectRatio15_11, 15, 11 ) setAR( vdecAspectRatio64_33, 64, 33 ) setAR( vdecAspectRatio160_99, 160, 99 ) setAR( vdecAspectRatio4_3, 4, 3 ) setAR( vdecAspectRatio16_9, 16, 9 ) setAR( vdecAspectRatio221_1, 221, 1 ) default: break; } #undef setAR msg_Dbg( p_dec, "Format Change Detected [%i, %i], AR: %i/%i", proc_out.PicInfo.width, proc_out.PicInfo.height, p_dec->fmt_out.video.i_sar_num, p_dec->fmt_out.video.i_sar_den ); break; /* Nothing is documented here... */ case BC_STS_NO_DATA: if( BC_FUNC_PSYS(DtsIsEndOfStream)( p_sys->bcm_handle, &b_eos ) == BC_STS_SUCCESS ) if( b_eos ) msg_Dbg( p_dec, "End of Stream" ); break; case BC_STS_TIMEOUT: /* Timeout */ msg_Err( p_dec, "ProcOutput timeout" ); break; case BC_STS_IO_XFR_ERROR: case BC_STS_IO_USER_ABORT: case BC_STS_IO_ERROR: msg_Err( p_dec, "ProcOutput return mode not implemented. Please report" ); break; default: msg_Err( p_dec, "Unknown return status. Please report %i", sts ); break; } if( p_pic ) picture_Release( p_pic ); return NULL; }
/**************************************************************************** * DecodeBlock: the whole thing **************************************************************************** * This function must be fed with a complete image. ****************************************************************************/ static int DecodeBlock( decoder_t *p_dec, block_t *p_block ) { decoder_sys_t *p_sys = (decoder_sys_t *) p_dec->p_sys; picture_t *p_pic = NULL; int32_t i_width, i_height; RsvgHandle *rsvg = NULL; cairo_surface_t *surface = NULL; cairo_t *cr = NULL; if( p_block == NULL ) /* No Drain */ return VLCDEC_SUCCESS; if( p_block->i_flags & BLOCK_FLAG_CORRUPTED) { block_Release( p_block ); return VLCDEC_SUCCESS; } rsvg = rsvg_handle_new_from_data( p_block->p_buffer, p_block->i_buffer, NULL ); if( !rsvg ) goto done; RsvgDimensionData dim; rsvg_handle_get_dimensions( rsvg, &dim ); if( p_sys->f_scale > 0.0 ) { i_width = (int32_t)(p_sys->f_scale * dim.width); i_height = (int32_t)(p_sys->f_scale * dim.height); } else { /* Keep aspect */ if( p_sys->i_width < 0 && p_sys->i_height > 0 ) { i_width = dim.width * p_sys->i_height / dim.height; i_height = p_sys->i_height; } else if( p_sys->i_width > 0 && p_sys->i_height < 0 ) { i_width = p_sys->i_width; i_height = dim.height * p_sys->i_width / dim.height; } else if( p_sys->i_width > 0 && p_sys->i_height > 0 ) { i_width = dim.width * p_sys->i_height / dim.height; i_height = p_sys->i_height; } else { i_width = dim.width; i_height = dim.height; } } p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma = VLC_CODEC_BGRA; p_dec->fmt_out.video.i_width = i_width; p_dec->fmt_out.video.i_height = i_height; p_dec->fmt_out.video.i_visible_width = i_width; p_dec->fmt_out.video.i_visible_height = i_height; p_dec->fmt_out.video.i_sar_num = 1; p_dec->fmt_out.video.i_sar_den = 1; p_dec->fmt_out.video.i_rmask = 0x80800000; /* Since librsvg v1.0 */ p_dec->fmt_out.video.i_gmask = 0x0000ff00; p_dec->fmt_out.video.i_bmask = 0x000000ff; video_format_FixRgb(&p_dec->fmt_out.video); /* Get a new picture */ if( decoder_UpdateVideoFormat( p_dec ) ) goto done; p_pic = decoder_NewPicture( p_dec ); if( !p_pic ) goto done; /* NOTE: Do not use the stride calculation from cairo, because it is wrong: * stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, dim.width); * Use the stride from VLC its picture_t::p[0].i_pitch, which is correct. */ memset(p_pic->p[0].p_pixels, 0, p_pic->p[0].i_pitch * p_pic->p[0].i_lines); surface = cairo_image_surface_create_for_data( p_pic->p->p_pixels, CAIRO_FORMAT_ARGB32, i_width, i_height, p_pic->p[0].i_pitch ); if( !surface ) { picture_Release( p_pic ); p_pic = NULL; goto done; } /* Decode picture */ cr = cairo_create( surface ); if( !cr ) { picture_Release( p_pic ); p_pic = NULL; goto done; } if ( i_width != dim.width || i_height != dim.height ) { double sw, sh; if ( p_sys->f_scale > 0.0 && !(p_sys->i_width > 0 || p_sys->i_height > 0) ) sw = sh = p_sys->f_scale; else { double aspect = (double) (dim.width * p_dec->fmt_out.video.i_sar_num) / (dim.height * p_dec->fmt_out.video.i_sar_den); sw = aspect * i_width / dim.width; sh = aspect * i_height / dim.height; } cairo_scale(cr, sw, sh); } if( !rsvg_handle_render_cairo( rsvg, cr ) ) { picture_Release( p_pic ); p_pic = NULL; goto done; } p_pic->date = p_block->i_pts != VLC_TICK_INVALID ? p_block->i_pts : p_block->i_dts; done: if( rsvg ) g_object_unref( G_OBJECT( rsvg ) ); if( cr ) cairo_destroy( cr ); if( surface ) cairo_surface_destroy( surface ); block_Release( p_block ); if( p_pic != NULL ) decoder_QueueVideo( p_dec, p_pic ); return VLCDEC_SUCCESS; }
/* * EncodeBlock */ static block_t *EncodeBlock(encoder_t *p_enc, picture_t *p_pic) { encoder_sys_t *p_sys = p_enc->p_sys; if( unlikely( !p_pic ) ) { return NULL; } block_t *p_block = block_Alloc( p_sys->i_blocksize ); if( p_block == NULL ) { return NULL; } png_structp p_png = png_create_write_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 ); if( p_png == NULL ) { block_Release( p_block ); return NULL; } /* save buffer start */ uint8_t *p_start = p_block->p_buffer; size_t i_start = p_block->i_buffer; p_sys->b_error = false; png_infop p_info = NULL; /* libpng longjmp's there in case of error */ if( setjmp( png_jmpbuf( p_png ) ) ) goto error; png_set_write_fn( p_png, p_block, user_write, user_flush ); png_set_error_fn( p_png, p_enc, user_error, user_warning ); p_info = png_create_info_struct( p_png ); if( p_info == NULL ) goto error; png_infop p_end_info = png_create_info_struct( p_png ); if( p_end_info == NULL ) goto error; const unsigned i_width = p_enc->fmt_in.video.i_visible_width; const unsigned i_height = p_enc->fmt_in.video.i_visible_height; png_set_IHDR( p_png, p_info, i_width, i_height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); if( p_sys->b_error ) goto error; png_write_info( p_png, p_info ); if( p_sys->b_error ) goto error; /* Encode picture */ for( unsigned i = 0; i < i_height; i++ ) { png_write_row( p_png, p_pic->p->p_pixels + (i_width * i * 3)); if( p_sys->b_error ) goto error; } png_write_end( p_png, p_end_info ); if( p_sys->b_error ) goto error; png_destroy_write_struct( &p_png, &p_info ); /* restore original buffer position */ p_block->p_buffer = p_start; p_block->i_buffer = i_start - p_block->i_buffer; p_block->i_dts = p_block->i_pts = p_pic->date; return p_block; error: png_destroy_write_struct( &p_png, p_info ? &p_info : NULL ); block_Release(p_block); return NULL; }
/***************************************************************************** * ProcessPacket: processes a Speex packet. *****************************************************************************/ static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_block = *pp_block; if( p_block && p_block->i_flags & BLOCK_FLAG_DISCONTINUITY ) Flush( p_dec ); /* Date management */ if( p_block && p_block->i_pts > VLC_TS_INVALID && p_block->i_pts != date_Get( &p_sys->end_date ) ) { date_Set( &p_sys->end_date, p_block->i_pts ); } if( !date_Get( &p_sys->end_date ) ) { /* We've just started the stream, wait for the first PTS. */ if( p_block ) block_Release( p_block ); return NULL; } *pp_block = NULL; /* To avoid being fed the same packet again */ if( p_sys->b_packetizer ) { if ( p_sys->p_header->frames_per_packet > 1 ) { short *p_frame_holder = NULL; int i_bits_before = 0, i_bits_after = 0, i_bytes_in_speex_frame = 0, i_pcm_output_size = 0, i_bits_in_speex_frame = 0; block_t *p_new_block = NULL; i_pcm_output_size = p_sys->p_header->frame_size; p_frame_holder = (short*)xmalloc( sizeof(short)*i_pcm_output_size ); speex_bits_read_from( &p_sys->bits, (char*)p_oggpacket->packet, p_oggpacket->bytes); i_bits_before = speex_bits_remaining( &p_sys->bits ); speex_decode_int(p_sys->p_state, &p_sys->bits, p_frame_holder); i_bits_after = speex_bits_remaining( &p_sys->bits ); i_bits_in_speex_frame = i_bits_before - i_bits_after; i_bytes_in_speex_frame = ( i_bits_in_speex_frame + (8 - (i_bits_in_speex_frame % 8)) ) / 8; p_new_block = block_Alloc( i_bytes_in_speex_frame ); memset( p_new_block->p_buffer, 0xff, i_bytes_in_speex_frame ); /* * Copy the first frame in this packet to a new packet. */ speex_bits_rewind( &p_sys->bits ); speex_bits_write( &p_sys->bits, (char*)p_new_block->p_buffer, (int)i_bytes_in_speex_frame ); /* * Move the remaining part of the original packet (subsequent * frames, if there are any) into the beginning * of the original packet so * they are preserved following the realloc. * Note: Any bits that * remain in the initial packet * are "filler" if they do not constitute * an entire byte. */ if ( i_bits_after > 7 ) { /* round-down since we rounded-up earlier (to include * the speex terminator code. */ i_bytes_in_speex_frame--; speex_bits_write( &p_sys->bits, (char*)p_block->p_buffer, p_block->i_buffer - i_bytes_in_speex_frame ); p_block = block_Realloc( p_block, 0, p_block->i_buffer-i_bytes_in_speex_frame ); *pp_block = p_block; } else { speex_bits_reset( &p_sys->bits ); } free( p_frame_holder ); return SendPacket( p_dec, p_new_block); } else { return SendPacket( p_dec, p_block ); } } else { block_t *p_aout_buffer = DecodePacket( p_dec, p_oggpacket ); if( p_block ) block_Release( p_block ); return p_aout_buffer; } }
/***************************************************************************** * Process: Process and dispatch buffers *****************************************************************************/ static mtime_t Process( sout_stream_t *p_stream, sout_stream_id_t *id, mtime_t i_max_dts ) { sout_stream_sys_t *p_sys = p_stream->p_sys; mtime_t i_dts = 0; block_t *p_blocks = NULL; block_t *p_blocks_out = NULL; /* Find out the blocks we need. */ while ( id->p_queued != NULL && (!i_max_dts || id->p_queued->i_dts <= i_max_dts) ) { block_t *p_next = id->p_queued->p_next; id->p_queued->p_next = NULL; i_dts = id->p_queued->i_dts; block_ChainAppend( &p_blocks, id->p_queued ); id->p_queued = p_next; } if ( p_sys->i_old_cmd == 0 ) { /* Full forward */ if ( p_blocks != NULL ) p_sys->p_out->pf_send( p_sys->p_out, id->id, p_blocks ); return i_dts; } if ( p_sys->i_old_cmd == -1 ) { /* No output at all */ while ( p_blocks != NULL ) { block_t * p_next = p_blocks->p_next; block_Release( p_blocks ); p_blocks = p_next; } return i_dts; } while ( p_blocks != NULL ) { block_t * p_next = p_blocks->p_next; block_t * p_out; if ( id->b_switcher_video ) { p_out = VideoGetBuffer( p_stream, id, p_blocks ); } else { p_out = AudioGetBuffer( p_stream, id, p_blocks ); } p_blocks = p_next; if ( p_out != NULL ) { block_ChainAppend( &p_blocks_out, p_out ); } } if ( p_blocks_out != NULL ) p_sys->p_out->pf_send( p_sys->p_out, id->id, p_blocks_out ); return i_dts; }
static aout_buffer_t *DecodeBlock (decoder_t *p_dec, block_t **pp_block) { block_t *p_block; decoder_sys_t *p_sys = p_dec->p_sys; aout_buffer_t *p_out = NULL; if (pp_block == NULL) return NULL; p_block = *pp_block; if (p_block == NULL) return NULL; *pp_block = NULL; if (p_block->i_pts && !date_Get (&p_sys->end_date)) date_Set (&p_sys->end_date, p_block->i_pts); else if (p_block->i_pts < date_Get (&p_sys->end_date)) { msg_Warn (p_dec, "MIDI message in the past?"); goto drop; } if (p_block->i_buffer < 1) goto drop; uint8_t channel = p_block->p_buffer[0] & 0xf; uint8_t p1 = (p_block->i_buffer > 1) ? (p_block->p_buffer[1] & 0x7f) : 0; uint8_t p2 = (p_block->i_buffer > 2) ? (p_block->p_buffer[2] & 0x7f) : 0; switch (p_block->p_buffer[0] & 0xf0) { case 0x80: fluid_synth_noteoff (p_sys->synth, channel, p1); break; case 0x90: fluid_synth_noteon (p_sys->synth, channel, p1, p2); break; case 0xB0: fluid_synth_cc (p_sys->synth, channel, p1, p2); break; case 0xC0: fluid_synth_program_change (p_sys->synth, channel, p1); break; case 0xE0: fluid_synth_pitch_bend (p_sys->synth, channel, (p1 << 7) | p2); break; } unsigned samples = (p_block->i_pts - date_Get (&p_sys->end_date)) * 441 / 10000; if (samples == 0) return NULL; p_out = decoder_NewAudioBuffer (p_dec, samples); if (p_out == NULL) goto drop; p_out->i_pts = date_Get (&p_sys->end_date ); p_out->i_length = date_Increment (&p_sys->end_date, samples) - p_out->i_pts; if (!p_sys->fixed) fluid_synth_write_float (p_sys->synth, samples, p_out->p_buffer, 0, 2, p_out->p_buffer, 1, 2); else fluid_synth_write_s16 (p_sys->synth, samples, (int16_t *)p_out->p_buffer, 0, 2, (int16_t *)p_out->p_buffer, 1, 2); drop: block_Release (p_block); return p_out; }
static void audio_del_buffer( decoder_t *p_dec, aout_buffer_t *p_buffer ) { VLC_UNUSED(p_dec); block_Release( p_buffer ); }
/***************************************************************************** * DecodeAudio: Called to decode one frame *****************************************************************************/ aout_buffer_t * DecodeAudio ( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; int i_used, i_output; aout_buffer_t *p_buffer; block_t *p_block; AVPacket pkt; if( !pp_block || !*pp_block ) return NULL; p_block = *pp_block; if( !p_sys->p_context->extradata_size && p_dec->fmt_in.i_extra && p_sys->b_delayed_open) { InitDecoderConfig( p_dec, p_sys->p_context); if( ffmpeg_OpenCodec( p_dec ) ) msg_Err( p_dec, "Cannot open decoder %s", p_sys->psz_namecodec ); } if( p_sys->b_delayed_open ) { block_Release( p_block ); return NULL; } if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { block_Release( p_block ); avcodec_flush_buffers( p_sys->p_context ); p_sys->i_samples = 0; date_Set( &p_sys->end_date, 0 ); if( p_sys->i_codec_id == CODEC_ID_MP2 || p_sys->i_codec_id == CODEC_ID_MP3 ) p_sys->i_reject_count = 3; return NULL; } if( p_sys->i_samples > 0 ) { /* More data */ p_buffer = SplitBuffer( p_dec ); if( !p_buffer ) block_Release( p_block ); return p_buffer; } if( !date_Get( &p_sys->end_date ) && !p_block->i_pts ) { /* We've just started the stream, wait for the first PTS. */ block_Release( p_block ); return NULL; } if( p_block->i_buffer <= 0 ) { block_Release( p_block ); return NULL; } if( (p_block->i_flags & BLOCK_FLAG_PRIVATE_REALLOCATED) == 0 ) { *pp_block = p_block = block_Realloc( p_block, 0, p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE ); if( !p_block ) return NULL; p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE; memset( &p_block->p_buffer[p_block->i_buffer], 0, FF_INPUT_BUFFER_PADDING_SIZE ); p_block->i_flags |= BLOCK_FLAG_PRIVATE_REALLOCATED; } do { i_output = __MAX( p_block->i_buffer, p_sys->i_output_max ); if( i_output > p_sys->i_output_max ) { /* Grow output buffer if necessary (eg. for PCM data) */ p_sys->p_output = av_realloc( p_sys->p_output, i_output ); } av_init_packet( &pkt ); pkt.data = p_block->p_buffer; pkt.size = p_block->i_buffer; i_used = avcodec_decode_audio3( p_sys->p_context, (int16_t*)p_sys->p_output, &i_output, &pkt ); if( i_used < 0 || i_output < 0 ) { if( i_used < 0 ) msg_Warn( p_dec, "cannot decode one frame (%zu bytes)", p_block->i_buffer ); block_Release( p_block ); return NULL; } else if( (size_t)i_used > p_block->i_buffer ) { i_used = p_block->i_buffer; } p_block->i_buffer -= i_used; p_block->p_buffer += i_used; } while( p_block->i_buffer > 0 && i_output <= 0 ); if( p_sys->p_context->channels <= 0 || p_sys->p_context->channels > 8 || p_sys->p_context->sample_rate <= 0 ) { msg_Warn( p_dec, "invalid audio properties channels count %d, sample rate %d", p_sys->p_context->channels, p_sys->p_context->sample_rate ); block_Release( p_block ); return NULL; } if( p_dec->fmt_out.audio.i_rate != (unsigned int)p_sys->p_context->sample_rate ) { date_Init( &p_sys->end_date, p_sys->p_context->sample_rate, 1 ); date_Set( &p_sys->end_date, p_block->i_pts ); } /* **** Set audio output parameters **** */ SetupOutputFormat( p_dec, true ); if( p_block->i_pts != 0 && p_block->i_pts != date_Get( &p_sys->end_date ) ) { date_Set( &p_sys->end_date, p_block->i_pts ); } p_block->i_pts = 0; /* **** Now we can output these samples **** */ p_sys->i_samples = i_output / (p_dec->fmt_out.audio.i_bitspersample / 8) / p_sys->p_context->channels; p_sys->p_samples = p_sys->p_output; /* Silent unwanted samples */ if( p_sys->i_reject_count > 0 ) { memset( p_sys->p_output, 0, i_output ); p_sys->i_reject_count--; } p_buffer = SplitBuffer( p_dec ); if( !p_buffer ) block_Release( p_block ); return p_buffer; }
/***************************************************************************** * Block: *****************************************************************************/ static block_t *Block( access_t *p_access ) { access_sys_t *p_sys = p_access->p_sys; int i_blocks = VCD_BLOCKS_ONCE; block_t *p_block; int i_read; /* Check end of file */ if( p_access->info.b_eof ) return NULL; /* Check end of title */ while( p_sys->i_sector >= p_sys->p_sectors[p_access->info.i_title + 2] ) { if( p_access->info.i_title + 2 >= p_sys->i_titles ) { p_access->info.b_eof = true; return NULL; } p_access->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT | INPUT_UPDATE_SIZE; p_access->info.i_title++; p_access->info.i_seekpoint = 0; p_access->info.i_size = p_sys->title[p_access->info.i_title]->i_size; p_access->info.i_pos = 0; } /* Don't read after the end of a title */ if( p_sys->i_sector + i_blocks >= p_sys->p_sectors[p_access->info.i_title + 2] ) { i_blocks = p_sys->p_sectors[p_access->info.i_title + 2 ] - p_sys->i_sector; } /* Do the actual reading */ if( !( p_block = block_New( p_access, i_blocks * VCD_DATA_SIZE ) ) ) { msg_Err( p_access, "cannot get a new block of size: %i", i_blocks * VCD_DATA_SIZE ); return NULL; } if( ioctl_ReadSectors( VLC_OBJECT(p_access), p_sys->vcddev, p_sys->i_sector, p_block->p_buffer, i_blocks, VCD_TYPE ) < 0 ) { msg_Err( p_access, "cannot read sector %i", p_sys->i_sector ); block_Release( p_block ); /* Try to skip one sector (in case of bad sectors) */ p_sys->i_sector++; p_access->info.i_pos += VCD_DATA_SIZE; return NULL; } /* Update seekpoints */ for( i_read = 0; i_read < i_blocks; i_read++ ) { input_title_t *t = p_sys->title[p_access->info.i_title]; if( t->i_seekpoint > 0 && p_access->info.i_seekpoint + 1 < t->i_seekpoint && p_access->info.i_pos + i_read * VCD_DATA_SIZE >= t->seekpoint[p_access->info.i_seekpoint+1]->i_byte_offset ) { msg_Dbg( p_access, "seekpoint change" ); p_access->info.i_update |= INPUT_UPDATE_SEEKPOINT; p_access->info.i_seekpoint++; } } /* Update a few values */ p_sys->i_sector += i_blocks; p_access->info.i_pos += p_block->i_buffer; return p_block; }
/***************************************************************************** * ParseMPEGBlock: Re-assemble fragments into a block containing a picture *****************************************************************************/ static block_t *ParseMPEGBlock( decoder_t *p_dec, block_t *p_frag ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_pic = NULL; /* * Check if previous picture is finished */ if( ( p_sys->b_frame_slice && (p_frag->p_buffer[3] == 0x00 || p_frag->p_buffer[3] > 0xaf) ) && p_sys->p_seq == NULL ) { /* We have a picture but without a sequence header we can't * do anything */ msg_Dbg( p_dec, "waiting for sequence start" ); if( p_sys->p_frame ) block_ChainRelease( p_sys->p_frame ); p_sys->p_frame = NULL; p_sys->pp_last = &p_sys->p_frame; p_sys->b_frame_slice = false; } else if( p_sys->b_frame_slice && (p_frag->p_buffer[3] == 0x00 || p_frag->p_buffer[3] > 0xaf) ) { const bool b_eos = p_frag->p_buffer[3] == 0xb7; mtime_t i_duration; if( b_eos ) { block_ChainLastAppend( &p_sys->pp_last, p_frag ); p_frag = NULL; } p_pic = block_ChainGather( p_sys->p_frame ); if( b_eos ) p_pic->i_flags |= BLOCK_FLAG_END_OF_SEQUENCE; i_duration = (mtime_t)( 1000000 * p_sys->i_frame_rate_base / p_sys->i_frame_rate ); if( !p_sys->b_seq_progressive && p_sys->i_picture_structure != 0x03 ) { i_duration /= 2; } if( p_sys->b_seq_progressive ) { if( p_sys->i_top_field_first == 0 && p_sys->i_repeat_first_field == 1 ) { i_duration *= 2; } else if( p_sys->i_top_field_first == 1 && p_sys->i_repeat_first_field == 1 ) { i_duration *= 3; } } else { if( p_sys->i_picture_structure == 0x03 ) { if( p_sys->i_progressive_frame && p_sys->i_repeat_first_field ) { i_duration += i_duration / 2; } } } if( p_sys->b_low_delay || p_sys->i_picture_type == 0x03 ) { /* Trivial case (DTS == PTS) */ /* Correct interpolated dts when we receive a new pts/dts */ if( p_sys->i_pts > VLC_TS_INVALID ) p_sys->i_interpolated_dts = p_sys->i_pts; if( p_sys->i_dts > VLC_TS_INVALID ) p_sys->i_interpolated_dts = p_sys->i_dts; } else { /* Correct interpolated dts when we receive a new pts/dts */ if(p_sys->i_last_ref_pts > VLC_TS_INVALID && !p_sys->b_second_field) p_sys->i_interpolated_dts = p_sys->i_last_ref_pts; if( p_sys->i_dts > VLC_TS_INVALID ) p_sys->i_interpolated_dts = p_sys->i_dts; if( !p_sys->b_second_field ) p_sys->i_last_ref_pts = p_sys->i_pts; } p_pic->i_dts = p_sys->i_interpolated_dts; p_sys->i_interpolated_dts += i_duration; /* Set PTS only if we have a B frame or if it comes from the stream */ if( p_sys->i_pts > VLC_TS_INVALID ) { p_pic->i_pts = p_sys->i_pts; } else if( p_sys->i_picture_type == 0x03 ) { p_pic->i_pts = p_pic->i_dts; } else { p_pic->i_pts = VLC_TS_INVALID; } switch ( p_sys->i_picture_type ) { case 0x01: p_pic->i_flags |= BLOCK_FLAG_TYPE_I; break; case 0x02: p_pic->i_flags |= BLOCK_FLAG_TYPE_P; break; case 0x03: p_pic->i_flags |= BLOCK_FLAG_TYPE_B; break; } p_pic->i_length = p_sys->i_interpolated_dts - p_pic->i_dts; #if 0 msg_Dbg( p_dec, "pic: type=%d dts=%"PRId64" pts-dts=%"PRId64, p_sys->i_picture_type, p_pic->i_dts, p_pic->i_pts - p_pic->i_dts); #endif /* Reset context */ p_sys->p_frame = NULL; p_sys->pp_last = &p_sys->p_frame; p_sys->b_frame_slice = false; if( p_sys->i_picture_structure != 0x03 ) { p_sys->b_second_field = !p_sys->b_second_field; } else { p_sys->b_second_field = 0; } /* CC */ p_sys->b_cc_reset = true; p_sys->i_cc_pts = p_pic->i_pts; p_sys->i_cc_dts = p_pic->i_dts; p_sys->i_cc_flags = p_pic->i_flags; } if( !p_pic && p_sys->b_cc_reset ) { p_sys->b_cc_reset = false; cc_Flush( &p_sys->cc ); } if( !p_frag ) return p_pic; /* * Check info of current fragment */ if( p_frag->p_buffer[3] == 0xb8 ) { /* Group start code */ if( p_sys->p_seq && p_sys->i_seq_old > p_sys->i_frame_rate/p_sys->i_frame_rate_base ) { /* Useful for mpeg1: repeat sequence header every second */ block_ChainLastAppend( &p_sys->pp_last, block_Duplicate( p_sys->p_seq ) ); if( p_sys->p_ext ) { block_ChainLastAppend( &p_sys->pp_last, block_Duplicate( p_sys->p_ext ) ); } p_sys->i_seq_old = 0; } } else if( p_frag->p_buffer[3] == 0xb3 && p_frag->i_buffer >= 8 ) { /* Sequence header code */ static const int code_to_frame_rate[16][2] = { { 1, 1 }, /* invalid */ { 24000, 1001 }, { 24, 1 }, { 25, 1 }, { 30000, 1001 }, { 30, 1 }, { 50, 1 }, { 60000, 1001 }, { 60, 1 }, /* Unofficial 15fps from Xing*/ { 15, 1001 }, /* Unofficial economy rates from libmpeg3 */ { 5000, 1001 }, { 1000, 1001 }, { 12000, 1001 }, { 15000, 1001 }, { 1, 1 }, { 1, 1 } /* invalid */ }; if( p_sys->p_seq ) block_Release( p_sys->p_seq ); if( p_sys->p_ext ) block_Release( p_sys->p_ext ); p_sys->p_seq = block_Duplicate( p_frag ); p_sys->i_seq_old = 0; p_sys->p_ext = NULL; p_dec->fmt_out.video.i_width = ( p_frag->p_buffer[4] << 4)|(p_frag->p_buffer[5] >> 4 ); p_dec->fmt_out.video.i_height = ( (p_frag->p_buffer[5]&0x0f) << 8 )|p_frag->p_buffer[6]; p_sys->i_aspect_ratio_info = p_frag->p_buffer[7] >> 4; /* TODO: MPEG1 aspect ratio */ p_sys->i_frame_rate = code_to_frame_rate[p_frag->p_buffer[7]&0x0f][0]; p_sys->i_frame_rate_base = code_to_frame_rate[p_frag->p_buffer[7]&0x0f][1]; p_dec->fmt_out.video.i_frame_rate = p_sys->i_frame_rate; p_dec->fmt_out.video.i_frame_rate_base = p_sys->i_frame_rate_base; p_sys->b_seq_progressive = true; p_sys->b_low_delay = true; if ( !p_sys->b_inited ) { msg_Dbg( p_dec, "size %dx%d fps=%.3f", p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height, p_sys->i_frame_rate / (float)p_sys->i_frame_rate_base ); p_sys->b_inited = 1; } } else if( p_frag->p_buffer[3] == 0xb5 )
/***************************************************************************** * Demux: *****************************************************************************/ static int Demux( demux_t *p_demux ) { demux_sys_t *p_sys = p_demux->p_sys; int i_ret, i_mux_rate; block_t *p_pkt; i_ret = ps_pkt_resynch( p_demux->s, p_sys->format, p_sys->b_have_pack ); if( i_ret < 0 ) { return VLC_DEMUXER_EOF; } else if( i_ret == 0 ) { if( !p_sys->b_lost_sync ) { msg_Warn( p_demux, "garbage at input from %"PRIu64", trying to resync...", vlc_stream_Tell(p_demux->s) ); NotifyDiscontinuity( p_sys->tk, p_demux->out ); } p_sys->b_lost_sync = true; return VLC_DEMUXER_SUCCESS; } if( p_sys->b_lost_sync ) msg_Warn( p_demux, "found sync code" ); p_sys->b_lost_sync = false; if( p_sys->i_length == VLC_TICK_INVALID && p_sys->b_seekable ) { if( !FindLength( p_demux ) ) return VLC_DEMUXER_EGENERIC; } if( ( p_pkt = ps_pkt_read( p_demux->s ) ) == NULL ) { return VLC_DEMUXER_EOF; } if( p_pkt->i_buffer < 4 ) { block_Release( p_pkt ); return VLC_DEMUXER_EGENERIC; } const uint8_t i_stream_id = p_pkt->p_buffer[3]; switch( i_stream_id ) { case PS_STREAM_ID_END_STREAM: block_Release( p_pkt ); break; case PS_STREAM_ID_PACK_HEADER: if( !ps_pkt_parse_pack( p_pkt, &p_sys->i_pack_scr, &i_mux_rate ) ) { if( p_sys->i_first_scr == VLC_TICK_INVALID ) p_sys->i_first_scr = p_sys->i_pack_scr; CheckPCR( p_sys, p_demux->out, p_sys->i_pack_scr ); p_sys->i_scr = p_sys->i_pack_scr; p_sys->i_lastpack_byte = vlc_stream_Tell( p_demux->s ); if( !p_sys->b_have_pack ) p_sys->b_have_pack = true; /* done later on to work around bad vcd/svcd streams */ /* es_out_SetPCR( p_demux->out, p_sys->i_scr ); */ if( i_mux_rate > 0 ) p_sys->i_mux_rate = i_mux_rate; } block_Release( p_pkt ); break; case PS_STREAM_ID_SYSTEM_HEADER: if( !ps_pkt_parse_system( p_pkt, &p_sys->psm, p_sys->tk ) ) { int i; for( i = 0; i < PS_TK_COUNT; i++ ) { ps_track_t *tk = &p_sys->tk[i]; if( !tk->b_configured && tk->fmt.i_cat != UNKNOWN_ES ) { if( tk->b_seen ) tk->es = es_out_Add( p_demux->out, &tk->fmt ); /* else create when seeing packet */ tk->b_configured = true; } } } block_Release( p_pkt ); break; case PS_STREAM_ID_MAP: if( p_sys->psm.i_version == 0xFFFF ) msg_Dbg( p_demux, "contains a PSM"); ps_psm_fill( &p_sys->psm, p_pkt, p_sys->tk, p_demux->out ); block_Release( p_pkt ); break; default: /* Reject non video/audio nor PES */ if( i_stream_id < 0xC0 || i_stream_id > 0xEF ) { block_Release( p_pkt ); break; } /* fallthrough */ case PS_STREAM_ID_PRIVATE_STREAM1: case PS_STREAM_ID_EXTENDED: { int i_id = ps_pkt_id( p_pkt ); /* Small heuristic to improve MLP detection from AOB */ if( i_id == 0xa001 && p_sys->i_aob_mlp_count < 500 ) { p_sys->i_aob_mlp_count++; } else if( i_id == 0xbda1 && p_sys->i_aob_mlp_count > 0 ) { p_sys->i_aob_mlp_count--; i_id = 0xa001; } bool b_new = false; ps_track_t *tk = &p_sys->tk[ps_id_to_tk(i_id)]; if( !tk->b_configured ) { if( !ps_track_fill( tk, &p_sys->psm, i_id, p_pkt, false ) ) { /* No PSM and no probing yet */ if( p_sys->format == PSMF_PS ) { if( tk->fmt.i_cat == VIDEO_ES ) tk->fmt.i_codec = VLC_CODEC_H264; #if 0 if( i_stream_id == PS_STREAM_ID_PRIVATE_STREAM1 ) { es_format_Change( &tk->fmt, AUDIO_ES, VLC_CODEC_ATRAC3P ); tk->fmt.audio.i_blockalign = 376; tk->fmt.audio.i_channels = 2; tk->fmt.audio.i_rate = 44100; } #endif } tk->es = es_out_Add( p_demux->out, &tk->fmt ); b_new = true; tk->b_configured = true; } else { msg_Dbg( p_demux, "es id=0x%x format unknown", i_id ); } } /* Late creation from system header */ if( !tk->b_seen && tk->b_configured && !tk->es && tk->fmt.i_cat != UNKNOWN_ES ) tk->es = es_out_Add( p_demux->out, &tk->fmt ); tk->b_seen = true; /* The popular VCD/SVCD subtitling WinSubMux does not * renumber the SCRs when merging subtitles into the PES */ if( tk->b_seen && !p_sys->b_bad_scr && ( tk->fmt.i_codec == VLC_CODEC_OGT || tk->fmt.i_codec == VLC_CODEC_CVD ) ) { p_sys->b_bad_scr = true; p_sys->i_first_scr = VLC_TICK_INVALID; } if( p_sys->i_pack_scr != VLC_TICK_INVALID && !p_sys->b_bad_scr ) { if( (tk->fmt.i_cat == AUDIO_ES || tk->fmt.i_cat == VIDEO_ES) && tk->i_first_pts != VLC_TICK_INVALID && tk->i_first_pts - p_sys->i_pack_scr > VLC_TICK_FROM_SEC(2)) { msg_Warn( p_demux, "Incorrect SCR timing offset by of %"PRId64 "ms, disabling", MS_FROM_VLC_TICK(tk->i_first_pts - p_sys->i_pack_scr) ); p_sys->b_bad_scr = true; /* Disable Offset SCR */ p_sys->i_first_scr = VLC_TICK_INVALID; } else es_out_SetPCR( p_demux->out, p_sys->i_pack_scr ); } if( tk->b_configured && tk->es && !ps_pkt_parse_pes( VLC_OBJECT(p_demux), p_pkt, tk->i_skip ) ) { if( tk->fmt.i_cat == AUDIO_ES || tk->fmt.i_cat == VIDEO_ES ) { if( !p_sys->b_bad_scr && p_sys->i_pack_scr != VLC_TICK_INVALID && p_pkt->i_pts != VLC_TICK_INVALID && p_sys->i_pack_scr > p_pkt->i_pts + VLC_TICK_FROM_MS(250) ) { msg_Warn( p_demux, "Incorrect SCR timing in advance of %" PRId64 "ms, disabling", MS_FROM_VLC_TICK(p_sys->i_pack_scr - p_pkt->i_pts) ); p_sys->b_bad_scr = true; p_sys->i_first_scr = VLC_TICK_INVALID; } if( (p_sys->b_bad_scr || !p_sys->b_have_pack) && !p_sys->i_scr_track_id ) { p_sys->i_scr_track_id = tk->i_id; } } if( ((!b_new && !p_sys->b_have_pack) || p_sys->b_bad_scr) && p_sys->i_scr_track_id == tk->i_id && p_pkt->i_pts != VLC_TICK_INVALID ) { /* A hack to sync the A/V on PES files. */ msg_Dbg( p_demux, "force SCR: %"PRId64, p_pkt->i_pts ); CheckPCR( p_sys, p_demux->out, p_pkt->i_pts ); p_sys->i_scr = p_pkt->i_pts; if( p_sys->i_first_scr == VLC_TICK_INVALID ) p_sys->i_first_scr = p_sys->i_scr; es_out_SetPCR( p_demux->out, p_pkt->i_pts ); } if( tk->fmt.i_codec == VLC_CODEC_TELETEXT && p_pkt->i_pts == VLC_TICK_INVALID && p_sys->i_scr != VLC_TICK_INVALID ) { /* Teletext may have missing PTS (ETSI EN 300 472 Annexe A) * In this case use the last SCR + 40ms */ p_pkt->i_pts = p_sys->i_scr + VLC_TICK_FROM_MS(40); } if( p_pkt->i_pts > p_sys->i_current_pts ) { p_sys->i_current_pts = p_pkt->i_pts; } if( tk->i_next_block_flags ) { p_pkt->i_flags = tk->i_next_block_flags; tk->i_next_block_flags = 0; } #if 0 if( tk->fmt.i_codec == VLC_CODEC_ATRAC3P ) { p_pkt->p_buffer += 14; p_pkt->i_buffer -= 14; } #endif es_out_Send( p_demux->out, tk->es, p_pkt ); } else { block_Release( p_pkt ); } p_sys->i_pack_scr = VLC_TICK_INVALID; } break; } demux_UpdateTitleFromStream( p_demux ); return VLC_DEMUXER_SUCCESS; }
block_t *DirBlock (access_t *p_access) { access_sys_t *p_sys = p_access->p_sys; directory_t *current = p_sys->current; if (p_access->info.b_eof) return NULL; if (current == NULL) { /* Startup: send the XSPF header */ static const char header[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n" " <trackList>\n"; block_t *block = block_Alloc (sizeof (header) - 1); if (!block) goto fatal; memcpy (block->p_buffer, header, sizeof (header) - 1); /* "Open" the base directory */ current = malloc (sizeof (*current)); if (current == NULL) { block_Release (block); goto fatal; } current->parent = NULL; current->handle = p_sys->handle; #ifndef HAVE_OPENAT current->path = strdup (p_access->psz_filepath); #endif current->uri = p_sys->uri; if (fstat (dirfd (current->handle), ¤t->st)) { free (current); block_Release (block); goto fatal; } p_sys->handle = NULL; p_sys->uri = NULL; p_sys->current = current; return block; } char *entry = vlc_readdir (current->handle); if (entry == NULL) { /* End of directory, go back to parent */ closedir (current->handle); p_sys->current = current->parent; free (current->uri); #ifndef HAVE_OPENAT free (current->path); #endif free (current); if (p_sys->current == NULL) { /* End of XSPF playlist */ char *footer; int len = asprintf( &footer, " </trackList>\n" \ " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" \ "%s" \ " </extension>\n" \ "</playlist>\n", p_sys->psz_xspf_extension ); if (unlikely(len == -1)) goto fatal; block_t *block = block_heap_Alloc (footer, footer, len); if (unlikely(block == NULL)) free (footer); p_access->info.b_eof = true; return block; } else { /* This was the end of a "subnode" */ /* Write the ID to the extension */ char *old_xspf_extension = p_sys->psz_xspf_extension; if (old_xspf_extension == NULL) goto fatal; int len2 = asprintf( &p_sys->psz_xspf_extension, "%s </vlc:node>\n", old_xspf_extension ); if (len2 == -1) goto fatal; free( old_xspf_extension ); } return NULL; } /* Skip current, parent and hidden directories */ if (entry[0] == '.') { free (entry); return NULL; } /* Handle recursion */ if (p_sys->mode != MODE_COLLAPSE) { directory_t *sub = malloc (sizeof (*sub)); if (sub == NULL) { free (entry); return NULL; } DIR *handle; #ifdef HAVE_OPENAT int fd = vlc_openat (dirfd (current->handle), entry, O_RDONLY); if (fd != -1) { handle = fdopendir (fd); if (handle == NULL) close (fd); } else handle = NULL; #else if (asprintf (&sub->path, "%s/%s", current->path, entry) != -1) handle = vlc_opendir (sub->path); else handle = NULL; #endif if (handle != NULL) { sub->parent = current; sub->handle = handle; char *encoded = encode_URI_component (entry); if ((encoded == NULL) || (asprintf (&sub->uri, "%s/%s", current->uri, encoded) == -1)) sub->uri = NULL; free (encoded); if ((p_sys->mode == MODE_NONE) || fstat (dirfd (handle), &sub->st) || has_inode_loop (sub) || (sub->uri == NULL)) { free (entry); closedir (handle); free (sub->uri); free (sub); return NULL; } p_sys->current = sub; /* Add node to xspf extension */ char *old_xspf_extension = p_sys->psz_xspf_extension; if (old_xspf_extension == NULL) { free (entry); goto fatal; } char *title = convert_xml_special_chars (entry); free (entry); if (title == NULL || asprintf (&p_sys->psz_xspf_extension, "%s" " <vlc:node title=\"%s\">\n", old_xspf_extension, title) == -1) { free (title); goto fatal; } free (title); free (old_xspf_extension); return NULL; } else free (sub); } /* Skip files with ignored extensions */ if (p_sys->ignored_exts != NULL) { const char *ext = strrchr (entry, '.'); if (ext != NULL) { size_t extlen = strlen (++ext); for (const char *type = p_sys->ignored_exts, *end; type[0]; type = end + 1) { end = strchr (type, ','); if (end == NULL) end = type + strlen (type); if (type + extlen == end && !strncasecmp (ext, type, extlen)) { free (entry); return NULL; } if (*end == '\0') break; } } } char *encoded = encode_URI_component (entry); free (entry); if (encoded == NULL) goto fatal; int len = asprintf (&entry, " <track><location>%s/%s</location>\n" \ " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" \ " <vlc:id>%d</vlc:id>\n" \ " </extension>\n" \ " </track>\n", current->uri, encoded, p_sys->i_item_count++); free (encoded); if (len == -1) goto fatal; /* Write the ID to the extension */ char *old_xspf_extension = p_sys->psz_xspf_extension; if (old_xspf_extension == NULL) goto fatal; int len2 = asprintf( &p_sys->psz_xspf_extension, "%s <vlc:item tid=\"%i\" />\n", old_xspf_extension, p_sys->i_item_count-1 ); if (len2 == -1) goto fatal; free( old_xspf_extension ); block_t *block = block_heap_Alloc (entry, entry, len); if (unlikely(block == NULL)) { free (entry); goto fatal; } return block; fatal: p_access->info.b_eof = true; return NULL; }
static block_t *DecodeBlock (decoder_t *p_dec, block_t **pp_block) { block_t *p_block; decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_out = NULL; if (pp_block == NULL) return NULL; p_block = *pp_block; if (p_block == NULL) return NULL; *pp_block = NULL; if (p_block->i_pts > VLC_TS_INVALID && !date_Get (&p_sys->end_date)) date_Set (&p_sys->end_date, p_block->i_pts); else if (p_block->i_pts < date_Get (&p_sys->end_date)) { msg_Warn (p_dec, "MIDI message in the past?"); goto drop; } if (p_block->i_buffer < 1) goto drop; uint8_t event = p_block->p_buffer[0]; uint8_t channel = p_block->p_buffer[0] & 0xf; event &= 0xF0; if (event == 0xF0) switch (channel) { case 0: if (p_block->p_buffer[p_block->i_buffer - 1] != 0xF7) { case 7: msg_Warn (p_dec, "fragmented SysEx not implemented"); goto drop; } fluid_synth_sysex (p_sys->synth, (char *)p_block->p_buffer + 1, p_block->i_buffer - 2, NULL, NULL, NULL, 0); break; case 0xF: fluid_synth_system_reset (p_sys->synth); break; } uint8_t p1 = (p_block->i_buffer > 1) ? (p_block->p_buffer[1] & 0x7f) : 0; uint8_t p2 = (p_block->i_buffer > 2) ? (p_block->p_buffer[2] & 0x7f) : 0; switch (event & 0xF0) { case 0x80: fluid_synth_noteoff (p_sys->synth, channel, p1); break; case 0x90: fluid_synth_noteon (p_sys->synth, channel, p1, p2); break; /*case 0xA0: note aftertouch not implemented */ case 0xB0: fluid_synth_cc (p_sys->synth, channel, p1, p2); break; case 0xC0: fluid_synth_program_change (p_sys->synth, channel, p1); break; case 0xD0: fluid_synth_channel_pressure (p_sys->synth, channel, p1); break; case 0xE0: fluid_synth_pitch_bend (p_sys->synth, channel, (p2 << 7) | p1); break; } unsigned samples = (p_block->i_pts - date_Get (&p_sys->end_date)) * 441 / 10000; if (samples == 0) goto drop; p_out = decoder_NewAudioBuffer (p_dec, samples); if (p_out == NULL) goto drop; p_out->i_pts = date_Get (&p_sys->end_date ); p_out->i_length = date_Increment (&p_sys->end_date, samples) - p_out->i_pts; fluid_synth_write_float (p_sys->synth, samples, p_out->p_buffer, 0, 2, p_out->p_buffer, 1, 2); drop: block_Release (p_block); return p_out; }
static int Open(vlc_object_t *object) { demux_t *demux = (demux_t*)object; /* Detect the image type */ const image_format_t *img; const uint8_t *peek; int peek_size = 0; for (int i = 0; ; i++) { img = &formats[i]; if (!img->codec) return VLC_EGENERIC; if (img->detect) { if (img->detect(demux->s)) break; } else { if (peek_size < img->marker_size) peek_size = stream_Peek(demux->s, &peek, img->marker_size); if (peek_size >= img->marker_size && !memcmp(peek, img->marker, img->marker_size)) break; } } msg_Dbg(demux, "Detected image: %s", vlc_fourcc_GetDescription(VIDEO_ES, img->codec)); if( img->codec == VLC_CODEC_MXPEG ) { return VLC_EGENERIC; //let avformat demux this file } /* Load and if selected decode */ es_format_t fmt; es_format_Init(&fmt, VIDEO_ES, img->codec); fmt.video.i_chroma = fmt.i_codec; block_t *data = Load(demux); if (data && var_InheritBool(demux, "image-decode")) { char *string = var_InheritString(demux, "image-chroma"); vlc_fourcc_t chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, string); free(string); data = Decode(demux, &fmt.video, chroma, data); fmt.i_codec = fmt.video.i_chroma; } fmt.i_id = var_InheritInteger(demux, "image-id"); fmt.i_group = var_InheritInteger(demux, "image-group"); if (var_InheritURational(demux, &fmt.video.i_frame_rate, &fmt.video.i_frame_rate_base, "image-fps") || fmt.video.i_frame_rate <= 0 || fmt.video.i_frame_rate_base <= 0) { msg_Err(demux, "Invalid frame rate, using 10/1 instead"); fmt.video.i_frame_rate = 10; fmt.video.i_frame_rate_base = 1; } /* If loadind failed, we still continue to avoid mis-detection * by other demuxers. */ if (!data) msg_Err(demux, "Failed to load the image"); /* */ demux_sys_t *sys = malloc(sizeof(*sys)); if (!sys) { if (data) block_Release(data); es_format_Clean(&fmt); return VLC_ENOMEM; } sys->data = data; sys->es = es_out_Add(demux->out, &fmt); sys->duration = CLOCK_FREQ * var_InheritFloat(demux, "image-duration"); sys->is_realtime = var_InheritBool(demux, "image-realtime"); sys->pts_origin = sys->is_realtime ? mdate() : 0; sys->pts_next = VLC_TS_INVALID; date_Init(&sys->pts, fmt.video.i_frame_rate, fmt.video.i_frame_rate_base); date_Set(&sys->pts, 0); es_format_Clean(&fmt); demux->pf_demux = Demux; demux->pf_control = Control; demux->p_sys = sys; return VLC_SUCCESS; }
/***************************************************************************** * Write: *****************************************************************************/ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) { sout_access_out_sys_t *p_sys = p_access->p_sys; int i_err = 0; int i_len = 0; while( p_buffer ) { block_t *p_next; if( p_buffer->i_flags & BLOCK_FLAG_HEADER ) { /* gather header */ if( p_sys->b_header_complete ) { /* free previously gathered header */ p_sys->i_header_size = 0; p_sys->b_header_complete = false; } if( (int)(p_buffer->i_buffer + p_sys->i_header_size) > p_sys->i_header_allocated ) { p_sys->i_header_allocated = p_buffer->i_buffer + p_sys->i_header_size + 1024; p_sys->p_header = xrealloc( p_sys->p_header, p_sys->i_header_allocated ); } memcpy( &p_sys->p_header[p_sys->i_header_size], p_buffer->p_buffer, p_buffer->i_buffer ); p_sys->i_header_size += p_buffer->i_buffer; } else if( !p_sys->b_header_complete ) { p_sys->b_header_complete = true; httpd_StreamHeader( p_sys->p_httpd_stream, p_sys->p_header, p_sys->i_header_size ); } i_len += p_buffer->i_buffer; /* send data */ i_err = httpd_StreamSend( p_sys->p_httpd_stream, p_buffer->p_buffer, p_buffer->i_buffer ); p_next = p_buffer->p_next; block_Release( p_buffer ); p_buffer = p_next; if( i_err < 0 ) { break; } } if( i_err < 0 ) { block_ChainRelease( p_buffer ); } return( i_err < 0 ? VLC_EGENERIC : i_len ); }
static picture_t *decode(decoder_t *dec, block_t **pblock) { decoder_sys_t *sys = dec->p_sys; block_t *block; MMAL_BUFFER_HEADER_T *buffer; bool need_flush = false; uint32_t len; uint32_t flags = 0; MMAL_STATUS_T status; picture_t *ret = NULL; /* * Configure output port if necessary */ if (sys->output_format) { if (change_output_format(dec) < 0) msg_Err(dec, "Failed to change output port format"); } if (!pblock) goto out; block = *pblock; /* * Check whether full flush is required */ if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) { flush_decoder(dec); block_Release(*pblock); return NULL; } /* * Send output buffers */ if (atomic_load(&sys->started)) { buffer = mmal_queue_get(sys->decoded_pictures); if (buffer) { ret = (picture_t *)buffer->user_data; ret->date = buffer->pts; ret->b_progressive = sys->b_progressive; ret->b_top_field_first = sys->b_top_field_first; if (sys->output_pool) { buffer->data = NULL; mmal_buffer_header_reset(buffer); mmal_buffer_header_release(buffer); } } fill_output_port(dec); } if (ret) goto out; /* * Process input */ if (!block) goto out; *pblock = NULL; if (block->i_flags & BLOCK_FLAG_CORRUPTED) flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED; vlc_mutex_lock(&sys->mutex); while (block->i_buffer > 0) { buffer = mmal_queue_timedwait(sys->input_pool->queue, 2); if (!buffer) { msg_Err(dec, "Failed to retrieve buffer header for input data"); need_flush = true; break; } mmal_buffer_header_reset(buffer); buffer->cmd = 0; buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts; buffer->dts = block->i_dts; buffer->alloc_size = sys->input->buffer_size; len = block->i_buffer; if (len > buffer->alloc_size) len = buffer->alloc_size; buffer->data = block->p_buffer; block->p_buffer += len; block->i_buffer -= len; buffer->length = len; if (block->i_buffer == 0) buffer->user_data = block; buffer->flags = flags; status = mmal_port_send_buffer(sys->input, buffer); if (status != MMAL_SUCCESS) { msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)", status, mmal_status_to_string(status)); break; } atomic_fetch_add(&sys->input_in_transit, 1); } vlc_mutex_unlock(&sys->mutex); out: if (need_flush) flush_decoder(dec); return ret; }
/**************************************************************************** * Decode: the whole thing ****************************************************************************/ static picture_t *Decode(decoder_t *dec, block_t **pp_block) { struct vpx_codec_ctx *ctx = &dec->p_sys->ctx; block_t *block = *pp_block; if (!block) return NULL; if (block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) return NULL; /* Associate packet PTS with decoded frame */ mtime_t *pkt_pts = malloc(sizeof(*pkt_pts)); if (!pkt_pts) { block_Release(block); *pp_block = NULL; return NULL; } *pkt_pts = block->i_pts; vpx_codec_err_t err; err = vpx_codec_decode(ctx, block->p_buffer, block->i_buffer, pkt_pts, 0); block_Release(block); *pp_block = NULL; if (err != VPX_CODEC_OK) { free(pkt_pts); const char *error = vpx_codec_error(ctx); const char *detail = vpx_codec_error_detail(ctx); if (!detail) detail = "no specific information"; msg_Err(dec, "Failed to decode frame: %s (%s)", error, detail); return NULL; } const void *iter = NULL; struct vpx_image *img = vpx_codec_get_frame(ctx, &iter); if (!img) { free(pkt_pts); return NULL; } /* fetches back the PTS */ pkt_pts = img->user_priv; mtime_t pts = *pkt_pts; free(pkt_pts); if (img->fmt != VPX_IMG_FMT_I420) { msg_Err(dec, "Unsupported output colorspace %d", img->fmt); return NULL; } video_format_t *v = &dec->fmt_out.video; if (img->d_w != v->i_visible_width || img->d_h != v->i_visible_height) { v->i_visible_width = img->d_w; v->i_visible_height = img->d_h; } picture_t *pic = decoder_NewPicture(dec); if (!pic) return NULL; for (int plane = 0; plane < pic->i_planes; plane++ ) { uint8_t *src = img->planes[plane]; uint8_t *dst = pic->p[plane].p_pixels; int src_stride = img->stride[plane]; int dst_stride = pic->p[plane].i_pitch; int size = __MIN( src_stride, dst_stride ); for( int line = 0; line < pic->p[plane].i_visible_lines; line++ ) { memcpy( dst, src, size ); src += src_stride; dst += dst_stride; } } pic->b_progressive = true; /* codec does not support interlacing */ pic->date = pts; return pic; }
/**************************************************************************** * DecodeBlock: the whole thing **************************************************************************** * This function is called just after the thread is launched. ****************************************************************************/ static void *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; uint8_t p_header[VLC_A52_HEADER_SIZE]; uint8_t *p_buf; void *p_out_buffer; if( !pp_block || !*pp_block ) return NULL; if( (*pp_block)->i_flags&(BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { if( (*pp_block)->i_flags&BLOCK_FLAG_CORRUPTED ) { p_sys->i_state = STATE_NOSYNC; block_BytestreamEmpty( &p_sys->bytestream ); } date_Set( &p_sys->end_date, 0 ); block_Release( *pp_block ); return NULL; } if( !date_Get( &p_sys->end_date ) && !(*pp_block)->i_pts ) { /* We've just started the stream, wait for the first PTS. */ block_Release( *pp_block ); return NULL; } block_BytestreamPush( &p_sys->bytestream, *pp_block ); while( 1 ) { switch( p_sys->i_state ) { case STATE_NOSYNC: while( block_PeekBytes( &p_sys->bytestream, p_header, 2 ) == VLC_SUCCESS ) { if( p_header[0] == 0x0b && p_header[1] == 0x77 ) { p_sys->i_state = STATE_SYNC; break; } block_SkipByte( &p_sys->bytestream ); } if( p_sys->i_state != STATE_SYNC ) { block_BytestreamFlush( &p_sys->bytestream ); /* Need more data */ return NULL; } case STATE_SYNC: /* New frame, set the Presentation Time Stamp */ p_sys->i_pts = p_sys->bytestream.p_block->i_pts; if( p_sys->i_pts != 0 && p_sys->i_pts != date_Get( &p_sys->end_date ) ) { date_Set( &p_sys->end_date, p_sys->i_pts ); } p_sys->i_state = STATE_HEADER; case STATE_HEADER: /* Get A/52 frame header (VLC_A52_HEADER_SIZE bytes) */ if( block_PeekBytes( &p_sys->bytestream, p_header, VLC_A52_HEADER_SIZE ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } /* Check if frame is valid and get frame info */ if( vlc_a52_header_Parse( &p_sys->frame, p_header, VLC_A52_HEADER_SIZE ) ) { msg_Dbg( p_dec, "emulated sync word" ); block_SkipByte( &p_sys->bytestream ); p_sys->i_state = STATE_NOSYNC; break; } p_sys->i_state = STATE_NEXT_SYNC; case STATE_NEXT_SYNC: /* TODO: If pp_block == NULL, flush the buffer without checking the * next sync word */ /* Check if next expected frame contains the sync word */ if( block_PeekOffsetBytes( &p_sys->bytestream, p_sys->frame.i_size, p_header, 2 ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } if( p_sys->b_packetizer && p_header[0] == 0 && p_header[1] == 0 ) { /* A52 wav files and audio CD's use stuffing */ p_sys->i_state = STATE_GET_DATA; break; } if( p_header[0] != 0x0b || p_header[1] != 0x77 ) { msg_Dbg( p_dec, "emulated sync word " "(no sync on following frame)" ); p_sys->i_state = STATE_NOSYNC; block_SkipByte( &p_sys->bytestream ); break; } p_sys->i_state = STATE_SEND_DATA; break; case STATE_GET_DATA: /* Make sure we have enough data. * (Not useful if we went through NEXT_SYNC) */ if( block_WaitBytes( &p_sys->bytestream, p_sys->frame.i_size ) != VLC_SUCCESS ) { /* Need more data */ return NULL; } p_sys->i_state = STATE_SEND_DATA; case STATE_SEND_DATA: if( !(p_buf = GetOutBuffer( p_dec, &p_out_buffer )) ) { //p_dec->b_error = true; return NULL; } /* Copy the whole frame into the buffer. When we reach this point * we already know we have enough data available. */ block_GetBytes( &p_sys->bytestream, p_buf, p_sys->frame.i_size ); /* Make sure we don't reuse the same pts twice */ if( p_sys->i_pts == p_sys->bytestream.p_block->i_pts ) p_sys->i_pts = p_sys->bytestream.p_block->i_pts = 0; /* So p_block doesn't get re-added several times */ *pp_block = block_BytestreamPop( &p_sys->bytestream ); p_sys->i_state = STATE_NOSYNC; return p_out_buffer; } } return NULL; }
static picture_t *ImageRead( image_handler_t *p_image, block_t *p_block, video_format_t *p_fmt_in, video_format_t *p_fmt_out ) { picture_t *p_pic = NULL, *p_tmp; /* Check if we can reuse the current decoder */ if( p_image->p_dec && p_image->p_dec->fmt_in.i_codec != p_fmt_in->i_chroma ) { DeleteDecoder( p_image->p_dec ); p_image->p_dec = 0; } /* Start a decoder */ if( !p_image->p_dec ) { p_image->p_dec = CreateDecoder( p_image->p_parent, p_fmt_in ); if( !p_image->p_dec ) { block_Release(p_block); return NULL; } } p_block->i_pts = p_block->i_dts = mdate(); while( (p_tmp = p_image->p_dec->pf_decode_video( p_image->p_dec, &p_block )) != NULL ) { if( p_pic != NULL ) picture_Release( p_pic ); p_pic = p_tmp; } if( p_pic == NULL ) { msg_Warn( p_image->p_parent, "no image decoded" ); return 0; } if( !p_fmt_out->i_chroma ) p_fmt_out->i_chroma = p_image->p_dec->fmt_out.video.i_chroma; if( !p_fmt_out->i_width && p_fmt_out->i_height ) p_fmt_out->i_width = (int64_t)p_image->p_dec->fmt_out.video.i_width * p_image->p_dec->fmt_out.video.i_sar_num * p_fmt_out->i_height / p_image->p_dec->fmt_out.video.i_height / p_image->p_dec->fmt_out.video.i_sar_den; if( !p_fmt_out->i_height && p_fmt_out->i_width ) p_fmt_out->i_height = (int64_t)p_image->p_dec->fmt_out.video.i_height * p_image->p_dec->fmt_out.video.i_sar_den * p_fmt_out->i_width / p_image->p_dec->fmt_out.video.i_width / p_image->p_dec->fmt_out.video.i_sar_num; if( !p_fmt_out->i_width ) p_fmt_out->i_width = p_image->p_dec->fmt_out.video.i_width; if( !p_fmt_out->i_height ) p_fmt_out->i_height = p_image->p_dec->fmt_out.video.i_height; if( !p_fmt_out->i_visible_width ) p_fmt_out->i_visible_width = p_fmt_out->i_width; if( !p_fmt_out->i_visible_height ) p_fmt_out->i_visible_height = p_fmt_out->i_height; /* Check if we need chroma conversion or resizing */ if( p_image->p_dec->fmt_out.video.i_chroma != p_fmt_out->i_chroma || p_image->p_dec->fmt_out.video.i_width != p_fmt_out->i_width || p_image->p_dec->fmt_out.video.i_height != p_fmt_out->i_height ) { if( p_image->p_filter ) if( p_image->p_filter->fmt_in.video.i_chroma != p_image->p_dec->fmt_out.video.i_chroma || p_image->p_filter->fmt_out.video.i_chroma != p_fmt_out->i_chroma ) { /* We need to restart a new filter */ DeleteFilter( p_image->p_filter ); p_image->p_filter = 0; } /* Start a filter */ if( !p_image->p_filter ) { p_image->p_filter = CreateFilter( p_image->p_parent, &p_image->p_dec->fmt_out, p_fmt_out ); if( !p_image->p_filter ) { picture_Release( p_pic ); return NULL; } } else { /* Filters should handle on-the-fly size changes */ p_image->p_filter->fmt_in = p_image->p_dec->fmt_out; p_image->p_filter->fmt_out = p_image->p_dec->fmt_out; p_image->p_filter->fmt_out.i_codec = p_fmt_out->i_chroma; p_image->p_filter->fmt_out.video = *p_fmt_out; } p_pic = p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic ); *p_fmt_out = p_image->p_filter->fmt_out.video; } else *p_fmt_out = p_image->p_dec->fmt_out.video; return p_pic; }
/***************************************************************************** VCDRead: reads VCD_BLOCKS_ONCE from the VCD and returns that. NULL is returned if something went wrong. *****************************************************************************/ static block_t * VCDReadBlock( access_t * p_access ) { vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys; const int i_blocks = p_vcdplayer->i_blocks_per_read; block_t *p_block; int i_read; uint8_t *p_buf; dbg_print( (INPUT_DBG_LSN), "lsn: %lu", (long unsigned int) p_vcdplayer->i_lsn ); /* Allocate a block for the reading */ if( !( p_block = block_Alloc( i_blocks * M2F2_SECTOR_SIZE ) ) ) { msg_Err( p_access, "cannot get a new block of size: %i", i_blocks * M2F2_SECTOR_SIZE ); block_Release( p_block ); return NULL; } p_buf = (uint8_t *) p_block->p_buffer; for ( i_read = 0 ; i_read < i_blocks ; i_read++ ) { vcdplayer_read_status_t read_status = vcdplayer_read(p_access, p_buf); p_access->info.i_pos += M2F2_SECTOR_SIZE; switch ( read_status ) { case READ_END: /* End reached. Return NULL to indicated this. */ /* We also set the postion to the end so the higher level (demux?) doesn't try to keep reading. If everything works out right this shouldn't have to happen. */ #if 0 if( p_access->info.i_pos != p_access->info.i_size ) { msg_Warn( p_access, "At end but pos (%llu) is not size (%llu). Adjusting.", p_access->info.i_pos, p_access->info.i_size ); p_access->info.i_pos = p_access->info.i_size; } #endif block_Release( p_block ); return NULL; case READ_ERROR: /* Some sort of error. Should we increment lsn? to skip block? */ block_Release( p_block ); return NULL; case READ_STILL_FRAME: /* FIXME The below should be done in an event thread. Until then... */ #if 1 msleep( INT64_C(1000) * *p_buf ); VCDSetOrigin(p_access, p_vcdplayer->origin_lsn, p_vcdplayer->i_track, &(p_vcdplayer->play_item)); // p_vcd->in_still = false; dbg_print(INPUT_DBG_STILL, "still wait time done"); #endif block_Release( p_block ); return NULL; default: case READ_BLOCK: /* Read buffer */ break; } p_buf += M2F2_SECTOR_SIZE; /* Update seekpoint */ if ( VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type ) { size_t i_entry = p_vcdplayer->play_item.num+1; lsn_t i_lsn = vcdinfo_get_entry_lsn(p_vcdplayer->vcd, i_entry); if ( p_vcdplayer->i_lsn >= i_lsn && i_lsn != VCDINFO_NULL_LSN ) { dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), "entry change to %zu, current LSN %u >= end %u", i_entry, p_vcdplayer->i_lsn, i_lsn); p_vcdplayer->play_item.num = i_entry; VCDSetOrigin( p_access, i_lsn, p_vcdplayer->i_track, &(p_vcdplayer->play_item) ); } } } return p_block; }
/**************************************************************************** * DecodeBlock: the whole thing **************************************************************************** * This function must be fed with a complete compressed frame. ****************************************************************************/ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_block; picture_t *p_pic = 0; png_uint_32 i_width, i_height; int i_color_type, i_interlace_type, i_compression_type, i_filter_type; int i_bit_depth, i; png_structp p_png; png_infop p_info, p_end_info; png_bytep *p_row_pointers = NULL; if( !pp_block || !*pp_block ) return NULL; p_block = *pp_block; p_sys->b_error = false; if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY ) { block_Release( p_block ); *pp_block = NULL; return NULL; } p_png = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 ); if( p_png == NULL ) { block_Release( p_block ); *pp_block = NULL; return NULL; } p_info = png_create_info_struct( p_png ); if( p_info == NULL ) { png_destroy_read_struct( &p_png, NULL, NULL ); block_Release( p_block ); *pp_block = NULL; return NULL; } p_end_info = png_create_info_struct( p_png ); if( p_end_info == NULL ) { png_destroy_read_struct( &p_png, &p_info, NULL ); block_Release( p_block ); *pp_block = NULL; return NULL; } /* libpng longjmp's there in case of error */ if( setjmp( png_jmpbuf( p_png ) ) ) goto error; png_set_read_fn( p_png, (void *)p_block, user_read ); png_set_error_fn( p_png, (void *)p_dec, user_error, user_warning ); png_read_info( p_png, p_info ); if( p_sys->b_error ) goto error; png_get_IHDR( p_png, p_info, &i_width, &i_height, &i_bit_depth, &i_color_type, &i_interlace_type, &i_compression_type, &i_filter_type); if( p_sys->b_error ) goto error; /* Set output properties */ p_dec->fmt_out.i_codec = VLC_CODEC_RGBA; p_dec->fmt_out.video.i_visible_width = p_dec->fmt_out.video.i_width = i_width; p_dec->fmt_out.video.i_visible_height = p_dec->fmt_out.video.i_height = i_height; p_dec->fmt_out.video.i_sar_num = 1; p_dec->fmt_out.video.i_sar_den = 1; p_dec->fmt_out.video.i_rmask = 0x000000ff; p_dec->fmt_out.video.i_gmask = 0x0000ff00; p_dec->fmt_out.video.i_bmask = 0x00ff0000; if( i_color_type == PNG_COLOR_TYPE_PALETTE ) png_set_palette_to_rgb( p_png ); if( i_color_type == PNG_COLOR_TYPE_GRAY || i_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) png_set_gray_to_rgb( p_png ); /* Strip to 8 bits per channel */ if( i_bit_depth == 16 ) png_set_strip_16( p_png ); if( png_get_valid( p_png, p_info, PNG_INFO_tRNS ) ) { png_set_tRNS_to_alpha( p_png ); } else if( !(i_color_type & PNG_COLOR_MASK_ALPHA) ) { p_dec->fmt_out.i_codec = VLC_CODEC_RGB24; } /* Get a new picture */ p_pic = decoder_NewPicture( p_dec ); if( !p_pic ) goto error; /* Decode picture */ p_row_pointers = malloc( sizeof(png_bytep) * i_height ); if( !p_row_pointers ) goto error; for( i = 0; i < (int)i_height; i++ ) p_row_pointers[i] = p_pic->p->p_pixels + p_pic->p->i_pitch * i; png_read_image( p_png, p_row_pointers ); if( p_sys->b_error ) goto error; png_read_end( p_png, p_end_info ); if( p_sys->b_error ) goto error; png_destroy_read_struct( &p_png, &p_info, &p_end_info ); free( p_row_pointers ); p_pic->date = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : p_block->i_dts; block_Release( p_block ); *pp_block = NULL; return p_pic; error: free( p_row_pointers ); png_destroy_read_struct( &p_png, &p_info, &p_end_info ); block_Release( p_block ); *pp_block = NULL; return NULL; }
/***************************************************************************** * Decode: *****************************************************************************/ static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block ) { block_t *p_block; subpicture_t *p_spu = NULL; if( ( pp_block == NULL ) || ( *pp_block == NULL ) ) return NULL; p_block = *pp_block; *pp_block = NULL; if( ( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) || p_block->i_buffer < sizeof(uint16_t) ) { block_Release( p_block ); return NULL; } uint8_t *p_buf = p_block->p_buffer; /* Read our raw string and create the styled segment for HTML */ uint16_t i_psz_length = GetWBE( p_buf ); char *psz_subtitle = malloc( i_psz_length + 1 ); if ( !psz_subtitle ) return NULL; memcpy( psz_subtitle, p_block->p_buffer + sizeof(uint16_t), i_psz_length ); psz_subtitle[ i_psz_length ] = '\0'; p_buf += i_psz_length + sizeof(uint16_t); for( uint16_t i=0; i < i_psz_length; i++ ) if ( psz_subtitle[i] == '\r' ) psz_subtitle[i] = '\n'; segment_t *p_segment = calloc( 1, sizeof(segment_t) ); if ( !p_segment ) { free( psz_subtitle ); return NULL; } p_segment->psz_string = strdup( psz_subtitle ); p_segment->i_size = strlen( psz_subtitle ); if ( p_dec->fmt_in.subs.p_style ) { p_segment->styles.i_color = p_dec->fmt_in.subs.p_style->i_font_color; p_segment->styles.i_color |= p_dec->fmt_in.subs.p_style->i_font_alpha << 24; if ( p_dec->fmt_in.subs.p_style->i_style_flags ) p_segment->styles.i_flags = p_dec->fmt_in.subs.p_style->i_style_flags; p_segment->styles.i_fontsize = p_dec->fmt_in.subs.p_style->i_font_size; } if ( !p_segment->psz_string ) { SegmentFree( p_segment ); free( psz_subtitle ); return NULL; } /* Create the subpicture unit */ p_spu = decoder_NewSubpictureText( p_dec ); if( !p_spu ) { free( psz_subtitle ); SegmentFree( p_segment ); return NULL; } subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; /* Parse our styles */ while( (size_t)(p_buf - p_block->p_buffer) + 8 < p_block->i_buffer ) { uint32_t i_atomsize = GetDWBE( p_buf ); vlc_fourcc_t i_atomtype = VLC_FOURCC(p_buf[4],p_buf[5],p_buf[6],p_buf[7]); p_buf += 8; switch( i_atomtype ) { case VLC_FOURCC('s','t','y','l'): { if ( (size_t)(p_buf - p_block->p_buffer) < 14 ) break; uint16_t i_nbrecords = GetWBE(p_buf); uint16_t i_cur_record = 0; p_buf += 2; while( i_cur_record++ < i_nbrecords ) { if ( (size_t)(p_buf - p_block->p_buffer) < 12 ) break; uint16_t i_start = __MIN( GetWBE(p_buf), i_psz_length - 1 ); uint16_t i_end = __MIN( GetWBE(p_buf + 2), i_psz_length - 1 ); segment_style_t style; style.i_flags = ConvertFlags( p_buf[6] ); style.i_fontsize = p_buf[7]; style.i_color = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB style.i_color |= (GetDWBE(p_buf+8) & 0xFF) << 24; ApplySegmentStyle( &p_segment, i_start, i_end, &style ); if ( i_nbrecords == 1 ) { if ( p_buf[6] ) { p_spu_sys->style_flags.i_value = ConvertFlags( p_buf[6] ); p_spu_sys->style_flags.b_set = true; } p_spu_sys->i_font_height_abs_to_src = p_buf[7]; p_spu_sys->font_color.i_value = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB p_spu_sys->font_color.i_value |= (GetDWBE(p_buf+8) & 0xFF) << 24; p_spu_sys->font_color.b_set = true; } p_buf += 12; } } break; case VLC_FOURCC('d','r','p','o'): if ( (size_t)(p_buf - p_block->p_buffer) < 4 ) break; p_spu_sys->i_drop_shadow = __MAX( GetWBE(p_buf), GetWBE(p_buf+2) ); break; case VLC_FOURCC('d','r','p','t'): if ( (size_t)(p_buf - p_block->p_buffer) < 2 ) break; p_spu_sys->i_drop_shadow_alpha = GetWBE(p_buf); break; default: break; } p_buf += i_atomsize; } p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = false; p_spu_sys->align = SUBPICTURE_ALIGN_BOTTOM; p_spu_sys->text = psz_subtitle; p_spu_sys->p_htmlsegments = p_segment; block_Release( p_block ); return p_spu; }
/***************************************************************************** * Decode: *****************************************************************************/ static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_block; subpicture_t *p_spu = NULL; video_format_t fmt; bool b_cached = false; vbi_page p_page; if( (pp_block == NULL) || (*pp_block == NULL) ) return NULL; p_block = *pp_block; *pp_block = NULL; if( p_block->i_buffer > 0 && ( ( p_block->p_buffer[0] >= 0x10 && p_block->p_buffer[0] <= 0x1f ) || ( p_block->p_buffer[0] >= 0x99 && p_block->p_buffer[0] <= 0x9b ) ) ) { vbi_sliced *p_sliced = p_sys->p_vbi_sliced; unsigned int i_lines = 0; p_block->i_buffer--; p_block->p_buffer++; while( p_block->i_buffer >= 2 ) { int i_id = p_block->p_buffer[0]; unsigned i_size = p_block->p_buffer[1]; if( 2 + i_size > p_block->i_buffer ) break; if( ( i_id == 0x02 || i_id == 0x03 ) && i_size >= 44 && i_lines < MAX_SLICES ) { if(p_block->p_buffer[3] == 0xE4 ) /* framing_code */ { unsigned line_offset = p_block->p_buffer[2] & 0x1f; unsigned field_parity = p_block->p_buffer[2] & 0x20; p_sliced[i_lines].id = VBI_SLICED_TELETEXT_B; if( line_offset > 0 ) p_sliced[i_lines].line = line_offset + (field_parity ? 0 : 313); else p_sliced[i_lines].line = 0; for( int i = 0; i < 42; i++ ) p_sliced[i_lines].data[i] = vbi_rev8( p_block->p_buffer[4 + i] ); i_lines++; } } p_block->i_buffer -= 2 + i_size; p_block->p_buffer += 2 + i_size; } if( i_lines > 0 ) vbi_decode( p_sys->p_vbi_dec, p_sliced, i_lines, 0 ); } /* */ vlc_mutex_lock( &p_sys->lock ); const int i_align = p_sys->i_align; const unsigned int i_wanted_page = p_sys->i_wanted_page; const unsigned int i_wanted_subpage = p_sys->i_wanted_subpage; const bool b_opaque = p_sys->b_opaque; vlc_mutex_unlock( &p_sys->lock ); /* Try to see if the page we want is in the cache yet */ memset( &p_page, 0, sizeof(vbi_page) ); b_cached = vbi_fetch_vt_page( p_sys->p_vbi_dec, &p_page, vbi_dec2bcd( i_wanted_page ), i_wanted_subpage, VBI_WST_LEVEL_3p5, 25, true ); if( i_wanted_page == p_sys->i_last_page && !p_sys->b_update ) goto error; if( !b_cached ) { if( p_sys->b_text && p_sys->i_last_page != i_wanted_page ) { /* We need to reset the subtitle */ p_spu = Subpicture( p_dec, &fmt, true, p_page.columns, p_page.rows, i_align, p_block->i_pts ); if( !p_spu ) goto error; subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; p_spu_sys->text = strdup(""); p_sys->b_update = true; p_sys->i_last_page = i_wanted_page; goto exit; } goto error; } p_sys->b_update = false; p_sys->i_last_page = i_wanted_page; #ifdef ZVBI_DEBUG msg_Dbg( p_dec, "we now have page: %d ready for display", i_wanted_page ); #endif /* Ignore transparent rows at the beginning and end */ int i_first_row = get_first_visible_row( p_page.text, p_page.rows, p_page.columns ); int i_num_rows; if ( i_first_row < 0 ) { i_first_row = p_page.rows - 1; i_num_rows = 0; } else { i_num_rows = get_last_visible_row( p_page.text, p_page.rows, p_page.columns ) - i_first_row + 1; } #ifdef ZVBI_DEBUG msg_Dbg( p_dec, "After top and tail of page we have rows %i-%i of %i", i_first_row + 1, i_first_row + i_num_rows, p_page.rows ); #endif /* If there is a page or sub to render, then we do that here */ /* Create the subpicture unit */ p_spu = Subpicture( p_dec, &fmt, p_sys->b_text, p_page.columns, i_num_rows, i_align, p_block->i_pts ); if( !p_spu ) goto error; if( p_sys->b_text ) { unsigned int i_textsize = 7000; int i_total,offset; // char p_text[i_textsize+1]; char *p_text = (char *)malloc(i_textsize+1); // sunqueen modify i_total = vbi_print_page_region( &p_page, p_text, i_textsize, "UTF-8", 0, 0, 0, i_first_row, p_page.columns, i_num_rows ); for( offset=1; offset<i_total && isspace( p_text[i_total-offset ] ); offset++) p_text[i_total-offset] = '\0'; i_total -= offset; offset=0; while( offset < i_total && isspace( p_text[offset] ) ) offset++; subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; p_spu_sys->text = strdup( &p_text[offset] ); p_spu_sys->align = i_align; p_spu_sys->i_font_height_percent = 5; p_spu_sys->renderbg = b_opaque; #ifdef ZVBI_DEBUG msg_Info( p_dec, "page %x-%x(%d)\n\"%s\"", p_page.pgno, p_page.subno, i_total, &p_text[offset] ); #endif free(p_text); // sunqueen add } else { picture_t *p_pic = p_spu->p_region->p_picture; /* ZVBI is stupid enough to assume pitch == width */ p_pic->p->i_pitch = 4 * fmt.i_width; /* Maintain subtitle postion */ p_spu->p_region->i_y = i_first_row*10; p_spu->i_original_picture_width = p_page.columns*12; p_spu->i_original_picture_height = p_page.rows*10; vbi_draw_vt_page_region( &p_page, ZVBI_PIXFMT_RGBA32, p_spu->p_region->p_picture->p->p_pixels, -1, 0, i_first_row, p_page.columns, i_num_rows, 1, 1); vlc_mutex_lock( &p_sys->lock ); memcpy( p_sys->nav_link, &p_page.nav_link, sizeof( p_sys->nav_link )) ; vlc_mutex_unlock( &p_sys->lock ); OpaquePage( p_pic, &p_page, fmt, b_opaque, i_first_row * p_page.columns ); } exit: vbi_unref_page( &p_page ); block_Release( p_block ); return p_spu; error: vbi_unref_page( &p_page ); if( p_spu != NULL ) { decoder_DeleteSubpicture( p_dec, p_spu ); p_spu = NULL; } block_Release( p_block ); return NULL; }
/***************************************************************************** * DoWork: convert a buffer *****************************************************************************/ static block_t *DoWork( filter_t * p_filter, block_t * p_in_buf ) { filter_sys_t * p_sys = p_filter->p_sys; float * p_in = (float*) p_in_buf->p_buffer; size_t i_nb_samples = p_in_buf->i_nb_samples; size_t i_nb_channels = aout_FormatNbChannels( &p_filter->fmt_out.audio ); size_t i_nb_rear = 0; size_t i; block_t *p_out_buf = block_Alloc( sizeof(float) * i_nb_samples * i_nb_channels ); if( !p_out_buf ) goto out; float * p_out = (float*) p_out_buf->p_buffer; p_out_buf->i_nb_samples = i_nb_samples; p_out_buf->i_dts = p_in_buf->i_dts; p_out_buf->i_pts = p_in_buf->i_pts; p_out_buf->i_length = p_in_buf->i_length; memset( p_out, 0, p_out_buf->i_buffer ); if( p_sys->i_rear_left >= 0 ) { ++i_nb_rear; } if( p_sys->i_rear_center >= 0 ) { ++i_nb_rear; } if( p_sys->i_rear_right >= 0 ) { ++i_nb_rear; } for( i = 0; i < i_nb_samples; ++i ) { float f_left = p_in[ i * 2 ]; float f_right = p_in[ i * 2 + 1 ]; float f_rear = ( f_left - f_right ) / i_nb_rear; if( p_sys->i_center >= 0 ) { float f_center = f_left + f_right; f_left -= f_center / 2; f_right -= f_center / 2; p_out[ i * i_nb_channels + p_sys->i_center ] = f_center; } if( p_sys->i_left >= 0 ) { p_out[ i * i_nb_channels + p_sys->i_left ] = f_left; } if( p_sys->i_right >= 0 ) { p_out[ i * i_nb_channels + p_sys->i_right ] = f_right; } if( p_sys->i_rear_left >= 0 ) { p_out[ i * i_nb_channels + p_sys->i_rear_left ] = f_rear; } if( p_sys->i_rear_center >= 0 ) { p_out[ i * i_nb_channels + p_sys->i_rear_center ] = f_rear; } if( p_sys->i_rear_right >= 0 ) { p_out[ i * i_nb_channels + p_sys->i_rear_right ] = f_rear; } } out: block_Release( p_in_buf ); return p_out_buf; }
/**************************************************************************** * DecodeBlock: ****************************************************************************/ static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; block_t *p_block; if( !pp_block || *pp_block == NULL ) return NULL; p_block = *pp_block; *pp_block = NULL; if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) { Flush( p_dec ); block_Release( p_block ); return NULL; } if( p_block->i_buffer == 0 || p_block->p_buffer[0] == '\0' ) { block_Release( p_block ); return NULL; } subpicture_updater_sys_t *p_spu_sys = malloc( sizeof(*p_spu_sys) ); if( !p_spu_sys ) { block_Release( p_block ); return NULL; } subpicture_updater_t updater = { .pf_validate = SubpictureValidate, .pf_update = SubpictureUpdate, .pf_destroy = SubpictureDestroy, .p_sys = p_spu_sys, }; p_spu = decoder_NewSubpicture( p_dec, &updater ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); free( p_spu_sys ); block_Release( p_block ); return NULL; } p_spu_sys->p_img = NULL; p_spu_sys->p_dec_sys = p_sys; p_spu_sys->i_subs_len = p_block->i_buffer; p_spu_sys->p_subs_data = malloc( p_block->i_buffer ); p_spu_sys->i_pts = p_block->i_pts; if( !p_spu_sys->p_subs_data ) { subpicture_Delete( p_spu ); block_Release( p_block ); return NULL; } memcpy( p_spu_sys->p_subs_data, p_block->p_buffer, p_block->i_buffer ); p_spu->i_start = p_block->i_pts; p_spu->i_stop = __MAX( p_sys->i_max_stop, p_block->i_pts + p_block->i_length ); p_spu->b_ephemer = true; p_spu->b_absolute = true; p_sys->i_max_stop = p_spu->i_stop; vlc_mutex_lock( &p_sys->lock ); if( p_sys->p_track ) { ass_process_chunk( p_sys->p_track, p_spu_sys->p_subs_data, p_spu_sys->i_subs_len, p_block->i_pts / 1000, p_block->i_length / 1000 ); } vlc_mutex_unlock( &p_sys->lock ); DecSysHold( p_sys ); /* Keep a reference for the returned subpicture */ block_Release( p_block ); return p_spu; }
/***************************************************************************** * DecodeVideo: Called to decode one or more frames *****************************************************************************/ static picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; AVCodecContext *p_context = p_sys->p_context; int b_drawpicture; block_t *p_block; if( !pp_block ) return NULL; if( !p_context->extradata_size && p_dec->fmt_in.i_extra ) { ffmpeg_InitCodec( p_dec ); if( p_sys->b_delayed_open ) OpenVideoCodec( p_dec ); } p_block = *pp_block; if(!p_block && !(p_sys->p_codec->capabilities & CODEC_CAP_DELAY) ) return NULL; if( p_sys->b_delayed_open ) { if( p_block ) block_Release( p_block ); return NULL; } if( p_block) { if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { p_sys->i_pts = VLC_TS_INVALID; /* To make sure we recover properly */ p_sys->i_late_frames = 0; post_mt( p_sys ); if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY ) avcodec_flush_buffers( p_context ); wait_mt( p_sys ); block_Release( p_block ); return NULL; } if( p_block->i_flags & BLOCK_FLAG_PREROLL ) { /* Do not care about late frames when prerolling * TODO avoid decoding of non reference frame * (ie all B except for H264 where it depends only on nal_ref_idc) */ p_sys->i_late_frames = 0; } } if( !p_dec->b_pace_control && (p_sys->i_late_frames > 0) && (mdate() - p_sys->i_late_frames_start > INT64_C(5000000)) ) { if( p_sys->i_pts > VLC_TS_INVALID ) { p_sys->i_pts = VLC_TS_INVALID; /* To make sure we recover properly */ } if( p_block ) block_Release( p_block ); p_sys->i_late_frames--; msg_Err( p_dec, "more than 5 seconds of late video -> " "dropping frame (computer too slow ?)" ); return NULL; } /* A good idea could be to decode all I pictures and see for the other */ if( !p_dec->b_pace_control && p_sys->b_hurry_up && (p_sys->i_late_frames > 4) ) { b_drawpicture = 0; if( p_sys->i_late_frames < 12 ) { p_context->skip_frame = (p_sys->i_skip_frame <= AVDISCARD_NONREF) ? AVDISCARD_NONREF : p_sys->i_skip_frame; } else { /* picture too late, won't decode * but break picture until a new I, and for mpeg4 ...*/ p_sys->i_late_frames--; /* needed else it will never be decrease */ if( p_block ) block_Release( p_block ); msg_Warn( p_dec, "More than 4 late frames, dropping frame" ); return NULL; } } else { if( p_sys->b_hurry_up ) p_context->skip_frame = p_sys->i_skip_frame; if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) b_drawpicture = 1; else b_drawpicture = 0; } if( p_context->width <= 0 || p_context->height <= 0 ) { if( p_sys->b_hurry_up ) p_context->skip_frame = p_sys->i_skip_frame; } else if( !b_drawpicture ) { /* It creates broken picture * FIXME either our parser or ffmpeg is broken */ #if 0 if( p_sys->b_hurry_up ) p_context->skip_frame = __MAX( p_context->skip_frame, AVDISCARD_NONREF ); #endif } /* * Do the actual decoding now */ /* Don't forget that libavcodec requires a little more bytes * that the real frame size */ if( p_block && p_block->i_buffer > 0 ) { p_sys->b_flush = ( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) != 0; p_block = block_Realloc( p_block, 0, p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE ); if( !p_block ) return NULL; p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE; *pp_block = p_block; memset( p_block->p_buffer + p_block->i_buffer, 0, FF_INPUT_BUFFER_PADDING_SIZE ); } while( !p_block || p_block->i_buffer > 0 || p_sys->b_flush ) { int i_used, b_gotpicture; AVPacket pkt; AVFrame *frame = av_frame_alloc(); if (unlikely(frame == NULL)) { p_dec->b_error = true; break; } post_mt( p_sys ); av_init_packet( &pkt ); if( p_block ) { pkt.data = p_block->p_buffer; pkt.size = p_block->i_buffer; pkt.pts = p_block->i_pts; pkt.dts = p_block->i_dts; } else { /* Return delayed frames if codec has CODEC_CAP_DELAY */ pkt.data = NULL; pkt.size = 0; } if( !p_sys->palette_sent ) { uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); if (pal) { memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE); p_sys->palette_sent = true; } } /* Make sure we don't reuse the same timestamps twice */ if( p_block ) { p_block->i_pts = p_block->i_dts = VLC_TS_INVALID; } i_used = avcodec_decode_video2( p_context, frame, &b_gotpicture, &pkt ); av_free_packet( &pkt ); wait_mt( p_sys ); if( p_sys->b_flush ) p_sys->b_first_frame = true; if( p_block ) { if( p_block->i_buffer <= 0 ) p_sys->b_flush = false; if( i_used < 0 ) { av_frame_unref(frame); if( b_drawpicture ) msg_Warn( p_dec, "cannot decode one frame (%zu bytes)", p_block->i_buffer ); break; } else if( (unsigned)i_used > p_block->i_buffer || p_context->thread_count > 1 ) { i_used = p_block->i_buffer; } /* Consumed bytes */ p_block->i_buffer -= i_used; p_block->p_buffer += i_used; } /* Nothing to display */ if( !b_gotpicture ) { av_frame_unref(frame); if( i_used == 0 ) break; continue; } /* Compute the PTS */ mtime_t i_pts = frame->pkt_pts; if (i_pts <= VLC_TS_INVALID) i_pts = frame->pkt_dts; if( i_pts <= VLC_TS_INVALID ) i_pts = p_sys->i_pts; /* Interpolate the next PTS */ if( i_pts > VLC_TS_INVALID ) p_sys->i_pts = i_pts; if( p_sys->i_pts > VLC_TS_INVALID ) { /* interpolate the next PTS */ if( p_dec->fmt_in.video.i_frame_rate > 0 && p_dec->fmt_in.video.i_frame_rate_base > 0 ) { p_sys->i_pts += CLOCK_FREQ * (2 + frame->repeat_pict) * p_dec->fmt_in.video.i_frame_rate_base / (2 * p_dec->fmt_in.video.i_frame_rate); } else if( p_context->time_base.den > 0 ) { int i_tick = p_context->ticks_per_frame; if( i_tick <= 0 ) i_tick = 1; p_sys->i_pts += CLOCK_FREQ * (2 + frame->repeat_pict) * i_tick * p_context->time_base.num / (2 * p_context->time_base.den); } } /* Update frame late count (except when doing preroll) */ mtime_t i_display_date = 0; if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) i_display_date = decoder_GetDisplayDate( p_dec, i_pts ); if( i_display_date > 0 && i_display_date <= mdate() ) { p_sys->i_late_frames++; if( p_sys->i_late_frames == 1 ) p_sys->i_late_frames_start = mdate(); } else { p_sys->i_late_frames = 0; } if( !b_drawpicture || ( !p_sys->p_va && !frame->linesize[0] ) ) { av_frame_unref(frame); continue; } picture_t *p_pic = frame->opaque; if( p_pic == NULL ) { /* Get a new picture */ if( p_sys->p_va == NULL ) p_pic = ffmpeg_NewPictBuf( p_dec, p_context ); if( !p_pic ) { av_frame_unref(frame); break; } /* Fill picture_t from AVFrame */ lavc_CopyPicture(p_dec, p_pic, frame); } else { if( p_sys->p_va != NULL ) vlc_va_Extract( p_sys->p_va, p_pic, frame->data[3] ); picture_Hold( p_pic ); } if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den ) { /* Fetch again the aspect ratio in case it changed */ p_dec->fmt_out.video.i_sar_num = p_context->sample_aspect_ratio.num; p_dec->fmt_out.video.i_sar_den = p_context->sample_aspect_ratio.den; if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den ) { p_dec->fmt_out.video.i_sar_num = 1; p_dec->fmt_out.video.i_sar_den = 1; } } p_pic->date = i_pts; /* Hack to force display of still pictures */ p_pic->b_force = p_sys->b_first_frame; p_pic->i_nb_fields = 2 + frame->repeat_pict; p_pic->b_progressive = !frame->interlaced_frame; p_pic->b_top_field_first = frame->top_field_first; av_frame_unref(frame); /* Send decoded frame to vout */ if (i_pts > VLC_TS_INVALID) { p_sys->b_first_frame = false; return p_pic; } else picture_Release( p_pic ); } if( p_block ) block_Release( p_block ); return NULL; }
/***************************************************************************** * RunDecoder: the libmpeg2 decoder *****************************************************************************/ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; mpeg2_state_t state; picture_t *p_pic; block_t *p_block; if( !pp_block || !*pp_block ) return NULL; p_block = *pp_block; if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED) ) { cc_Flush( &p_sys->cc ); mpeg2_reset( p_sys->p_mpeg2dec, p_sys->p_info->sequence != NULL ); DpbClean( p_dec ); } while( 1 ) { state = mpeg2_parse( p_sys->p_mpeg2dec ); switch( state ) { case STATE_SEQUENCE: { /* */ DpbClean( p_dec ); /* */ mpeg2_custom_fbuf( p_sys->p_mpeg2dec, 1 ); /* Set the first 2 reference frames */ GetAR( p_dec ); for( int i = 0; i < 2; i++ ) { picture_t *p_picture = DpbNewPicture( p_dec ); if( !p_picture ) { /* Is it ok ? or do we need a reset ? */ block_Release( p_block ); return NULL; } PutPicture( p_dec, p_picture ); } if( p_sys->p_synchro ) decoder_SynchroRelease( p_sys->p_synchro ); p_sys->p_synchro = decoder_SynchroInit( p_dec, (uint32_t)((uint64_t)1001000000 * 27 / p_sys->p_info->sequence->frame_period) ); p_sys->b_after_sequence_header = true; break; } case STATE_GOP: /* There can be userdata in a GOP. It needs to be remembered for the next picture. */ if( p_sys->p_info->user_data_len > 2 ) { free( p_sys->p_gop_user_data ); p_sys->p_gop_user_data = calloc( p_sys->p_info->user_data_len, sizeof(uint8_t) ); if( p_sys->p_gop_user_data ) { p_sys->i_gop_user_data = p_sys->p_info->user_data_len; memcpy( p_sys->p_gop_user_data, p_sys->p_info->user_data, p_sys->p_info->user_data_len ); } } break; case STATE_PICTURE: { const mpeg2_info_t *p_info = p_sys->p_info; const mpeg2_picture_t *p_current = p_info->current_picture; mtime_t i_pts, i_dts; if( p_sys->b_after_sequence_header && (p_current->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_P ) { /* Intra-slice refresh. Simulate a blank I picture. */ msg_Dbg( p_dec, "intra-slice refresh stream" ); decoder_SynchroNewPicture( p_sys->p_synchro, I_CODING_TYPE, 2, 0, 0, p_info->sequence->flags & SEQ_FLAG_LOW_DELAY ); decoder_SynchroDecode( p_sys->p_synchro ); decoder_SynchroEnd( p_sys->p_synchro, I_CODING_TYPE, 0 ); p_sys->b_slice_i = true; } p_sys->b_after_sequence_header = false; #ifdef PIC_FLAG_PTS i_pts = p_current->flags & PIC_FLAG_PTS ? ( ( p_current->pts == (uint32_t)p_sys->i_current_pts ) ? p_sys->i_current_pts : p_sys->i_previous_pts ) : 0; i_dts = 0; /* Hack to handle demuxers which only have DTS timestamps */ if( !i_pts && !p_block->i_pts && p_block->i_dts > 0 ) { if( p_info->sequence->flags & SEQ_FLAG_LOW_DELAY || (p_current->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_B ) { i_pts = p_block->i_dts; } } p_block->i_pts = p_block->i_dts = 0; /* End hack */ #else /* New interface */ i_pts = p_current->flags & PIC_FLAG_TAGS ? ( ( p_current->tag == (uint32_t)p_sys->i_current_pts ) ? p_sys->i_current_pts : p_sys->i_previous_pts ) : 0; i_dts = p_current->flags & PIC_FLAG_TAGS ? ( ( p_current->tag2 == (uint32_t)p_sys->i_current_dts ) ? p_sys->i_current_dts : p_sys->i_previous_dts ) : 0; #endif /* If nb_fields == 1, it is a field picture, and it will be * followed by another field picture for which we won't call * decoder_SynchroNewPicture() because this would have other * problems, so we take it into account here. * This kind of sucks, but I didn't think better. --Meuuh */ decoder_SynchroNewPicture( p_sys->p_synchro, p_current->flags & PIC_MASK_CODING_TYPE, p_current->nb_fields == 1 ? 2 : p_current->nb_fields, i_pts, i_dts, p_info->sequence->flags & SEQ_FLAG_LOW_DELAY ); bool b_skip = false; if( !p_dec->b_pace_control && !p_sys->b_preroll && !(p_sys->b_slice_i && ((p_current->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_P)) && !decoder_SynchroChoose( p_sys->p_synchro, p_current->flags & PIC_MASK_CODING_TYPE, /*p_sys->p_vout->render_time*/ 0 /*FIXME*/, p_info->sequence->flags & SEQ_FLAG_LOW_DELAY ) ) { b_skip = true; } p_pic = NULL; if( !b_skip ) p_pic = DpbNewPicture( p_dec ); if( b_skip || !p_pic ) { mpeg2_skip( p_sys->p_mpeg2dec, 1 ); p_sys->b_skip = true; decoder_SynchroTrash( p_sys->p_synchro ); PutPicture( p_dec, NULL ); if( !b_skip ) { block_Release( p_block ); return NULL; } } else { mpeg2_skip( p_sys->p_mpeg2dec, 0 ); p_sys->b_skip = false; decoder_SynchroDecode( p_sys->p_synchro ); PutPicture( p_dec, p_pic ); } if( p_info->user_data_len > 2 || p_sys->i_gop_user_data > 2 ) { p_sys->i_cc_pts = i_pts; p_sys->i_cc_dts = i_dts; if( (p_current->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_P ) p_sys->i_cc_flags = BLOCK_FLAG_TYPE_P; else if( (p_current->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_B ) p_sys->i_cc_flags = BLOCK_FLAG_TYPE_B; else p_sys->i_cc_flags = BLOCK_FLAG_TYPE_I; if( p_sys->i_gop_user_data > 2 ) { /* We now have picture info for any cached user_data out of the gop */ cc_Extract( &p_sys->cc, &p_sys->p_gop_user_data[0], p_sys->i_gop_user_data ); p_sys->i_gop_user_data = 0; } /* Extract the CC from the user_data of the picture */ if( p_info->user_data_len > 2 ) cc_Extract( &p_sys->cc, &p_info->user_data[0], p_info->user_data_len ); } } break; case STATE_BUFFER: if( !p_block->i_buffer ) { block_Release( p_block ); return NULL; } if( (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED)) && p_sys->p_synchro && p_sys->p_info->sequence && p_sys->p_info->sequence->width != (unsigned)-1 ) { decoder_SynchroReset( p_sys->p_synchro ); if( p_sys->p_info->current_fbuf != NULL && p_sys->p_info->current_fbuf->id != NULL ) { p_sys->b_garbage_pic = true; } if( p_sys->b_slice_i ) { decoder_SynchroNewPicture( p_sys->p_synchro, I_CODING_TYPE, 2, 0, 0, p_sys->p_info->sequence->flags & SEQ_FLAG_LOW_DELAY ); decoder_SynchroDecode( p_sys->p_synchro ); decoder_SynchroEnd( p_sys->p_synchro, I_CODING_TYPE, 0 ); } } if( p_block->i_flags & BLOCK_FLAG_PREROLL ) { p_sys->b_preroll = true; } else if( p_sys->b_preroll ) { p_sys->b_preroll = false; if( p_sys->p_synchro ) decoder_SynchroReset( p_sys->p_synchro ); } #ifdef PIC_FLAG_PTS if( p_block->i_pts ) { mpeg2_pts( p_sys->p_mpeg2dec, (uint32_t)p_block->i_pts ); #else /* New interface */ if( p_block->i_pts || p_block->i_dts ) { mpeg2_tag_picture( p_sys->p_mpeg2dec, (uint32_t)p_block->i_pts, (uint32_t)p_block->i_dts ); #endif p_sys->i_previous_pts = p_sys->i_current_pts; p_sys->i_current_pts = p_block->i_pts; p_sys->i_previous_dts = p_sys->i_current_dts; p_sys->i_current_dts = p_block->i_dts; } mpeg2_buffer( p_sys->p_mpeg2dec, p_block->p_buffer, p_block->p_buffer + p_block->i_buffer ); p_block->i_buffer = 0; break; #if MPEG2_RELEASE >= MPEG2_VERSION (0, 5, 0) case STATE_SEQUENCE_MODIFIED: GetAR( p_dec ); break; #endif case STATE_PICTURE_2ND: p_sys->b_second_field = true; break; case STATE_INVALID_END: case STATE_END: case STATE_SLICE: p_pic = NULL; if( p_sys->p_info->display_fbuf && p_sys->p_info->display_fbuf->id ) { p_pic = p_sys->p_info->display_fbuf->id; DpbDisplayPicture( p_dec, p_pic ); decoder_SynchroEnd( p_sys->p_synchro, p_sys->p_info->display_picture->flags & PIC_MASK_CODING_TYPE, p_sys->b_garbage_pic ); p_pic->date = decoder_SynchroDate( p_sys->p_synchro ); if( p_sys->b_garbage_pic ) p_pic->date = 0; /* ??? */ p_sys->b_garbage_pic = false; } if( p_sys->p_info->discard_fbuf && p_sys->p_info->discard_fbuf->id ) { DpbUnlinkPicture( p_dec, p_sys->p_info->discard_fbuf->id ); } /* For still frames */ if( state == STATE_END && p_pic ) p_pic->b_force = true; if( p_pic ) { /* Avoid frames with identical timestamps. * Especially needed for still frames in DVD menus. */ if( p_sys->i_last_frame_pts == p_pic->date ) p_pic->date++; p_sys->i_last_frame_pts = p_pic->date; return p_pic; } break; case STATE_INVALID: { msg_Err( p_dec, "invalid picture encountered" ); /* I don't think we have anything to do, but well without * docs ... */ break; } default: break; } } /* Never reached */ return NULL; } /***************************************************************************** * CloseDecoder: libmpeg2 decoder destruction *****************************************************************************/ static void CloseDecoder( vlc_object_t *p_this ) { decoder_t *p_dec = (decoder_t *)p_this; decoder_sys_t *p_sys = p_dec->p_sys; DpbClean( p_dec ); free( p_sys->p_gop_user_data ); if( p_sys->p_synchro ) decoder_SynchroRelease( p_sys->p_synchro ); if( p_sys->p_mpeg2dec ) mpeg2_close( p_sys->p_mpeg2dec ); free( p_sys ); }
static int ProcessOutputStream(decoder_t *p_dec, DWORD stream_id) { decoder_sys_t *p_sys = p_dec->p_sys; HRESULT hr; picture_t *picture = NULL; block_t *aout_buffer = NULL; DWORD output_status = 0; MFT_OUTPUT_DATA_BUFFER output_buffer = { stream_id, p_sys->output_sample, 0, NULL }; hr = IMFTransform_ProcessOutput(p_sys->mft, 0, 1, &output_buffer, &output_status); if (output_buffer.pEvents) IMFCollection_Release(output_buffer.pEvents); /* Use the returned sample since it can be provided by the MFT. */ IMFSample *output_sample = output_buffer.pSample; if (hr == S_OK) { if (!output_sample) return VLC_SUCCESS; LONGLONG sample_time; hr = IMFSample_GetSampleTime(output_sample, &sample_time); if (FAILED(hr)) goto error; /* Convert from 100 nanoseconds unit to microseconds. */ sample_time /= 10; DWORD total_length = 0; hr = IMFSample_GetTotalLength(output_sample, &total_length); if (FAILED(hr)) goto error; if (p_dec->fmt_in.i_cat == VIDEO_ES) { if (decoder_UpdateVideoFormat(p_dec)) return VLC_SUCCESS; picture = decoder_NewPicture(p_dec); if (!picture) return VLC_SUCCESS; UINT32 interlaced = false; hr = IMFSample_GetUINT32(output_sample, &MFSampleExtension_Interlaced, &interlaced); picture->b_progressive = !interlaced; picture->date = sample_time; } else { if (decoder_UpdateAudioFormat(p_dec)) goto error; if (p_dec->fmt_out.audio.i_bitspersample == 0 || p_dec->fmt_out.audio.i_channels == 0) goto error; int samples = total_length / (p_dec->fmt_out.audio.i_bitspersample * p_dec->fmt_out.audio.i_channels / 8); aout_buffer = decoder_NewAudioBuffer(p_dec, samples); if (!aout_buffer) return VLC_SUCCESS; if (aout_buffer->i_buffer < total_length) goto error; aout_buffer->i_pts = sample_time; } IMFMediaBuffer *output_media_buffer = NULL; hr = IMFSample_GetBufferByIndex(output_sample, 0, &output_media_buffer); BYTE *buffer_start; hr = IMFMediaBuffer_Lock(output_media_buffer, &buffer_start, NULL, NULL); if (FAILED(hr)) goto error; if (p_dec->fmt_in.i_cat == VIDEO_ES) CopyPackedBufferToPicture(picture, buffer_start); else memcpy(aout_buffer->p_buffer, buffer_start, total_length); hr = IMFMediaBuffer_Unlock(output_media_buffer); IMFSample_Release(output_media_buffer); if (FAILED(hr)) goto error; if (p_sys->output_sample) { /* Sample is not provided by the MFT: clear its content. */ hr = IMFMediaBuffer_SetCurrentLength(output_media_buffer, 0); if (FAILED(hr)) goto error; } else { /* Sample is provided by the MFT: decrease refcount. */ IMFSample_Release(output_sample); } } else if (hr == MF_E_TRANSFORM_STREAM_CHANGE || hr == MF_E_TRANSFORM_TYPE_NOT_SET) { if (p_sys->output_type) IMFMediaType_Release(p_sys->output_type); if (SetOutputType(p_dec, p_sys->output_stream_id, &p_sys->output_type)) goto error; /* Reallocate output sample. */ if (p_sys->output_sample) IMFSample_Release(p_sys->output_sample); p_sys->output_sample = NULL; if (AllocateOutputSample(p_dec, 0, &p_sys->output_sample)) goto error; return VLC_SUCCESS; } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { return VLC_SUCCESS; } else /* An error not listed above occurred */ { msg_Err(p_dec, "Unexpected error in IMFTransform::ProcessOutput: %#lx", hr); goto error; } if (p_dec->fmt_in.i_cat == VIDEO_ES) decoder_QueueVideo(p_dec, picture); else decoder_QueueAudio(p_dec, aout_buffer); return VLC_SUCCESS; error: msg_Err(p_dec, "Error in ProcessOutputStream()"); if (picture) picture_Release(picture); if (aout_buffer) block_Release(aout_buffer); return VLC_EGENERIC; }