Index: libs/libmythtv/avformatdecoder.cpp =================================================================== RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/avformatdecoder.cpp,v retrieving revision 1.147 diff -a -u -r1.147 avformatdecoder.cpp --- libs/libmythtv/avformatdecoder.cpp 4 May 2005 14:47:57 -0000 1.147 +++ libs/libmythtv/avformatdecoder.cpp 7 May 2005 08:12:13 -0000 @@ -25,6 +25,32 @@ #define MAX_AC3_FRAME_SIZE 6144 +#define USE_GENERIC_SEEK 1 + +/* partial struct from avidec */ +extern "C" { +#include "libavformat/avi.h" +} +typedef struct AVIIndexEntry { + unsigned int flags; + unsigned int pos; + unsigned int cum_len; /* sum of all lengths before this packet */ +} AVIIndexEntry; +typedef struct AVIStreamPartial { + AVIIndexEntry *index_entries; + int nb_index_entries; + int index_entries_allocated_size; +} AVIStreamPartial; +typedef struct { + int64_t riff_end; + int64_t movi_end; + offset_t movi_list; + int index_loaded; + int is_odml; + //DVDemuxContext* dv_demux; +} AVIContext; +/* end partial struct from avidec */ + extern pthread_mutex_t avcodeclock; class AvFormatDecoderPrivate @@ -210,18 +236,107 @@ d->DestroyMPEG2(); } +extern "C" +int avi_read_seek_update(AVFormatContext *s, int stream_index, int64_t timestamp, int flags); + +bool AvFormatDecoder::DoRewind(long long desiredFrame, bool doflush) +{ +#if USE_GENERIC_SEEK + if (recordingHasPositionMap) +#endif + return DecoderBase::DoRewind(desiredFrame, doflush); +#if USE_GENERIC_SEEK + return DoFastForward(desiredFrame, doflush); +#endif +} + +bool AvFormatDecoder::DoFastForward(long long desiredFrame, bool doflush) +{ +#if USE_GENERIC_SEEK + if (recordingHasPositionMap) +#endif + return DecoderBase::DoFastForward(desiredFrame, doflush); +#if USE_GENERIC_SEEK + AVStream *st = NULL; + int i; + for (i = 0; i < ic->nb_streams; i++) + { + AVStream *st1 = ic->streams[i]; + if (st1 && st1->codec.codec_type == CODEC_TYPE_VIDEO) + { + st = st1; + break; + } + } + if (!st) + return false; + + // convert framenumber to normalized timestamp + long long ts = (long long)((desiredFrame*AV_TIME_BASE)/(long double)fps); + if (av_seek_frame(ic,-1,ts,0)<0) + return false; + + // convert current timestamp to frame number + lastKey = (long long)((st->cur_dts*(long double)fps+(AV_TIME_BASE/2))/AV_TIME_BASE); + framesPlayed = lastKey; + framesRead = lastKey; + + VERBOSE(VB_IMPORTANT, QString("AvFormatDecoder::DoFastForward newframe %5 frame %1 fps %2 ts %3 fl %4 keyts %6") + .arg(desiredFrame) + .arg(fps) + .arg(ts) + .arg(doflush) + .arg(framesPlayed) + .arg(st->cur_dts) + ); + + int normalframes = desiredFrame - framesPlayed; + + if (!exactseeks) + normalframes = 0; + + SeekReset(lastKey, normalframes, doflush); + + if (doflush) + { + m_parent->SetFramesPlayed(framesPlayed+1); + m_parent->getVideoOutput()->SetFramesPlayed(framesPlayed+1); + } + + return true; +#endif +} + void AvFormatDecoder::SeekReset(long long, int skipFrames, bool doflush) { lastapts = 0; lastvpts = 0; +#if USE_GENERIC_SEEK + if (recordingHasPositionMap) +#endif av_read_frame_flush(ic); d->ResetMPEG2(); +#if USE_GENERIC_SEEK + if (recordingHasPositionMap) + { +#endif ic->pb.pos = ringBuffer->GetTotalReadPosition(); ic->pb.buf_ptr = ic->pb.buffer; ic->pb.buf_end = ic->pb.buffer; +#if USE_GENERIC_SEEK + } +#endif + +#if !USE_GENERIC_SEEK + const char *name = ic->av_class->item_name(ic); + if (strcmp(name,"avi")==0) + { + avi_read_seek_update(ic,-1,framesRead,0); + } +#endif if (doflush) { @@ -407,6 +522,80 @@ decoder->ScanStreams(false); } + +int AvFormatDecoder::LoadAVIIndex() +{ + int i; + AVStream *st = NULL; + AVIContext *avi; + const char *name = ic->av_class->item_name(ic); + if (strcmp(name,"avi")==0) + { + for (i = 0; i < ic->nb_streams; i++) + { + AVStream *st1 = ic->streams[i]; + if (st1 && st1->codec.codec_type == CODEC_TYPE_VIDEO) + { + st = st1; + break; + } + } + if (st) + { + AVIStreamPartial *ast = (AVIStreamPartial*)(st->priv_data); + if (ast && (ast->nb_index_entries>0)) + { + VERBOSE(VB_PLAYBACK,QString("video index %1 count %2 size %3") + .arg((unsigned int)ast->index_entries) + .arg(ast->nb_index_entries) + .arg(ast->index_entries_allocated_size) + ); + avi = (AVIContext*)ic->priv_data; + int nKeyFrames = 0; + int prevkeyframe = 0; + keyframedist = 1; + long long totframes = ast->nb_index_entries; + for(i=0;inb_index_entries;i++) + { + if (ast->index_entries[i].flags & AVIIF_INDEX) + nKeyFrames++; + } + m_positionMap.reserve(totframes); + + if ((keyframedist == 15) || + (keyframedist == 12)) + positionMapType = MARK_GOP_START; + else + positionMapType = MARK_GOP_BYFRAME; + + m_parent->SetKeyframeDistance(keyframedist); + + PosMapEntry entry; + for(i=0;iindex_entries[i].flags & AVIIF_INDEX) + { + unsigned long long pos = + ast->index_entries[i].pos + avi->movi_list; // file offset + entry.index = i; + entry.adjFrame = i; + entry.pos = pos; + m_positionMap.push_back(entry); + prevkeyframe = i; + } + } + int length = (int)((totframes * 1.0) / fps); + m_parent->SetFileLength(length, totframes); + recordingHasPositionMap = true; + hasFullPositionMap = true; + } + } + } + return 0; +} + +/***************/ + /** \fn AvFormatDecoder::OpenFile(RingBuffer*, bool, char[2048]) * OpenFile opens a ringbuffer for playback. * @@ -499,8 +688,21 @@ } } +#if !USE_GENERIC_SEEK + if (!recordingHasPositionMap) + { + LoadAVIIndex(); + } +#endif + if (!recordingHasPositionMap) { +#if USE_GENERIC_SEEK + // determine length + av_estimate_timings(ic); + int64_t dur = ic->duration/(int64_t)AV_TIME_BASE; + m_parent->SetFileLength((int)(dur), (int)(dur * fps)); +#else VERBOSE(VB_PLAYBACK, "recording has no position -- guessing at length"); // the pvr-250 seems to overreport the bitrate by * 2 float bytespersec = (float)bitrate / 8 / 2; @@ -512,10 +714,13 @@ // auto detection will change it. keyframedist = 15; positionMapType = MARK_GOP_START; +#endif } +#if !USE_GENERIC_SEEK //if (livetv || watchingrecording) ic->build_index = 0; +#endif dump_format(ic, 0, filename, 0); if (hasFullPositionMap) @@ -526,12 +731,33 @@ { VERBOSE(VB_PLAYBACK, "Partial position map found"); } + else + { + QString text1; + int64_t dur = ic->duration/(int64_t)1000000; + int phours = (int)dur / 3600; + int pmins = ((int)dur - phours * 3600) / 60; + int psecs = ((int)dur - phours * 3600 - pmins * 60); + text1.sprintf("%d:%02d:%02d", phours, pmins, psecs); + + VERBOSE(VB_PLAYBACK, QString("No position map found, duration is %1 (%2)") + .arg(text1).arg(ic->duration)); + int i; + for(i=0;inb_streams; i++) + { + VERBOSE(VB_PLAYBACK, QString(" stream %1 duration is %2").arg(i).arg(ic->streams[i]->duration)); + } + } VERBOSE(VB_PLAYBACK, QString("AvFormatDecoder: Successfully opened decoder for file: " "\"%1\". novideo(%2)").arg(filename).arg(novideo)); +#if USE_GENERIC_SEEK + return 1; +#else return recordingHasPositionMap; +#endif } void AvFormatDecoder::InitVideoCodec(AVCodecContext *enc) @@ -1004,6 +1230,9 @@ void AvFormatDecoder::HandleGopStart(AVPacket *pkt) { +#if USE_GENERIC_SEEK + pkt = pkt; +#else if (prevgoppos != 0 && keyframedist != 1) { int tempKeyFrameDist = framesRead - 1 - prevgoppos; @@ -1088,7 +1317,7 @@ // If we are > 150 frames in and saw no positionmap at all, reset // length based on the actual bitrate seen so far - if (framesRead > 150 && !recordingHasPositionMap && !livetv) + if (framesRead > 150 && framesRead > last_frame && !recordingHasPositionMap && !livetv) { bitrate = (int)((pkt->startpos * 8 * fps) / (framesRead - 1)); float bytespersec = (float)bitrate / 8; @@ -1096,6 +1325,7 @@ m_parent->SetFileLength((int)(secs), (int)(secs * fps)); } } +#endif } #define SEQ_START 0x000001b3 Index: libs/libmythtv/avformatdecoder.h =================================================================== RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/avformatdecoder.h,v retrieving revision 1.56 diff -a -u -r1.56 avformatdecoder.h --- libs/libmythtv/avformatdecoder.h 27 Apr 2005 19:32:49 -0000 1.56 +++ libs/libmythtv/avformatdecoder.h 7 May 2005 08:12:13 -0000 @@ -77,6 +77,10 @@ int ScanStreams(bool novideo); + int LoadAVIIndex(); + virtual bool DoRewind(long long desiredFrame, bool doflush = true); + virtual bool DoFastForward(long long desiredFrame, bool doflush = true); + protected: /// Attempt to find the optimal audio stream to use based on the number of channels, /// and if we're doing AC3 passthrough. This will select the highest stream number Index: libs/libavformat/avidec.c =================================================================== RCS file: /var/lib/mythcvs/mythtv/libs/libavformat/avidec.c,v retrieving revision 1.15 diff -a -u -r1.15 avidec.c --- libs/libavformat/avidec.c 31 Jan 2005 17:15:16 -0000 1.15 +++ libs/libavformat/avidec.c 7 May 2005 08:12:17 -0000 @@ -744,6 +745,10 @@ st = s->streams[i]; ast = st->priv_data; ast->frame_offset = ast->new_frame_offset; + st->cur_dts = av_rescale(ast->frame_offset, + AV_TIME_BASE * (int64_t)st->time_base.num, + st->time_base.den + ); #ifdef DEBUG_SEEK printf("%d: frame_offset=%d\n", i, ast->frame_offset); @@ -757,6 +762,100 @@ return 0; } +int avi_read_seek_update(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + AVIContext *avi = s->priv_data; + AVStream *st; + AVIStream *ast; + int frame_number, i; + int64_t pos; + + if (!avi->index_loaded) { + /* we only load the index on demand */ + avi_load_index(s); + avi->index_loaded = 1; + } + if (stream_index < 0) { + for(i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + if (st->codec.codec_type == CODEC_TYPE_VIDEO) + goto found; + } + return -1; + found: + stream_index = i; + } + + st = s->streams[stream_index]; + if (st->codec.codec_type != CODEC_TYPE_VIDEO) + return -1; + ast = st->priv_data; + /* compute the frame number */ + frame_number = timestamp; +#ifdef DEBUG_SEEK + printf("timestamp=%0.3f nb_indexes=%d frame_number=%d\n", + (double)timestamp / AV_TIME_BASE, + ast->nb_index_entries, frame_number); +#endif + /* find a closest key frame before */ + if (frame_number >= ast->nb_index_entries) + return -1; + while (frame_number >= 0 && + !(ast->index_entries[frame_number].flags & AVIIF_INDEX)) + frame_number--; + if (frame_number < 0) + return -1; + ast->new_frame_offset = frame_number; + + /* find the position */ + pos = ast->index_entries[frame_number].pos; + +#ifdef DEBUG_SEEK + printf("key_frame_number=%d pos=0x%llx\n", + frame_number, pos); +#endif + + /* update the frame counters for all the other stream by looking + at the positions just after the one found */ + for(i = 0; i < s->nb_streams; i++) { + int j; + if (i != stream_index) { + st = s->streams[i]; + ast = st->priv_data; + if (ast->nb_index_entries <= 0) + return -1; + j = locate_frame_in_index(ast->index_entries, + ast->nb_index_entries, + pos); + /* get next frame */ + if ((j + 1) < ast->nb_index_entries) + j++; + /* extract the current frame number */ + if (ast->sample_size==0) + ast->new_frame_offset = j; + else + ast->new_frame_offset = ast->index_entries[j].cum_len; + } + } + + /* everything is OK now. We can update the frame offsets */ + for(i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + ast = st->priv_data; + ast->frame_offset = ast->new_frame_offset; +#ifdef DEBUG_SEEK + printf("%d: frame_offset=%d\n", i, + ast->frame_offset); +#endif + } + if (avi->dv_demux) + dv_flush_audio_packets(avi->dv_demux); + /* do the seek */ + pos += avi->movi_list; + //url_fseek(&s->pb, pos, SEEK_SET); + return 0; +} + static int avi_read_close(AVFormatContext *s) { int i;