diff options
Diffstat (limited to 'video/decode/dec_video.c')
-rw-r--r-- | video/decode/dec_video.c | 220 |
1 files changed, 127 insertions, 93 deletions
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c index f28871c6bb..e8a57749ab 100644 --- a/video/decode/dec_video.c +++ b/video/decode/dec_video.c @@ -59,7 +59,6 @@ const vd_functions_t * const mpcodecs_vd_drivers[] = { void video_reset(struct dec_video *d_video) { video_vd_control(d_video, VDCTRL_RESET, NULL); - d_video->num_buffered_pts = 0; d_video->first_packet_pdts = MP_NOPTS_VALUE; d_video->start_pts = MP_NOPTS_VALUE; d_video->decoded_pts = MP_NOPTS_VALUE; @@ -67,8 +66,13 @@ void video_reset(struct dec_video *d_video) d_video->codec_dts = MP_NOPTS_VALUE; d_video->last_format = d_video->fixed_format = (struct mp_image_params){0}; d_video->dropped_frames = 0; - d_video->current_state = VIDEO_SKIP; + d_video->current_state = DATA_AGAIN; mp_image_unrefp(&d_video->current_mpi); + talloc_free(d_video->packet); + d_video->packet = NULL; + talloc_free(d_video->new_segment); + d_video->new_segment = NULL; + d_video->start = d_video->end = MP_NOPTS_VALUE; } int video_vd_control(struct dec_video *d_video, int cmd, void *arg) @@ -81,12 +85,16 @@ int video_vd_control(struct dec_video *d_video, int cmd, void *arg) void video_uninit(struct dec_video *d_video) { + if (!d_video) + return; mp_image_unrefp(&d_video->current_mpi); mp_image_unrefp(&d_video->cover_art_mpi); if (d_video->vd_driver) { MP_VERBOSE(d_video, "Uninit video.\n"); d_video->vd_driver->uninit(d_video); } + talloc_free(d_video->packet); + talloc_free(d_video->new_segment); talloc_free(d_video); } @@ -125,15 +133,17 @@ static const struct vd_functions *find_driver(const char *name) return NULL; } -bool video_init_best_codec(struct dec_video *d_video, char* video_decoders) +bool video_init_best_codec(struct dec_video *d_video) { + struct MPOpts *opts = d_video->opts; + assert(!d_video->vd_driver); video_reset(d_video); d_video->has_broken_packet_pts = -10; // needs 10 packets to reach decision struct mp_decoder_entry *decoder = NULL; struct mp_decoder_list *list = - mp_select_video_decoders(d_video->header->codec->codec, video_decoders); + mp_select_video_decoders(d_video->codec->codec, opts->video_decoders); mp_print_decoders(d_video->log, MSGL_V, "Codec list:", list); @@ -161,7 +171,7 @@ bool video_init_best_codec(struct dec_video *d_video, char* video_decoders) MP_VERBOSE(d_video, "Selected video codec: %s\n", d_video->decoder_desc); } else { MP_ERR(d_video, "Failed to initialize a video decoder for codec '%s'.\n", - d_video->header->codec->codec); + d_video->codec->codec); } if (d_video->header->missing_timestamps) { @@ -179,7 +189,7 @@ static void fix_image_params(struct dec_video *d_video, { struct MPOpts *opts = d_video->opts; struct mp_image_params p = *params; - struct mp_codec_params *c = d_video->header->codec; + struct mp_codec_params *c = d_video->codec; MP_VERBOSE(d_video, "Decoder format: %s\n", mp_image_params_to_str(params)); @@ -233,73 +243,35 @@ static void fix_image_params(struct dec_video *d_video, d_video->fixed_format = p; } -static void add_avi_pts(struct dec_video *d_video, double pts) -{ - if (pts != MP_NOPTS_VALUE) { - int delay = -1; - video_vd_control(d_video, VDCTRL_QUERY_UNSEEN_FRAMES, &delay); - if (delay >= 0 && delay < d_video->num_buffered_pts) - d_video->num_buffered_pts = delay; - if (d_video->num_buffered_pts == MP_ARRAY_SIZE(d_video->buffered_pts)) { - MP_ERR(d_video, "Too many buffered pts\n"); - } else { - for (int i = d_video->num_buffered_pts; i > 0; i--) - d_video->buffered_pts[i] = d_video->buffered_pts[i - 1]; - d_video->buffered_pts[0] = pts; - d_video->num_buffered_pts++; - } - } -} - -static double retrieve_avi_pts(struct dec_video *d_video, double codec_pts) -{ - if (d_video->num_buffered_pts) { - d_video->num_buffered_pts--; - return d_video->buffered_pts[d_video->num_buffered_pts]; - } - MP_ERR(d_video, "No pts value from demuxer to use for frame!\n"); - return MP_NOPTS_VALUE; -} - static struct mp_image *decode_packet(struct dec_video *d_video, struct demux_packet *packet, int drop_frame) { struct MPOpts *opts = d_video->opts; - bool avi_pts = d_video->header->codec->avi_dts && opts->correct_pts; - struct demux_packet packet_copy; - if (packet && packet->dts == MP_NOPTS_VALUE) { - packet_copy = *packet; - packet = &packet_copy; - packet->dts = packet->pts; - } + if (!d_video->vd_driver) + return NULL; double pkt_pts = packet ? packet->pts : MP_NOPTS_VALUE; double pkt_dts = packet ? packet->dts : MP_NOPTS_VALUE; + if (pkt_pts == MP_NOPTS_VALUE) + d_video->has_broken_packet_pts = 1; + double pkt_pdts = pkt_pts == MP_NOPTS_VALUE ? pkt_dts : pkt_pts; if (pkt_pdts != MP_NOPTS_VALUE && d_video->first_packet_pdts == MP_NOPTS_VALUE) d_video->first_packet_pdts = pkt_pdts; - if (avi_pts) - add_avi_pts(d_video, pkt_pdts); - - double prev_codec_pts = d_video->codec_pts; - double prev_codec_dts = d_video->codec_dts; - - if (d_video->header->codec->avi_dts) - drop_frame = 0; - MP_STATS(d_video, "start decode video"); struct mp_image *mpi = d_video->vd_driver->decode(d_video, packet, drop_frame); MP_STATS(d_video, "end decode video"); + // Error, discarded frame, dropped frame, or initial codec delay. if (!mpi || drop_frame) { talloc_free(mpi); - return NULL; // error / skipped frame + return NULL; } if (opts->field_dominance == 0) { @@ -310,30 +282,31 @@ static struct mp_image *decode_packet(struct dec_video *d_video, } // Note: the PTS is reordered, but the DTS is not. Both should be monotonic. - double pts = d_video->codec_pts; - double dts = d_video->codec_dts; + double pts = mpi->pts; + double dts = mpi->dts; - if (pts == MP_NOPTS_VALUE) { - d_video->codec_pts = prev_codec_pts; - } else if (pts < prev_codec_pts) { - d_video->num_codec_pts_problems++; + if (pts != MP_NOPTS_VALUE) { + if (pts < d_video->codec_pts) + d_video->num_codec_pts_problems++; + d_video->codec_pts = mpi->pts; } - if (dts == MP_NOPTS_VALUE) { - d_video->codec_dts = prev_codec_dts; - } else if (dts <= prev_codec_dts) { - d_video->num_codec_dts_problems++; + if (dts != MP_NOPTS_VALUE) { + if (dts <= d_video->codec_dts) + d_video->num_codec_dts_problems++; + d_video->codec_dts = mpi->dts; } + if (d_video->has_broken_packet_pts < 0) + d_video->has_broken_packet_pts++; + if (d_video->num_codec_pts_problems) + d_video->has_broken_packet_pts = 1; + // If PTS is unset, or non-monotonic, fall back to DTS. if ((d_video->num_codec_pts_problems > d_video->num_codec_dts_problems || pts == MP_NOPTS_VALUE) && dts != MP_NOPTS_VALUE) pts = dts; - // Alternative PTS determination methods - if (avi_pts) - pts = retrieve_avi_pts(d_video, pts); - if (!opts->correct_pts || pts == MP_NOPTS_VALUE) { if (opts->correct_pts && !d_video->header->missing_timestamps) MP_WARN(d_video, "No video PTS! Making something up.\n"); @@ -348,11 +321,6 @@ static struct mp_image *decode_packet(struct dec_video *d_video, } } - if (d_video->has_broken_packet_pts < 0) - d_video->has_broken_packet_pts++; - if (d_video->num_codec_pts_problems || pkt_pts == MP_NOPTS_VALUE) - d_video->has_broken_packet_pts = 1; - if (!mp_image_params_equal(&d_video->last_format, &mpi->params)) fix_image_params(d_video, &mpi->params); @@ -360,6 +328,16 @@ static struct mp_image *decode_packet(struct dec_video *d_video, mpi->pts = pts; d_video->decoded_pts = pts; + + // Compensate for incorrectly using mpeg-style DTS for avi timestamps. + if (d_video->codec->avi_dts && opts->correct_pts && + mpi->pts != MP_NOPTS_VALUE && d_video->fps > 0) + { + int delay = -1; + video_vd_control(d_video, VDCTRL_GET_BFRAMES, &delay); + mpi->pts -= MPMAX(delay, 0) / d_video->fps; + } + return mpi; } @@ -385,63 +363,119 @@ void video_work(struct dec_video *d_video) return; if (d_video->header->attached_picture) { - if (d_video->current_state == VIDEO_SKIP && !d_video->cover_art_mpi) { + if (d_video->current_state == DATA_AGAIN && !d_video->cover_art_mpi) { d_video->cover_art_mpi = decode_packet(d_video, d_video->header->attached_picture, 0); // Might need flush. if (!d_video->cover_art_mpi) d_video->cover_art_mpi = decode_packet(d_video, NULL, 0); - d_video->current_state = VIDEO_OK; + d_video->current_state = DATA_OK; } - if (d_video->current_state == VIDEO_OK) + if (d_video->current_state == DATA_OK) d_video->current_mpi = mp_image_new_ref(d_video->cover_art_mpi); - // (VIDEO_OK is returned the first time, when current_mpi is sill set) - d_video->current_state = VIDEO_EOF; + // (DATA_OK is returned the first time, when current_mpi is sill set) + d_video->current_state = DATA_EOF; return; } - struct demux_packet *pkt; - if (demux_read_packet_async(d_video->header, &pkt) == 0) { - d_video->current_state = VIDEO_WAIT; + if (!d_video->packet && !d_video->new_segment && + demux_read_packet_async(d_video->header, &d_video->packet) == 0) + { + d_video->current_state = DATA_WAIT; return; } + if (d_video->packet) { + if (d_video->packet->dts == MP_NOPTS_VALUE && !d_video->codec->avi_dts) + d_video->packet->dts = d_video->packet->pts; + } + + if (d_video->packet && d_video->packet->new_segment) { + assert(!d_video->new_segment); + d_video->new_segment = d_video->packet; + d_video->packet = NULL; + } + + bool had_input_packet = !!d_video->packet; + bool had_packet = had_input_packet || d_video->new_segment; + + double start_pts = d_video->start_pts; + if (d_video->start != MP_NOPTS_VALUE && (start_pts == MP_NOPTS_VALUE || + d_video->start > start_pts)) + start_pts = d_video->start; + int framedrop_type = d_video->framedrop_enabled ? 1 : 0; - if (d_video->start_pts != MP_NOPTS_VALUE && pkt && - pkt->pts < d_video->start_pts - .005 && + if (start_pts != MP_NOPTS_VALUE && d_video->packet && + d_video->packet->pts < start_pts - .005 && !d_video->has_broken_packet_pts) { framedrop_type = 2; } - d_video->current_mpi = decode_packet(d_video, pkt, framedrop_type); - bool had_packet = !!pkt; - talloc_free(pkt); + d_video->current_mpi = decode_packet(d_video, d_video->packet, framedrop_type); + if (d_video->packet && d_video->packet->len == 0) { + talloc_free(d_video->packet); + d_video->packet = NULL; + } - d_video->current_state = VIDEO_OK; + d_video->current_state = DATA_OK; if (!d_video->current_mpi) { - d_video->current_state = VIDEO_EOF; + d_video->current_state = DATA_EOF; if (had_packet) { if (framedrop_type == 1) d_video->dropped_frames += 1; - d_video->current_state = VIDEO_SKIP; + d_video->current_state = DATA_AGAIN; } } + + bool segment_ended = !d_video->current_mpi && !had_input_packet; + + if (d_video->current_mpi && d_video->current_mpi->pts != MP_NOPTS_VALUE) { + double vpts = d_video->current_mpi->pts; + segment_ended = d_video->end != MP_NOPTS_VALUE && vpts >= d_video->end; + if ((d_video->start != MP_NOPTS_VALUE && vpts < d_video->start) + || segment_ended) + { + talloc_free(d_video->current_mpi); + d_video->current_mpi = NULL; + } + } + + // If there's a new segment, start it as soon as we're drained/finished. + if (segment_ended && d_video->new_segment) { + struct demux_packet *new_segment = d_video->new_segment; + d_video->new_segment = NULL; + + // Could avoid decoder reinit; would still need flush. + d_video->codec = new_segment->codec; + if (d_video->vd_driver) + d_video->vd_driver->uninit(d_video); + d_video->vd_driver = NULL; + video_init_best_codec(d_video); + + d_video->start = new_segment->start; + d_video->end = new_segment->end; + + new_segment->new_segment = false; + + d_video->packet = new_segment; + d_video->current_state = DATA_AGAIN; + } } // Fetch an image decoded with video_work(). Returns one of: -// VIDEO_OK: *out_mpi is set to a new image -// VIDEO_WAIT: waiting for demuxer; will receive a wakeup signal -// VIDEO_EOF: end of file, no more frames to be expected -// VIDEO_SKIP: dropped frame or something similar +// DATA_OK: *out_mpi is set to a new image +// DATA_WAIT: waiting for demuxer; will receive a wakeup signal +// DATA_EOF: end of file, no more frames to be expected +// DATA_AGAIN: dropped frame or something similar int video_get_frame(struct dec_video *d_video, struct mp_image **out_mpi) { *out_mpi = NULL; if (d_video->current_mpi) { *out_mpi = d_video->current_mpi; d_video->current_mpi = NULL; - return VIDEO_OK; + return DATA_OK; } - if (d_video->current_state == VIDEO_OK) - return VIDEO_SKIP; + if (d_video->current_state == DATA_OK) + return DATA_AGAIN; return d_video->current_state; } |