/* Reads in buffers, parses them, reframes into one-buffer-per-ogg-page, submits * pages to output pad. */ static GstFlowReturn gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) { GstOggParse *ogg; GstFlowReturn result = GST_FLOW_OK; gint ret = -1; guint32 serialno; GstBuffer *pagebuffer; GstClockTime buffertimestamp = GST_BUFFER_TIMESTAMP (buffer); ogg = GST_OGG_PARSE (GST_OBJECT_PARENT (pad)); GST_LOG_OBJECT (ogg, "Chain function received buffer of size %d", GST_BUFFER_SIZE (buffer)); gst_ogg_parse_submit_buffer (ogg, buffer); while (ret != 0 && result == GST_FLOW_OK) { ogg_page page; /* We use ogg_sync_pageseek() rather than ogg_sync_pageout() so that we can * track how many bytes the ogg layer discarded (in the case of sync errors, * etc.); this allows us to accurately track the current stream offset */ ret = ogg_sync_pageseek (&ogg->sync, &page); if (ret == 0) { /* need more data, that's fine... */ break; } else if (ret < 0) { /* discontinuity; track how many bytes we skipped (-ret) */ ogg->offset -= ret; } else { #ifndef GST_DISABLE_GST_DEBUG gint64 granule = ogg_page_granulepos (&page); int bos = ogg_page_bos (&page); #endif guint64 startoffset = ogg->offset; GST_LOG_OBJECT (ogg, "Timestamping outgoing buffer as %" GST_TIME_FORMAT, GST_TIME_ARGS (buffertimestamp)); /* Turn our page into a GstBuffer TODO: better timestamps? Requires format * parsing. */ pagebuffer = gst_ogg_parse_buffer_from_page (&page, startoffset, FALSE, buffertimestamp); /* We read out 'ret' bytes, so we set the next offset appropriately */ ogg->offset += ret; serialno = ogg_page_serialno (&page); GST_LOG_OBJECT (ogg, "processing ogg page (serial %08x, pageno %ld, " "granule pos %" G_GUINT64_FORMAT ", bos %d, offset %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ")", serialno, ogg_page_pageno (&page), granule, bos, startoffset, ogg->offset); if (ogg_page_bos (&page)) { /* If we've seen this serialno before, this is technically an error, * we log this case but accept it - this one replaces the previous * stream with this serialno. We can do this since we're streaming, and * not supporting seeking... */ GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno); if (stream != NULL) { GST_LOG_OBJECT (ogg, "Incorrect stream; repeats serial number %u " "at offset %lld", serialno, ogg->offset); } if (ogg->last_page_not_bos) { GST_LOG_OBJECT (ogg, "Deleting all referenced streams, found a new " "chain starting with serial %u", serialno); gst_ogg_parse_delete_all_streams (ogg); } stream = gst_ogg_parse_new_stream (ogg, serialno); ogg->last_page_not_bos = FALSE; gst_buffer_ref (pagebuffer); stream->headers = g_slist_append (stream->headers, pagebuffer); if (!ogg->in_headers) { GST_LOG_OBJECT (ogg, "Found start of new chain at offset %llu", startoffset); ogg->in_headers = 1; } /* For now, we just keep the header buffer in the stream->headers list; * it actually gets output once we've collected the entire set */ } else { /* Non-BOS page. Either: we're outside headers, and this isn't a * header (normal data), outside headers and this is (error!), inside * headers, this is (append header), or inside headers and this isn't * (we've found the end of headers; flush the lot!) * * Before that, we flag that the last page seen (this one) was not a * BOS page; that way we know that when we next see a BOS page it's a * new chain, and we can flush all existing streams. */ page_type type; GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno); if (!stream) { GST_LOG_OBJECT (ogg, "Non-BOS page unexpectedly found at %lld", ogg->offset); goto failure; } ogg->last_page_not_bos = TRUE; type = gst_ogg_parse_is_header (ogg, stream, &page); if (type == PAGE_PENDING && ogg->in_headers) { gst_buffer_ref (pagebuffer); stream->unknown_pages = g_slist_append (stream->unknown_pages, pagebuffer); } else if (type == PAGE_HEADER) { if (!ogg->in_headers) { GST_LOG_OBJECT (ogg, "Header page unexpectedly found outside " "headers at offset %lld", ogg->offset); goto failure; } else { /* Append the header to the buffer list, after any unknown previous * pages */ stream->headers = g_slist_concat (stream->headers, stream->unknown_pages); g_slist_free (stream->unknown_pages); gst_buffer_ref (pagebuffer); stream->headers = g_slist_append (stream->headers, pagebuffer); } } else { /* PAGE_DATA, or PAGE_PENDING but outside headers */ if (ogg->in_headers) { /* First non-header page... set caps, flush headers. * * First up, we build a single GValue list of all the pagebuffers * we're using for the headers, in order. * Then we set this on the caps structure. Then we can start pushing * buffers for the headers, and finally we send this non-header * page. */ GstCaps *caps; GstStructure *structure; GValue array = { 0 }; gint count = 0; gboolean found_pending_headers = FALSE; GSList *l; g_value_init (&array, GST_TYPE_ARRAY); for (l = ogg->oggstreams; l != NULL; l = l->next) { GstOggStream *stream = (GstOggStream *) l->data; if (g_slist_length (stream->headers) == 0) { GST_LOG_OBJECT (ogg, "No primary header found for stream %u", stream->serialno); goto failure; } gst_ogg_parse_append_header (&array, GST_BUFFER (stream->headers->data)); count++; } for (l = ogg->oggstreams; l != NULL; l = l->next) { GstOggStream *stream = (GstOggStream *) l->data; int j; for (j = 1; j < g_slist_length (stream->headers); j++) { gst_ogg_parse_append_header (&array, GST_BUFFER (g_slist_nth_data (stream->headers, j))); count++; } } caps = gst_pad_get_caps (ogg->srcpad); caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); gst_structure_set_value (structure, "streamheader", &array); gst_pad_set_caps (ogg->srcpad, caps); g_value_unset (&array); if (ogg->caps) gst_caps_unref (ogg->caps); ogg->caps = caps; GST_LOG_OBJECT (ogg, "Set \"streamheader\" caps with %d buffers " "(one per page)", count); /* Now, we do the same thing, but push buffers... */ for (l = ogg->oggstreams; l != NULL; l = l->next) { GstOggStream *stream = (GstOggStream *) l->data; GstBuffer *buf = GST_BUFFER (stream->headers->data); gst_buffer_set_caps (buf, caps); result = gst_pad_push (ogg->srcpad, buf); if (result != GST_FLOW_OK) return result; } for (l = ogg->oggstreams; l != NULL; l = l->next) { GstOggStream *stream = (GstOggStream *) l->data; int j; for (j = 1; j < g_slist_length (stream->headers); j++) { GstBuffer *buf = GST_BUFFER (g_slist_nth_data (stream->headers, j)); gst_buffer_set_caps (buf, caps); result = gst_pad_push (ogg->srcpad, buf); if (result != GST_FLOW_OK) return result; } } ogg->in_headers = 0; /* And finally the pending data pages */ for (l = ogg->oggstreams; l != NULL; l = l->next) { GstOggStream *stream = (GstOggStream *) l->data; GSList *k; if (stream->unknown_pages == NULL) continue; if (found_pending_headers) { GST_WARNING_OBJECT (ogg, "Incorrectly muxed headers found at " "approximate offset %lld", ogg->offset); } found_pending_headers = TRUE; GST_LOG_OBJECT (ogg, "Pushing %d pending pages after headers", g_slist_length (stream->unknown_pages) + 1); for (k = stream->unknown_pages; k != NULL; k = k->next) { GstBuffer *buf; buf = GST_BUFFER (k->data); gst_buffer_set_caps (buf, caps); result = gst_pad_push (ogg->srcpad, buf); if (result != GST_FLOW_OK) return result; } g_slist_foreach (stream->unknown_pages, (GFunc) gst_mini_object_unref, NULL); g_slist_free (stream->unknown_pages); stream->unknown_pages = NULL; } gst_buffer_set_caps (pagebuffer, caps); result = gst_pad_push (ogg->srcpad, GST_BUFFER (pagebuffer)); if (result != GST_FLOW_OK) return result; } else { /* Normal data page, submit buffer */ gst_buffer_set_caps (pagebuffer, ogg->caps); result = gst_pad_push (ogg->srcpad, GST_BUFFER (pagebuffer)); if (result != GST_FLOW_OK) return result; } } } } } return result; failure: gst_pad_push_event (GST_PAD (ogg->srcpad), gst_event_new_eos ()); return GST_FLOW_ERROR; }
static int ReadPacketTime( ogg* p, format_reader* Reader, format_packet* Packet ) { // only called by CalcDuration format_buffer* Buffer; int Bytes; ogg_buffer* Ptr; ogg_sync_state* Sync = ogg_sync_create(); ogg_page Page; if (!Sync) return ERR_OUT_OF_MEMORY; while ((Buffer = Format_BufferRemove(Reader))!=NULL) { Ptr = ogg_sync_bufferinext(Sync); if (Ptr) { Ptr->ext = &p->Format; Ptr->extdata = Buffer; Ptr->data = Buffer->Block.Ptr; Ptr->size = Buffer->Length; ogg_sync_wrote(Sync,Buffer->Length); } else Format_BufferRelease(&p->Format,Buffer); } memset(&Page,0,sizeof(Page)); while ((Bytes = ogg_sync_pageseek(Sync,&Page)) != 0) { if (Bytes > 0) { int64_t MediaTime = ogg_page_granulepos(&Page); int Id = ogg_page_serialno(&Page); int i; if (MediaTime != -1) for (i=0;i<p->Format.StreamCount;++i) if (p->Format.Streams[i]->Id == Id) { oggstream* s = (oggstream*) p->Format.Streams[i]; if (s->MediaRateNum) { tick_t RefTime = (tick_t)((MediaTime * s->MediaRateDen) / s->MediaRateNum); if (RefTime > Packet->RefTime) { Packet->Stream = &s->Stream; Packet->RefTime = RefTime; } } break; } } } ogg_page_release(&Page); ogg_sync_destroy(Sync); return ERR_NONE; }
void probe_ogg(info_t *ipipe) { ogg_sync_state sync; ogg_page page; ogg_packet pack; char *buf; int nread, np, sno, nvtracks = 0, natracks = 0, i, idx; //int endofstream = 0, k, n; struct demux_t streams[MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS]; int fdin = -1; char vid_codec[5]; ogm_stream_header *sth; fdin = ipipe->fd_in; if (fdin == -1) { tc_log_error(__FILE__, "Could not open file."); goto ogg_out; } ipipe->probe_info->magic=TC_MAGIC_OGG; memset(streams, 0, sizeof(streams)); for (i = 0; i < (MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS); i++) streams[i].serial = -1; ogg_sync_init(&sync); while (1) { np = ogg_sync_pageseek(&sync, &page); if (np < 0) { tc_log_error(__FILE__, "ogg_sync_pageseek failed"); goto ogg_out; } if (np == 0) { buf = ogg_sync_buffer(&sync, BLOCK_SIZE); if (!buf) { tc_log_error(__FILE__, "ogg_sync_buffer failed"); goto ogg_out; } if ((nread = read(fdin, buf, BLOCK_SIZE)) <= 0) { } ogg_sync_wrote(&sync, nread); continue; } if (!ogg_page_bos(&page)) { break; } else { ogg_stream_state sstate; vorbis_info *inf = tc_malloc (sizeof(vorbis_info)); vorbis_comment *com = tc_malloc (sizeof(vorbis_comment)); if (!inf || !com) { tc_log_error(__FILE__, "Out of Memory at %d", __LINE__); goto ogg_out; } sno = ogg_page_serialno(&page); if (ogg_stream_init(&sstate, sno)) { tc_log_error(__FILE__, "ogg_stream_init failed"); goto ogg_out; } ogg_stream_pagein(&sstate, &page); ogg_stream_packetout(&sstate, &pack); switch (ogm_packet_type(pack)) { case Vorbis: vorbis_info_init(inf); vorbis_comment_init(com); if(vorbis_synthesis_headerin(inf, com, &pack) < 0) { tc_log_warn(__FILE__, "Could not decode vorbis header " "packet - invalid vorbis stream ()"); } else { #ifdef OGM_DEBUG tc_log_msg(__FILE__, "(a%d/%d) Vorbis audio; " "rate: %ldHz, channels: %d, bitrate %3.2f kb/s", natracks + 1, natracks + nvtracks + 1, inf->rate, inf->channels, (double)inf->bitrate_nominal/1000.0); #endif ipipe->probe_info->track[natracks].samplerate = inf->rate; ipipe->probe_info->track[natracks].chan = inf->channels; ipipe->probe_info->track[natracks].bits = 0; /* XXX --tibit*/ ipipe->probe_info->track[natracks].format = TC_CODEC_VORBIS; ipipe->probe_info->track[natracks].bitrate = (double)inf->bitrate_nominal/1000.0; ipipe->probe_info->track[natracks].tid=natracks; if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks; streams[natracks].serial = sno; streams[natracks].vorbis = 1; ac_memcpy(&streams[natracks].state, &sstate, sizeof(sstate)); natracks++; } break; #ifdef HAVE_THEORA case Theora: { theora_info ti; theora_comment tc; theora_decode_header(&ti, &tc, &pack); ipipe->probe_info->width = ti.width; ipipe->probe_info->height = ti.height; ipipe->probe_info->fps = (double)ti.fps_numerator/ti.fps_denominator; tc_frc_code_from_ratio(&(ipipe->probe_info->frc), ti.fps_numerator, ti.fps_denominator); ipipe->probe_info->codec=TC_CODEC_THEORA; idx = natracks + MAX_AUDIO_TRACKS; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); nvtracks++; break; } #endif case DirectShow: if ((*(int32_t*)(pack.packet+96) == 0x05589f80) && (pack.bytes >= 184)) { tc_log_warn(__FILE__, "(v%d/%d) Found old video " "header. Not supported.", nvtracks + 1, natracks + nvtracks + 1); } else if (*(int32_t*)pack.packet+96 == 0x05589F81) { tc_log_warn(__FILE__, "(a%d/%d) Found old audio " "header. Not supported.", natracks + 1, natracks + nvtracks + 1); } break; case StreamHeader: sth = (ogm_stream_header *)(pack.packet + 1); if (!strncmp(sth->streamtype, "video", 5)) { #ifdef OGM_DEBUG unsigned long codec; codec = (sth->subtype[0] << 24) + (sth->subtype[1] << 16) + (sth->subtype[2] << 8) + sth->subtype[3]; tc_log_msg(__FILE__, "(v%d/%d) video; fps: %.3f width height: %dx%d " "codec: %p (%c%c%c%c)", nvtracks + 1, natracks + nvtracks + 1, (double)10000000 / (double)sth->time_unit, sth->sh.video.width, sth->sh.video.height, (void *)codec, sth->subtype[0], sth->subtype[1], sth->subtype[2], sth->subtype[3]); #endif vid_codec[0] = sth->subtype[0]; vid_codec[1] = sth->subtype[1]; vid_codec[2] = sth->subtype[2]; vid_codec[3] = sth->subtype[3]; vid_codec[4] = '\0'; //ipipe->probe_info->frames = AVI_video_frames(avifile); ipipe->probe_info->width = sth->sh.video.width; ipipe->probe_info->height = sth->sh.video.height; ipipe->probe_info->fps = (double)10000000 / (double)sth->time_unit; tc_frc_code_from_value(&(ipipe->probe_info->frc), ipipe->probe_info->fps); ipipe->probe_info->codec=TC_CODEC_UNKNOWN; // gets rewritten if(strlen(vid_codec)==0) { ipipe->probe_info->codec=TC_CODEC_RGB24; } else { if(strcasecmp(vid_codec,"dvsd")==0) ipipe->probe_info->codec=TC_CODEC_DV; if(strcasecmp(vid_codec,"DIV3")==0) ipipe->probe_info->codec=TC_CODEC_DIVX3; if(strcasecmp(vid_codec,"DIVX")==0) ipipe->probe_info->codec=TC_CODEC_DIVX4; if(strcasecmp(vid_codec,"DX50")==0) ipipe->probe_info->codec=TC_CODEC_DIVX5; if(strcasecmp(vid_codec,"XVID")==0) ipipe->probe_info->codec=TC_CODEC_XVID; if(strcasecmp(vid_codec,"MJPG")==0) ipipe->probe_info->codec=TC_CODEC_MJPEG; } idx = natracks + MAX_AUDIO_TRACKS; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); nvtracks++; } else if (!strncmp(sth->streamtype, "audio", 5)) { int codec; char buf[5]; ac_memcpy(buf, sth->subtype, 4); buf[4] = 0; codec = strtoul(buf, NULL, 16); #ifdef OGM_DEBUG tc_log_msg(__FILE__, "(a%d/%d) codec: %d (0x%04x) (%s) bits per " "sample: %d channels: %hd samples per second: %ld " "avgbytespersec: %hd blockalign: %d", natracks + 1, natracks + nvtracks + 1, codec, codec, codec == 0x1 ? "PCM" : codec == 55 ? "MP3" : codec == 0x55 ? "MP3" : codec == 0x2000 ? "AC3" : "unknown", sth->bits_per_sample, sth->sh.audio.channels, (long)sth->samples_per_unit, sth->sh.audio.avgbytespersec, sth->sh.audio.blockalign); #endif idx = natracks; ipipe->probe_info->track[natracks].samplerate = sth->samples_per_unit; ipipe->probe_info->track[natracks].chan = sth->sh.audio.channels; ipipe->probe_info->track[natracks].bits = (sth->bits_per_sample<4)?sth->bits_per_sample*8:sth->bits_per_sample; ipipe->probe_info->track[natracks].format = codec; ipipe->probe_info->track[natracks].bitrate = 0; ipipe->probe_info->track[natracks].tid=natracks; if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); natracks++; } else { tc_log_warn(__FILE__, "(%d) found new header of unknown/" "unsupported type\n", nvtracks + natracks + 1); } break; case none: tc_log_warn(__FILE__, "OGG stream %d is of an unknown type " "(bad header?)", nvtracks + natracks + 1); break; } /* switch type */ free(inf); free(com); ogg_stream_clear(&sstate); } /* beginning of page */ } /* while (1) */ ogg_out: //close(fdin); return; }
void VideoClip_Theora::_load(DataSource* source) { #ifdef _DEBUG log("-----"); #endif this->stream = source; this->_readTheoraVorbisHeaders(); this->info.TheoraDecoder = th_decode_alloc(&this->info.TheoraInfo, this->info.TheoraSetup); this->width = this->info.TheoraInfo.frame_width; this->height = this->info.TheoraInfo.frame_height; this->subFrameWidth = this->info.TheoraInfo.pic_width; this->subFrameHeight = this->info.TheoraInfo.pic_height; this->subFrameX = this->info.TheoraInfo.pic_x; this->subFrameY = this->info.TheoraInfo.pic_y; this->stride = this->getWidth(); if (this->useStride) { this->stride = potCeil(this->stride); } this->fps = this->info.TheoraInfo.fps_numerator / (float)this->info.TheoraInfo.fps_denominator; #ifdef _DEBUG log("width: " + str(this->width) + ", height: " + str(this->height) + ", fps: " + str((int)this->getFps())); #endif this->frameQueue = new FrameQueue(this); this->frameQueue->setSize(this->precachedFramesCount); // find out the duration of the file by seeking to the end // having ogg decode pages, extract the granule pos from // the last theora page and seek back to beginning of the file char* buffer = NULL; int bytesRead = 0; uint64_t streamSize = this->stream->getSize(); uint64_t seekPos = 0; int result = 0; ogg_int64_t granule = 0; for (unsigned int i = 1; i <= 50; ++i) { ogg_sync_reset(&this->info.OggSyncState); seekPos = (BUFFER_SIZE * i > streamSize ? 0 : streamSize - BUFFER_SIZE * i); this->stream->seek(seekPos); buffer = ogg_sync_buffer(&this->info.OggSyncState, BUFFER_SIZE * i); bytesRead = this->stream->read(buffer, BUFFER_SIZE * i); ogg_sync_wrote(&this->info.OggSyncState, bytesRead); ogg_sync_pageseek(&this->info.OggSyncState, &this->info.OggPage); while (true) { result = ogg_sync_pageout(&this->info.OggSyncState, &this->info.OggPage); if (result == 0) { break; } // if page is not a theora page or page is unsynced(-1), skip it if (result == -1 || ogg_page_serialno(&this->info.OggPage) != this->info.TheoraStreamState.serialno) { continue; } granule = ogg_page_granulepos(&this->info.OggPage); if (granule >= 0) { this->framesCount = (int)th_granule_frame(this->info.TheoraDecoder, granule) + 1; } else if (this->framesCount > 0) { ++this->framesCount; // append delta frames at the end to get the exact number } } if (this->framesCount > 0 || streamSize < BUFFER_SIZE * i) { break; } } if (this->framesCount < 0) { log("unable to determine file duration!"); } else { this->duration = this->framesCount / this->fps; #ifdef _DEBUG log("duration: " + strf(this->duration) + " seconds"); #endif } // restore to beginning of stream. ogg_sync_reset(&this->info.OggSyncState); this->stream->seek(0); if (this->vorbisStreams > 0) // if there is no audio interface factory defined, even though the video clip might have audio, it will be ignored { vorbis_synthesis_init(&this->info.VorbisDSPState, &this->info.VorbisInfo); vorbis_block_init(&this->info.VorbisDSPState, &this->info.VorbisBlock); this->audioChannelsCount = this->info.VorbisInfo.channels; this->audioFrequency = (int) this->info.VorbisInfo.rate; // create an audio interface instance if available AudioInterfaceFactory* audioInterfaceFactory = theoraplayer::manager->getAudioInterfaceFactory(); if (audioInterfaceFactory != NULL) { this->setAudioInterface(audioInterfaceFactory->createInstance(this, this->audioChannelsCount, this->audioFrequency)); } } this->frameDuration = 1.0f / this->getFps(); #ifdef _DEBUG log("-----"); #endif }
long VideoClip_Theora::_seekPage(long targetFrame, bool returnKeyFrame) { uint64_t seekMin = 0; uint64_t seekMax = this->stream->getSize(); long frame = 0; ogg_int64_t granule = 0; if (targetFrame == 0) { this->stream->seek(0); } char* buffer = NULL; int bytesRead = 0; if (targetFrame != 0) { for (int i = 0; i < 100; ++i) { ogg_sync_reset(&this->info.OggSyncState); this->stream->seek(seekMin / 2 + seekMax / 2); // do a binary search memset(&this->info.OggPage, 0, sizeof(ogg_page)); ogg_sync_pageseek(&this->info.OggSyncState, &this->info.OggPage); while (i < 1000) { if (ogg_sync_pageout(&this->info.OggSyncState, &this->info.OggPage) == 1) { if (ogg_page_serialno(&this->info.OggPage) == this->info.TheoraStreamState.serialno) { granule = ogg_page_granulepos(&this->info.OggPage); if (granule >= 0) { frame = (long)th_granule_frame(this->info.TheoraDecoder, granule); if (frame < targetFrame && targetFrame - frame < 10) { // we're close enough, let's break this. i = 1000; break; } // we're not close enough, let's shorten the borders of the binary search if (targetFrame - 1 > frame) { seekMin = seekMin / 2 + seekMax / 2; } else { seekMax = seekMin / 2 + seekMax / 2; } break; } } } else { buffer = ogg_sync_buffer(&this->info.OggSyncState, BUFFER_SIZE); bytesRead = this->stream->read(buffer, BUFFER_SIZE); if (bytesRead == 0) { break; } ogg_sync_wrote(&this->info.OggSyncState, bytesRead); } } } } if (returnKeyFrame) { return (long)(granule >> this->info.TheoraInfo.keyframe_granule_shift); } ogg_sync_reset(&this->info.OggSyncState); memset(&this->info.OggPage, 0, sizeof(ogg_page)); ogg_sync_pageseek(&this->info.OggSyncState, &this->info.OggPage); if (targetFrame == 0) { return -1; } this->stream->seek((seekMin + seekMax) / 2); // do a binary search return -1; }
status_t OggReader::FindLastPages() { TRACE("OggReader::FindLastPages\n"); bigtime_t start_time = system_time(); status_t result = B_ERROR; const int read_size = 256*256; ogg_page page; ogg_sync_state sync; ogg_sync_init(&sync); off_t right = fSeekable->Seek(0, SEEK_END); off_t left = right; // we assume the common case is that the last pages are near the end uint serial_count = 0; while (serial_count < fCookies.size()) { int offset; ssize_t bytes = 0; while ((offset = ogg_sync_pageseek(&sync, &page)) <= 0) { left += -offset; if (offset == 0) { off_t pos = fSeekable->Position(); if (pos >= right || bytes == 0) { if (left == 0) { TRACE("OggReader::FindLastPages: couldn't find some stream's page!!!\n"); goto done; } left = max_c(0, left - read_size); result = fSeekable->Seek(left, SEEK_SET); if (result < 0) { goto done; } ogg_sync_reset(&sync); } char * buffer = ogg_sync_buffer(&sync, read_size); bytes = fSeekable->Read(buffer, read_size); if (bytes < 0) { TRACE("OggReader::FindLastPages: Read: error\n"); result = bytes; goto done; } if (ogg_sync_wrote(&sync, bytes) != 0) { TRACE("OggReader::FindLastPages: ogg_sync_wrote failed?: error\n"); goto done; } } } off_t current = left; do { // found a page at "current" long serialno = ogg_page_serialno(&page); OggSeekable * track = dynamic_cast<OggSeekable*>(fTracks[serialno]); if (track == 0) { TRACE("OggReader::FindLastPages: unknown serialno == TODO: chaining?\n"); } else { if (track->GetLastPagePosition() == 0) { serial_count++; } track->SetLastPagePosition(current); } current += page.header_len + page.body_len; } while ((current < right) && (ogg_sync_pageout(&sync, &page) == 1)); right = left; ogg_sync_reset(&sync); } result = B_OK; done: ogg_sync_clear(&sync); TRACE("OggReader::FindLastPages took %lld microseconds\n", system_time() - start_time); return result; }
static int64_t find_first_page( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2, logical_stream_t *p_stream, int64_t *pi_kframe, int64_t *pi_frame ) { int64_t i_result; int64_t i_granulepos; int64_t i_bytes_to_read = i_pos2 - i_pos1 + 1; int64_t i_bytes_read; int64_t i_pages_checked = 0; int64_t i_packets_checked; demux_sys_t *p_sys = p_demux->p_sys; ogg_packet op; seek_byte( p_demux, i_pos1 ); if ( i_pos1 == p_stream->i_data_start ) { /* set a dummy granulepos at data_start */ *pi_kframe = p_stream->i_keyframe_offset; *pi_frame = p_stream->i_keyframe_offset; p_sys->b_page_waiting = true; return p_sys->i_input_position; } if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ; while ( 1 ) { if ( p_sys->i_input_position >= i_pos2 ) { /* we reached the end and found no pages */ *pi_frame=-1; return -1; } /* read next chunk */ if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) ) { /* EOF */ *pi_frame = -1; return -1; } i_bytes_to_read = OGGSEEK_BYTES_TO_READ; i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page ); if ( i_result < 0 ) { /* found a page, sync to page start */ p_sys->i_input_position -= i_result; i_pos1 = p_sys->i_input_position; continue; } if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 && ! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) ) { i_pos1 = p_sys->i_input_position; break; } p_sys->i_input_position += i_bytes_read; }; seek_byte( p_demux, p_sys->i_input_position ); ogg_stream_reset( &p_stream->os ); while( 1 ) { if ( p_sys->i_input_position >= i_pos2 ) { /* reached the end of the search region and nothing was found */ *pi_frame = -1; return p_sys->i_input_position; } p_sys->b_page_waiting = false; if ( ! ( i_result = oggseek_read_page( p_demux ) ) ) { /* EOF */ *pi_frame = -1; return p_sys->i_input_position; } // found a page if ( p_stream->os.serialno != ogg_page_serialno( &p_sys->current_page ) ) { /* page is not for this stream */ p_sys->i_input_position += i_result; if ( ! i_pages_checked ) i_pos1 = p_sys->i_input_position; continue; } ogg_stream_pagein( &p_stream->os, &p_sys->current_page ); i_pages_checked++; i_packets_checked = 0; if ( ogg_stream_packetout( &p_stream->os, &op ) > 0 ) { i_packets_checked++; } if ( i_packets_checked ) { i_granulepos = ogg_page_granulepos( &p_sys->current_page ); oggseek_theora_index_entry_add( p_stream, i_granulepos, i_pos1 ); *pi_kframe = i_granulepos >> p_stream->i_granule_shift; *pi_frame = *pi_kframe + i_granulepos - ( *pi_kframe << p_stream->i_granule_shift ); p_sys->b_page_waiting = true; return i_pos1; } /* -> start of next page */ p_sys->i_input_position += i_result; } }