static void reset_eof(demuxer_t *demuxer) { stream_reset(demuxer->stream); demux_flush(demuxer); demuxer->video->eof=0; demuxer->audio->eof=0; demuxer->sub->eof=0; }
int demuxer_seek_chapter(demuxer_t *demuxer, int chapter, double *seek_pts) { int ris; if (!demuxer->num_chapters || !demuxer->chapters) { demux_flush(demuxer); ris = stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_CHAPTER, &chapter); if (ris != STREAM_UNSUPPORTED) demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL); // exit status may be ok, but main() doesn't have to seek itself // (because e.g. dvds depend on sectors, not on pts) *seek_pts = -1.0; return ris != STREAM_UNSUPPORTED ? chapter : -1; } else { // chapters structure is set in the demuxer if (chapter >= demuxer->num_chapters) return -1; if (chapter < 0) chapter = 0; *seek_pts = demuxer->chapters[chapter].start / 1e9; return chapter; } }
int demux_seek(demuxer_t *demuxer, float rel_seek_secs, float audio_delay, int flags) { if (!demuxer->seekable) { if (demuxer->file_format == DEMUXER_TYPE_AVI) mp_tmsg(MSGT_SEEK, MSGL_WARN, "Cannot seek in raw AVI streams. (Index required, try with the -idx switch.)\n"); #ifdef CONFIG_TV else if (demuxer->file_format == DEMUXER_TYPE_TV) mp_tmsg(MSGT_SEEK, MSGL_WARN, "TV input is not seekable! (Seeking will probably be for changing channels ;)\n"); #endif else mp_tmsg(MSGT_SEEK, MSGL_WARN, "Cannot seek in this file.\n"); return 0; } // clear demux buffers: demux_flush(demuxer); demuxer->video->eof = 0; demuxer->audio->eof = 0; demuxer->sub->eof = 0; /* HACK: assume any demuxer used with these streams can cope with * the stream layer suddenly seeking to a different position under it * (nothing actually implements DEMUXER_CTRL_RESYNC now). */ struct stream *stream = demuxer->stream; if (stream->type == STREAMTYPE_DVD || stream->type == STREAMTYPE_DVDNAV) { double pts; if (flags & SEEK_ABSOLUTE) pts = 0.0f; else { if (demuxer->stream_pts == MP_NOPTS_VALUE) goto dmx_seek; pts = demuxer->stream_pts; } if (flags & SEEK_FACTOR) { double tmp = 0; if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &tmp) == STREAM_UNSUPPORTED) goto dmx_seek; pts += tmp * rel_seek_secs; } else pts += rel_seek_secs; if (stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_TIME, &pts) != STREAM_UNSUPPORTED) { demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL); return 1; } } dmx_seek: if (demuxer->desc->seek) demuxer->desc->seek(demuxer, rel_seek_secs, audio_delay, flags); return 1; }
//returns the first pts found within TIME_STAMP_PROBE_LEN bytes after stream_pos in demuxer's stream. //if no pts is found or an error occurs, -1.0 is returned. //Packs are freed. static float read_first_mpeg_pts_at_position(demuxer_t* demuxer, off_t stream_pos) { stream_t *s = demuxer->stream; mpg_demuxer_t *mpg_d = demuxer->priv; float pts = -1.0; //the pts to return; float found_pts1; //the most recently found pts float found_pts2; //the pts found before found_pts1 float found_pts3; //the pts found before found_pts2 int found = 0; if(!mpg_d || stream_pos < 0) return pts; found_pts3 = found_pts2 = found_pts1 = mpg_d->last_pts; stream_seek(s, stream_pos); //We look for pts. //However, we do not stop at the first found one, as timestamps may reset //Therefore, we seek until we found three consecutive //pts within MAX_PTS_DIFF_FOR_CONSECUTIVE. while(found<3 && !s->eof && (fabsf(found_pts2-found_pts1) < MAX_PTS_DIFF_FOR_CONSECUTIVE) && (fabsf(found_pts3-found_pts2) < MAX_PTS_DIFF_FOR_CONSECUTIVE) && (stream_tell(s) < stream_pos + TIMESTAMP_PROBE_LEN) && ds_fill_buffer(demuxer->video)) { if(mpg_d->last_pts != found_pts1) { if(!found) found_pts3 = found_pts2 = found_pts1 = mpg_d->last_pts; //the most recently found pts else { found_pts3 = found_pts2; found_pts2 = found_pts1; found_pts1 = mpg_d->last_pts; } found++; } } if(found == 3) pts = found_pts3; //clean up from searching of first pts; demux_flush(demuxer); return pts; }
int demuxer_set_angle(demuxer_t *demuxer, int angle) { int ris, angles = -1; angles = demuxer_angles_count(demuxer); if ((angles < 1) || (angle > angles)) return -1; demux_flush(demuxer); ris = stream_control(demuxer->stream, STREAM_CTRL_SET_ANGLE, &angle); if (ris == STREAM_UNSUPPORTED) return -1; demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL); return angle; }
static int d_control(demuxer_t *demuxer, int cmd, void *arg) { struct priv *p = demuxer->priv; switch (cmd) { case DEMUXER_CTRL_GET_TIME_LENGTH: { double len; if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) < 1) break; *(double *)arg = len; return DEMUXER_CTRL_OK; } case DEMUXER_CTRL_RESYNC: demux_flush(p->slave); break; // relay to slave demuxer case DEMUXER_CTRL_SWITCHED_TRACKS: reselect_streams(demuxer); return DEMUXER_CTRL_OK; } return demux_control(p->slave, cmd, arg); }
static int d_open(demuxer_t *demuxer, enum demux_check check) { struct priv *p = demuxer->priv = talloc_zero(demuxer, struct priv); if (check != DEMUX_CHECK_FORCE) return -1; struct demuxer_params params = {.force_format = "+lavf"}; if (demuxer->stream->uncached_type == STREAMTYPE_CDDA) params.force_format = "+rawaudio"; char *t = NULL; stream_control(demuxer->stream, STREAM_CTRL_GET_DISC_NAME, &t); if (t) { mp_tags_set_str(demuxer->metadata, "TITLE", t); talloc_free(t); } // Initialize the playback time. We need to read _some_ data to get the // correct stream-layer time (at least with libdvdnav). stream_peek(demuxer->stream, 1); reset_pts(demuxer); p->slave = demux_open(demuxer->stream, ¶ms, demuxer->global); if (!p->slave) return -1; // So that we don't miss initial packets of delayed subtitle streams. demux_set_stream_autoselect(p->slave, true); // With cache enabled, the stream can be seekable. This causes demux_lavf.c // (actually libavformat/mpegts.c) to seek sometimes when reading a packet. // It does this to seek back a bit in case the current file position points // into the middle of a packet. if (demuxer->stream->uncached_type != STREAMTYPE_CDDA) { demuxer->stream->seekable = false; // Can be seekable even if the stream isn't. demuxer->seekable = true; demuxer->rel_seeks = true; } add_dvd_streams(demuxer); add_streams(demuxer); add_stream_chapters(demuxer); return 0; } static void d_close(demuxer_t *demuxer) { struct priv *p = demuxer->priv; free_demuxer(p->slave); } static int d_control(demuxer_t *demuxer, int cmd, void *arg) { struct priv *p = demuxer->priv; switch (cmd) { case DEMUXER_CTRL_GET_TIME_LENGTH: { double len; if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) < 1) break; *(double *)arg = len; return DEMUXER_CTRL_OK; } case DEMUXER_CTRL_RESYNC: demux_flush(p->slave); break; // relay to slave demuxer case DEMUXER_CTRL_SWITCHED_TRACKS: reselect_streams(demuxer); return DEMUXER_CTRL_OK; } return demux_control(p->slave, cmd, arg); } const demuxer_desc_t demuxer_desc_disc = { .name = "disc", .desc = "CD/DVD/BD wrapper", .fill_buffer = d_fill_buffer, .open = d_open, .close = d_close, .seek = d_seek, .control = d_control, };
void video_callback(mvp_widget_t *widget, char key) { int jump; long long offset, size; pts_sync_data_t async, vsync; av_state_t state; /* printf("**SSDEBUG: In video_callback and got key %d \n",key); */ if (!video_playing) return; if(showing_guide) { if(mvp_tvguide_callback(widget, key) == 1) return; } if ( video_functions->key != NULL ) { if ( video_functions->key(key) == 1 ) { return; } } switch (key) { case MVPW_KEY_GO: case MVPW_KEY_GUIDE: if(showing_guide == 0 && showing_guide == 0) { printf("In %s showing guide %d \n",__FUNCTION__, key); showing_guide = 1; mvp_tvguide_video_topright(1); mvp_tvguide_show(mythtv_livetv_program_list, mythtv_livetv_description, mythtv_livetv_clock); break; } /* if the guide button is pressed while guide is active fall through to go back to remove guide and return to TV */ case MVPW_KEY_TV: if(showing_guide == 1) { printf("In %s hiding guide %d \n", __FUNCTION__, key); showing_guide = 0; mvp_tvguide_video_topright(0); mvp_tvguide_hide(mythtv_livetv_program_list, mythtv_livetv_description, mythtv_livetv_clock); } break; case MVPW_KEY_STOP: case MVPW_KEY_EXIT: back_to_guide_menu(); new_live_tv = 0; break; case MVPW_KEY_PAUSE: if (av_pause()) { mvpw_show(pause_widget); mvpw_hide(ffwd_widget); paused = 1; if (pause_osd && !display_on && (display_on_alt < 2)) { display_on_alt = 2; enable_osd(); } screensaver_enable(); } else { if (pause_osd && !display_on && (display_on_alt == 2)) { display_on_alt = 0; disable_osd(); mvpw_expose(root); } av_get_state(&state); if (state.mute) mvpw_show(mute_widget); else mvpw_hide(mute_widget); mvpw_hide(pause_widget); paused = 0; screensaver_disable(); } break; case MVPW_KEY_PLAY: if ( paused ) { /* * play key can be used to un-pause */ av_pause(); if (pause_osd && !display_on && (display_on_alt == 2)) { display_on_alt = 0; disable_osd(); mvpw_expose(root); } mvpw_hide(pause_widget); mvpw_hide(mute_widget); paused = 0; screensaver_disable(); } break; case MVPW_KEY_REPLAY: seek_by(-30); timed_osd(seek_osd_timeout*1000); break; case MVPW_KEY_REWIND: seek_by(-10); timed_osd(seek_osd_timeout*1000); break; case MVPW_KEY_SKIP: if (mythtv_seek_amount == 0 ) { seek_by(30); } else if (mythtv_seek_amount == 1 ) { seek_by(60); } else { seek_by(30); } timed_osd(seek_osd_timeout*1000); break; case MVPW_KEY_FFWD: if (av_ffwd() == 0) { demux_flush(handle); demux_seek(handle); av_get_state(&state); av_stop(); av_reset(); if (state.mute) { av_set_mute(1); } av_play(); mvpw_hide(ffwd_widget); } else { av_get_state(&state); mvpw_show(ffwd_widget); mvpw_hide(pause_widget); screensaver_disable(); } timed_osd(seek_osd_timeout*1000); break; case MVPW_KEY_LEFT: if (video_functions->seek) { size = video_functions->size(); jump_target = -1; jumping = 1; if (video_write_thread) { pthread_kill(video_write_thread, SIGURG); } if (audio_write_thread) { pthread_kill(audio_write_thread, SIGURG); } offset = video_functions->seek(0, SEEK_CUR); jump_target = ((-size / 100.0) + offset); pthread_cond_broadcast(&video_cond); } break; case MVPW_KEY_RIGHT: if (video_functions->seek) { size = video_functions->size(); jump_target = -1; jumping = 1; if (video_write_thread) { pthread_kill(video_write_thread, SIGURG); } if (audio_write_thread) { pthread_kill(audio_write_thread, SIGURG); } offset = video_functions->seek(0, SEEK_CUR); jump_target = ((size / 100.0) + offset); pthread_cond_broadcast(&video_cond); timed_osd(seek_osd_timeout*1000); } break; case MVPW_KEY_ZERO ... MVPW_KEY_NINE: if(new_live_tv) { printf("In %s showing guide %d \n",__FUNCTION__, key); showing_guide = 1; mvp_tvguide_video_topright(1); mvp_tvguide_show(mythtv_livetv_program_list, mythtv_livetv_description, mythtv_livetv_clock); mvp_tvguide_callback(widget, key); } else if (mythtv_livetv) { back_to_guide_menu(); mythtv_key_callback(mythtv_browser, key); } else { size = video_functions->size(); jump_target = -1; jumping = 1; if (video_write_thread) { pthread_kill(video_write_thread, SIGURG); } if (audio_write_thread) { pthread_kill(audio_write_thread, SIGURG); } jump = key; jump_target = size * (jump / 10.0); pthread_cond_broadcast(&video_cond); timed_osd(seek_osd_timeout*1000); } break; case MVPW_KEY_MENU: mvpw_show(popup_menu); mvpw_focus(popup_menu); break; case MVPW_KEY_MUTE: if (mvpw_visible(ffwd_widget)) { mvpw_hide(mute_widget); break; } if (av_mute() == 1) { mvpw_show(mute_widget); } else { mvpw_hide(mute_widget); } break; case MVPW_KEY_BLANK: case MVPW_KEY_OK: if (display_on || display_on_alt) { disable_osd(); mvpw_expose(root); display_on = 1; display_on_alt = 0; } else { enable_osd(); } display_on = !display_on; break; case MVPW_KEY_FULL: case MVPW_KEY_PREV_CHAN: if(IS_4x3(av_get_tv_aspect())) { if(av_get_tv_aspect() == AV_TV_ASPECT_4x3_CCO) av_set_tv_aspect(AV_TV_ASPECT_4x3); else av_set_tv_aspect(AV_TV_ASPECT_4x3_CCO); } break; case MVPW_KEY_CHAN_UP: case MVPW_KEY_UP: if (mythtv_livetv) mythtv_channel_up(); break; case MVPW_KEY_CHAN_DOWN: case MVPW_KEY_DOWN: if (mythtv_livetv) mythtv_channel_down(); break; case MVPW_KEY_RECORD: /* * XXX: This is a temporary hack until we figure out how * to tell when the audio and video are out of sync, * and correct it automatically. */ av_get_audio_sync(&async); av_get_video_sync(&vsync); printf("PRE SYNC: a 0x%"PRIx64" 0x%"PRIx64" v 0x%"PRIx64" 0x%"PRIx64"\n", async.stc, async.pts, vsync.stc, vsync.pts); av_delay_video(1000); av_get_audio_sync(&async); av_get_video_sync(&vsync); printf("POST SYNC: a 0x%"PRIx64" 0x%"PRIx64" v 0x%"PRIx64" 0x%"PRIx64"\n", async.stc, async.pts, vsync.stc, vsync.pts); break; case MVPW_KEY_VOL_UP: case MVPW_KEY_VOL_DOWN: volume_key_callback(volume_dialog, key); mvpw_show(volume_dialog); mvpw_set_timer(volume_dialog, timer_hide, 3000); break; default: PRINTF("button %d\n", key); break; } }
static int do_seek(void) { demux_attr_t *attr; int seconds, new_seek_Bps; long long offset; struct timeval now, delta; static int count = 0; count++; attr = demux_get_attr(handle); gettimeofday(&now, NULL); timersub(&now, &seek_timeval, &delta); /* * Give up after 2 seconds */ if ((delta.tv_sec >= 2) && seeking) { seeking = 0; printf("SEEK ABORTED (%lu.%.2lu) %d\n", delta.tv_sec, delta.tv_usec/10000, count); count = 0; return 0; } if (!attr->gop_valid) { if ( --seek_attempts > 0 ) { PRINTF("GOP retry\n"); return -1; } printf("SEEK RETRY due to lack of GOP\n"); demux_flush(handle); demux_seek(handle); seek_attempts = 16; return -1; } if (pts_seek_attempts > 0) { seconds = attr->gop.pts/PTS_HZ; } else if (gop_seek_attempts > 0) { seconds = (attr->gop.hour * 3600) + (attr->gop.minute * 60) + attr->gop.second; } else { seconds = 0; } /* * Recompute Bps from actual time and position differences * provided the time difference is big enough */ if ( abs(seconds - seek_start_seconds) > SEEK_FUDGE ) { offset = video_functions->seek(0, SEEK_CUR); new_seek_Bps = (offset - seek_start_pos) / (seconds - seek_start_seconds); if ( new_seek_Bps > 10000 ) /* Sanity check */ seek_Bps = new_seek_Bps; } PRINTF("New Bps %d\n", seek_Bps); if ( abs(seconds - seek_seconds) <= SEEK_FUDGE ) { seeking = 0; printf("SEEK DONE: to %d at %d (%lu.%.2lu) %d\n", seek_seconds, seconds, delta.tv_sec, delta.tv_usec/10000, count); } else { offset = video_functions->seek(0, SEEK_CUR); PRINTF("RESEEK: From %lld + %d\n", offset, seek_Bps * (seek_seconds-seconds)); offset = video_functions->seek(seek_Bps * (seek_seconds-seconds), SEEK_CUR); demux_flush(handle); demux_seek(handle); seek_attempts--; PRINTF("SEEKING 1: %d/%d %lld\n", seconds, seek_seconds, offset); return -1; } count = 0; return 0; }