static bool try_open(struct MPContext *mpctx, char *filename) { struct bstr bfilename = bstr0(filename); // Avoid trying to open itself or another .cue file. Best would be // to check the result of demuxer auto-detection, but the demuxer // API doesn't allow this without opening a full demuxer. if (bstr_case_endswith(bfilename, bstr0(".cue")) || bstrcasecmp(bstr0(mpctx->demuxer->filename), bfilename) == 0) return false; struct stream *s = stream_open(filename, mpctx->opts); if (!s) return false; struct demuxer *d = demux_open(s, NULL, NULL, mpctx->opts); // Since .bin files are raw PCM data with no headers, we have to explicitly // open them. Also, try to avoid to open files that are most likely not .bin // files, as that would only play noise. Checking the file extension is // fragile, but it's about the only way we have. // TODO: maybe also could check if the .bin file is a multiple of the Audio // CD sector size (2352 bytes) if (!d && bstr_case_endswith(bfilename, bstr0(".bin"))) { mp_msg(MSGT_CPLAYER, MSGL_WARN, "CUE: Opening as BIN file!\n"); d = demux_open(s, "rawaudio", NULL, mpctx->opts); } if (d) { add_source(mpctx, d); return true; } mp_msg(MSGT_CPLAYER, MSGL_ERR, "Could not open source '%s'!\n", filename); free_stream(s); return false; }
static int enable_cache(struct MPContext *mpctx, struct stream **stream, struct demuxer **demuxer, struct demuxer_params *params) { struct MPOpts *opts = mpctx->opts; if (opts->stream_cache_size <= 0) return 0; char *filename = talloc_strdup(NULL, (*demuxer)->filename); free_demuxer(*demuxer); free_stream(*stream); *stream = stream_open(filename, opts); if (!*stream) { talloc_free(filename); return -1; } stream_enable_cache_percent(stream, opts->stream_cache_size, opts->stream_cache_def_size, opts->stream_cache_min_percent, opts->stream_cache_seek_min_percent); *demuxer = demux_open(*stream, "mkv", params, opts); if (!*demuxer) { talloc_free(filename); free_stream(*stream); return -1; } talloc_free(filename); return 1; }
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; char *demux = "+lavf"; if (demuxer->stream->uncached_type == STREAMTYPE_CDDA) demux = "+rawaudio"; char *t = NULL; stream_control(demuxer->stream, STREAM_CTRL_GET_DISC_NAME, &t); if (t) { mp_tags_set_bstr(demuxer->metadata, bstr0("TITLE"), bstr0(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, demux, NULL, 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); // Can be seekable even if the stream isn't. demuxer->seekable = 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. demuxer->stream->seekable = false; add_dvd_streams(demuxer); add_streams(demuxer); add_stream_chapters(demuxer); return 0; }
struct playlist *playlist_parse_file(const char *file, struct mpv_global *global) { struct mp_log *log = mp_log_new(NULL, global->log, "!playlist_parser"); mp_verbose(log, "Parsing playlist file %s...\n", file); struct playlist *ret = NULL; stream_t *stream = stream_open(file, global); if(!stream) { mp_err(log, "Error while opening playlist file %s\n", file); talloc_free(log); return NULL; } struct demuxer *pl_demux = demux_open(stream, "playlist", NULL, global); if (pl_demux && pl_demux->playlist) { ret = talloc_zero(NULL, struct playlist); playlist_transfer_entries(ret, pl_demux->playlist); }
// segment = get Nth segment of a multi-segment file static bool check_file_seg(struct MPContext *mpctx, struct demuxer **sources, int num_sources, unsigned char uid_map[][16], char *filename, int segment) { bool was_valid = false; struct demuxer_params params = { .matroska_wanted_uids = uid_map, .matroska_wanted_segment = segment, .matroska_was_valid = &was_valid, }; struct stream *s = stream_open(filename, mpctx->opts); if (!s) return false; struct demuxer *d = demux_open(s, "mkv", ¶ms, mpctx->opts); if (!d) { free_stream(s); return was_valid; } if (d->type == DEMUXER_TYPE_MATROSKA) { for (int i = 1; i < num_sources; i++) { if (sources[i]) continue; if (!memcmp(uid_map[i], d->matroska_data.segment_uid, 16)) { mp_msg(MSGT_CPLAYER, MSGL_INFO, "Match for source %d: %s\n", i, d->filename); if (enable_cache(mpctx, &s, &d, ¶ms) < 0) continue; sources[i] = d; return true; } } } free_demuxer(d); free_stream(s); return was_valid; }
extern "C" demuxer_t* demux_open_rtp(demuxer_t* demuxer) { struct MPOpts *opts = demuxer->opts; Boolean success = False; do { TaskScheduler* scheduler = BasicTaskScheduler::createNew(); if (scheduler == NULL) break; UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler); if (env == NULL) break; RTSPClient* rtspClient = NULL; SIPClient* sipClient = NULL; if (demuxer == NULL || demuxer->stream == NULL) break; // shouldn't happen demuxer->stream->eof = 0; // just in case // Look at the stream's 'priv' field to see if we were initiated // via a SDP description: char* sdpDescription = (char*)(demuxer->stream->priv); if (sdpDescription == NULL) { // We weren't given a SDP description directly, so assume that // we were given a RTSP or SIP URL: char const* protocol = demuxer->stream->streaming_ctrl->url->protocol; char const* url = demuxer->stream->streaming_ctrl->url->url; extern int verbose; if (strcmp(protocol, "rtsp") == 0) { rtspClient = RTSPClient::createNew(*env, verbose, "MPlayer"); if (rtspClient == NULL) { fprintf(stderr, "Failed to create RTSP client: %s\n", env->getResultMsg()); break; } sdpDescription = openURL_rtsp(rtspClient, url); } else { // SIP unsigned char desiredAudioType = 0; // PCMU (use 3 for GSM) sipClient = SIPClient::createNew(*env, desiredAudioType, NULL, verbose, "MPlayer"); if (sipClient == NULL) { fprintf(stderr, "Failed to create SIP client: %s\n", env->getResultMsg()); break; } sipClient->setClientStartPortNum(8000); sdpDescription = openURL_sip(sipClient, url); } if (sdpDescription == NULL) { fprintf(stderr, "Failed to get a SDP description from URL \"%s\": %s\n", url, env->getResultMsg()); break; } } // Now that we have a SDP description, create a MediaSession from it: MediaSession* mediaSession = MediaSession::createNew(*env, sdpDescription); if (mediaSession == NULL) break; // Create a 'RTPState' structure containing the state that we just created, // and store it in the demuxer's 'priv' field, for future reference: RTPState* rtpState = new RTPState; rtpState->sdpDescription = sdpDescription; rtpState->rtspClient = rtspClient; rtpState->sipClient = sipClient; rtpState->mediaSession = mediaSession; rtpState->audioBufferQueue = rtpState->videoBufferQueue = NULL; rtpState->flags = 0; rtpState->firstSyncTime.tv_sec = rtpState->firstSyncTime.tv_usec = 0; demuxer->priv = rtpState; int audiofound = 0, videofound = 0; // Create RTP receivers (sources) for each subsession: MediaSubsessionIterator iter(*mediaSession); MediaSubsession* subsession; unsigned desiredReceiveBufferSize; while ((subsession = iter.next()) != NULL) { // Ignore any subsession that's not audio or video: if (strcmp(subsession->mediumName(), "audio") == 0) { if (audiofound) { fprintf(stderr, "Additional subsession \"audio/%s\" skipped\n", subsession->codecName()); continue; } desiredReceiveBufferSize = 100000; } else if (strcmp(subsession->mediumName(), "video") == 0) { if (videofound) { fprintf(stderr, "Additional subsession \"video/%s\" skipped\n", subsession->codecName()); continue; } desiredReceiveBufferSize = 2000000; } else { continue; } if (rtsp_port) subsession->setClientPortNum (rtsp_port); if (!subsession->initiate()) { fprintf(stderr, "Failed to initiate \"%s/%s\" RTP subsession: %s\n", subsession->mediumName(), subsession->codecName(), env->getResultMsg()); } else { fprintf(stderr, "Initiated \"%s/%s\" RTP subsession on port %d\n", subsession->mediumName(), subsession->codecName(), subsession->clientPortNum()); // Set the OS's socket receive buffer sufficiently large to avoid // incoming packets getting dropped between successive reads from this // subsession's demuxer. Depending on the bitrate(s) that you expect, // you may wish to tweak the "desiredReceiveBufferSize" values above. int rtpSocketNum = subsession->rtpSource()->RTPgs()->socketNum(); int receiveBufferSize = increaseReceiveBufferTo(*env, rtpSocketNum, desiredReceiveBufferSize); if (verbose > 0) { fprintf(stderr, "Increased %s socket receive buffer to %d bytes \n", subsession->mediumName(), receiveBufferSize); } if (rtspClient != NULL) { // Issue a RTSP "SETUP" command on the chosen subsession: if (!rtspClient->setupMediaSubsession(*subsession, False, rtsp_transport_tcp)) break; if (!strcmp(subsession->mediumName(), "audio")) audiofound = 1; if (!strcmp(subsession->mediumName(), "video")) videofound = 1; } } } if (rtspClient != NULL) { // Issue a RTSP aggregate "PLAY" command on the whole session: if (!rtspClient->playMediaSession(*mediaSession)) break; } else if (sipClient != NULL) { sipClient->sendACK(); // to start the stream flowing } // Now that the session is ready to be read, do additional // MPlayer codec-specific initialization on each subsession: iter.reset(); while ((subsession = iter.next()) != NULL) { if (subsession->readSource() == NULL) continue; // not reading this unsigned flags = 0; if (strcmp(subsession->mediumName(), "audio") == 0) { rtpState->audioBufferQueue = new ReadBufferQueue(subsession, demuxer, "audio"); rtpState->audioBufferQueue->otherQueue = &(rtpState->videoBufferQueue); rtpCodecInitialize_audio(demuxer, subsession, flags); } else if (strcmp(subsession->mediumName(), "video") == 0) { rtpState->videoBufferQueue = new ReadBufferQueue(subsession, demuxer, "video"); rtpState->videoBufferQueue->otherQueue = &(rtpState->audioBufferQueue); rtpCodecInitialize_video(demuxer, subsession, flags); } rtpState->flags |= flags; } success = True; } while (0); if (!success) return NULL; // an error occurred // Hack: If audio and video are demuxed together on a single RTP stream, // then create a new "demuxer_t" structure to allow the higher-level // code to recognize this: if (demux_is_multiplexed_rtp_stream(demuxer)) { stream_t* s = new_ds_stream(demuxer->video); demuxer_t* od = demux_open(opts, s, DEMUXER_TYPE_UNKNOWN, opts->audio_id, opts->video_id, opts->sub_id, NULL); demuxer = new_demuxers_demuxer(od, od, od); } return demuxer; }
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, };