SINT SoundSourceFFmpeg::seekSampleFrame(SINT frameIndex) { DEBUG_ASSERT(isValidFrameIndex(frameIndex)); int ret = 0; qint64 i = 0; if (frameIndex < 0 || frameIndex < m_lCacheStartFrame) { ret = avformat_seek_file(m_pFormatCtx, m_iAudioStream, 0, 32767 * 2, 32767 * 2, AVSEEK_FLAG_BACKWARD); if (ret < 0) { qDebug() << "SoundSourceFFmpeg::seek: Can't seek to 0 byte!"; return -1; } clearCache(); m_lCacheStartFrame = 0; m_lCacheEndFrame = 0; m_lCacheLastPos = 0; m_lCacheFramePos = 0; m_lStoredSeekPoint = -1; // Try to find some jump point near to // where we are located so we don't needed // to try guess it if (frameIndex >= AUDIOSOURCEFFMPEG_POSDISTANCE) { for (i = 0; i < m_SJumpPoints.size(); i ++) { if (m_SJumpPoints[i]->startFrame >= frameIndex && i > 2) { m_lCacheFramePos = m_SJumpPoints[i - 2]->startFrame * 2; m_lStoredSeekPoint = m_SJumpPoints[i - 2]->pos; break; } } } if (frameIndex == 0) { readFramesToCache((AUDIOSOURCEFFMPEG_CACHESIZE - 50), -1); } else { readFramesToCache((AUDIOSOURCEFFMPEG_CACHESIZE / 2), frameIndex); } } if (m_lCacheEndFrame <= frameIndex) { readFramesToCache(100, frameIndex); } m_currentMixxxFrameIndex = frameIndex; m_bIsSeeked = TRUE; return frameIndex; }
int FFMpegDecoder::seekToSec(double sec) { //TODO: implement /* AV_TIME_BASE avformat_seek_file(pFormatCtx,this->videoStream, */ int64_t seekTarget = sec*AV_TIME_BASE; int ret = avformat_seek_file(pFormatCtx,-1,INT64_MIN,seekTarget,INT64_MAX,0); return ret; }
bool VideoDecoder::seekFrame(int64_t frame) { qDebug("\t avformat_seek_file\n"); if(avformat_seek_file(videoFormatContext, videoStream, 0, frame, frame, AVSEEK_FLAG_FRAME)<0) return false; avcodec_flush_buffers(videoFormatContext->streams[videoStream]->codec); return true; }
bool AVDecoder::seekVideo(int percentage) { if(!loadCodec(CODEC_VIDEO)) { return false; } qint64 seek_pos = pFormatCtx->duration * percentage / (AV_TIME_BASE * 100); qint64 frame = av_rescale(seek_pos, av_stream->time_base.den, av_stream->time_base.num); return avformat_seek_file(pFormatCtx, stream_index, 0, frame, frame, AVSEEK_FLAG_FRAME) >= 0; }
void Demuxer::run() { for(;;) { if(aw->abortRequest == 1) break; if(aw->seekPos != 0){ qint64 seek_target = aw->seekPos; qint64 seek_min = aw->seekPos > aw->pos ? aw->pos + 2: INT64_MIN; qint64 seek_max = aw->seekPos < aw->pos ? aw->pos - 2: INT64_MAX; int ret = avformat_seek_file(aw->formatCtx, -1, seek_min, seek_target, seek_max, 0); if(ret < 0){ std::cout<<"seek failed!"<<"\n"; } else { /*seek success*/ aw->serial++; aw->basePos = seek_target; } aw->seekPos = 0; } auto sharedPkt = std::make_shared<Packet>(); //av_init_packet(&pkt); sharedPkt->serial = aw->serial; AVPacket* ppkt = &sharedPkt->pkt; ppkt->size = 0; ppkt->data = NULL; int ret = av_read_frame(aw->formatCtx, ppkt); //end of stream, put null packet flush the remaining frames if((ret == AVERROR_EOF || avio_feof(aw->formatCtx->pb)) && !aw->eof){ ppkt->size = 0; ppkt->data = NULL; aw->audioPacketQ.push(sharedPkt); aw->videoPacketQ.push(sharedPkt); aw->eof = true; //FIXME: maybe stop break; } /*push the audio packet*/ if(ppkt->stream_index == aw->audioIndex){ aw->audioPacketQ.push(sharedPkt); } else if(ppkt->stream_index == aw->videoIndex){ aw->videoPacketQ.push(sharedPkt); } } }
static int demuxer_ffmpeg_seek_frame (demuxer_wrapper_t * wrapper, int timestamp) { AVFormatContext *ic = (AVFormatContext *) wrapper->demuxer_priv; int seek_flags = AVSEEK_FLAG_ANY; int64_t seek_target = timestamp * 1000000; int64_t seek_min = (seek_target > 0) ? seek_target - timestamp + 2 : INT64_MIN; int64_t seek_max = (seek_target < 0) ? seek_target - timestamp - 2 : INT64_MAX; int64_t ret = avformat_seek_file (ic, -1, seek_min, seek_target, seek_max, seek_flags); if (ret >= 0) { dt_info (TAG, "AV_FORMAT_SEEK_FILE OK \n"); return 0; } dt_info (TAG, "AV_FORMAT_SEEK_FILE FAIL \n"); return -1; }
uint_t aubio_source_avcodec_seek (aubio_source_avcodec_t * s, uint_t pos) { int64_t resampled_pos = (uint_t)ROUND(pos * (s->input_samplerate * 1. / s->samplerate)); int64_t min_ts = MAX(resampled_pos - 2000, 0); int64_t max_ts = MIN(resampled_pos + 2000, INT64_MAX); int seek_flags = AVSEEK_FLAG_FRAME | AVSEEK_FLAG_ANY; int ret = avformat_seek_file(s->avFormatCtx, s->selected_stream, min_ts, resampled_pos, max_ts, seek_flags); if (ret < 0) { AUBIO_ERR("Failed seeking to %d in file %s", pos, s->path); } // reset read status s->eof = 0; s->read_index = 0; s->read_samples = 0; // reset the AVAudioResampleContext avresample_close(s->avr); avresample_open(s->avr); return ret; }
void VideoPlayer::doAccurateSeek() { int ret; vLOGE("before seek....\n"); //ret = av_seek_frame(mFormatCtx, mVideoStreamIndex, mSeekTime, 0); ret = avformat_seek_file(mFormatCtx, -1, INT64_MIN, mSeekTime, INT64_MAX, 0); if (ret < 0) { vLOGE("Could not find seek time: %lld\n", mSeekTime); return; } decodeToSeekTime(); mPictureRingBuffer.notifyRingBufferExit(); mPictureRingBuffer.flush(); mAccurateSeek = false; vLOGE("after seek...\n"); }
int ffsox_source_seek(source_t *n, int64_t ts) { AVStream *st; int si; if (0ll<ts) { si=av_find_default_stream_index(n->f.fc); st=n->f.fc->streams[si]; ts=av_rescale_q(ts,AV_TIME_BASE_Q,st->time_base); if (avformat_seek_file(n->f.fc,si,INT64_MIN,ts,INT64_MAX,0)<0) { DMESSAGE("seeking"); goto seek; } n->ts=av_rescale_q(st->cur_dts,st->time_base,AV_TIME_BASE_Q); } return 0; seek: return -1; }
/** \brief Seek to frame **/ bool DecodeThread::seekFrame(qint64 frame) { //printf("**** seekFrame to %d. LLT: %d. LT: %d. LLF: %d. LF: %d. LastFrameOk: %d\n",(int)frame,LastLastFrameTime,LastFrameTime,LastLastFrameNumber,LastFrameNumber,(int)LastFrameOk); cout << "frame" << frame << endl; // Seek if: // - we don't know where we are (Ok=false) // - we know where we are but: // - the desired frame is after the last decoded frame (this could be optimized: if the distance is small, calling decodeSeekFrame may be faster than seeking from the last key frame) // - the desired frame is smaller or equal than the previous to the last decoded frame. Equal because if frame==LastLastFrameNumber we don't want the LastFrame, but the one before->we need to seek there //if( (LastFrameOk==false) || ((LastFrameOk==true) && (frame<=LastLastFrameNumber || frame>LastFrameNumber) ) ) //{ //printf("\t avformat_seek_file\n"); if(avformat_seek_file(pFormatCtx,videoStream,frame,frame,frame,AVSEEK_FLAG_FRAME)<0) return false; avcodec_flush_buffers(pVideoCodecCtx); //DesiredFrameNumber = frame; //LastFrameOk=false; //} //printf("\t decodeSeekFrame\n"); return true; //return decodeSeekFrame(frame); }
status_t StreamBase::Seek(uint32 flags, int64* frame, bigtime_t* time) { BAutolock _(fStreamLock); if (fContext == NULL || fStream == NULL) return B_NO_INIT; TRACE_SEEK("StreamBase::Seek(%ld,%s%s%s%s, %lld, " "%lld)\n", VirtualIndex(), (flags & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "", (flags & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "", (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", *frame, *time); double frameRate = FrameRate(); if ((flags & B_MEDIA_SEEK_TO_FRAME) != 0) { // Seeking is always based on time, initialize it when client seeks // based on frame. *time = (bigtime_t)(*frame * 1000000.0 / frameRate + 0.5); } int64_t timeStamp = *time; int searchFlags = AVSEEK_FLAG_BACKWARD; if ((flags & B_MEDIA_SEEK_CLOSEST_FORWARD) != 0) searchFlags = 0; if (fSeekByBytes) { searchFlags |= AVSEEK_FLAG_BYTE; BAutolock _(fSourceLock); int64_t fileSize; if (fSource->GetSize(&fileSize) != B_OK) return B_NOT_SUPPORTED; int64_t duration = Duration(); if (duration == 0) return B_NOT_SUPPORTED; timeStamp = int64_t(fileSize * ((double)timeStamp / duration)); if ((flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) != 0) { timeStamp -= 65536; if (timeStamp < 0) timeStamp = 0; } bool seekAgain = true; bool seekForward = true; bigtime_t lastFoundTime = -1; int64_t closestTimeStampBackwards = -1; while (seekAgain) { if (avformat_seek_file(fContext, -1, INT64_MIN, timeStamp, INT64_MAX, searchFlags) < 0) { TRACE(" avformat_seek_file() (by bytes) failed.\n"); return B_ERROR; } seekAgain = false; // Our last packet is toast in any case. Read the next one so we // know where we really seeked. fReusePacket = false; if (_NextPacket(true) == B_OK) { while (fPacket.pts == kNoPTSValue) { fReusePacket = false; if (_NextPacket(true) != B_OK) return B_ERROR; } if (fPacket.pos >= 0) timeStamp = fPacket.pos; bigtime_t foundTime = _ConvertFromStreamTimeBase(fPacket.pts); if (foundTime != lastFoundTime) { lastFoundTime = foundTime; if (foundTime > *time) { if (closestTimeStampBackwards >= 0) { timeStamp = closestTimeStampBackwards; seekAgain = true; seekForward = false; continue; } int64_t diff = int64_t(fileSize * ((double)(foundTime - *time) / (2 * duration))); if (diff < 8192) break; timeStamp -= diff; TRACE_SEEK(" need to seek back (%lld) (time: %.2f " "-> %.2f)\n", timeStamp, *time / 1000000.0, foundTime / 1000000.0); if (timeStamp < 0) foundTime = 0; else { seekAgain = true; continue; } } else if (seekForward && foundTime < *time - 100000) { closestTimeStampBackwards = timeStamp; int64_t diff = int64_t(fileSize * ((double)(*time - foundTime) / (2 * duration))); if (diff < 8192) break; timeStamp += diff; TRACE_SEEK(" need to seek forward (%lld) (time: " "%.2f -> %.2f)\n", timeStamp, *time / 1000000.0, foundTime / 1000000.0); if (timeStamp > duration) foundTime = duration; else { seekAgain = true; continue; } } } TRACE_SEEK(" found time: %lld -> %lld (%.2f)\n", *time, foundTime, foundTime / 1000000.0); *time = foundTime; *frame = (uint64)(*time * frameRate / 1000000LL + 0.5); TRACE_SEEK(" seeked frame: %lld\n", *frame); } else { TRACE_SEEK(" _NextPacket() failed!\n"); return B_ERROR; } } } else { // We may not get a PTS from the next packet after seeking, so // we try to get an expected time from the index. int64_t streamTimeStamp = _ConvertToStreamTimeBase(*time); int index = av_index_search_timestamp(fStream, streamTimeStamp, searchFlags); if (index < 0) { TRACE(" av_index_search_timestamp() failed\n"); } else { if (index > 0) { const AVIndexEntry& entry = fStream->index_entries[index]; streamTimeStamp = entry.timestamp; } else { // Some demuxers use the first index entry to store some // other information, like the total playing time for example. // Assume the timeStamp of the first entry is alays 0. // TODO: Handle start-time offset? streamTimeStamp = 0; } bigtime_t foundTime = _ConvertFromStreamTimeBase(streamTimeStamp); bigtime_t timeDiff = foundTime > *time ? foundTime - *time : *time - foundTime; if (timeDiff > 1000000 && (fStreamBuildsIndexWhileReading || index == fStream->nb_index_entries - 1)) { // If the stream is building the index on the fly while parsing // it, we only have entries in the index for positions already // decoded, i.e. we cannot seek into the future. In that case, // just assume that we can seek where we want and leave // time/frame unmodified. Since successfully seeking one time // will generate index entries for the seeked to position, we // need to remember this in fStreamBuildsIndexWhileReading, // since when seeking back there will be later index entries, // but we still want to ignore the found entry. fStreamBuildsIndexWhileReading = true; TRACE_SEEK(" Not trusting generic index entry. " "(Current count: %d)\n", fStream->nb_index_entries); } else { // If we found a reasonably time, write it into *time. // After seeking, we will try to read the sought time from // the next packet. If the packet has no PTS value, we may // still have a more accurate time from the index lookup. *time = foundTime; } } if (avformat_seek_file(fContext, -1, INT64_MIN, timeStamp, INT64_MAX, searchFlags) < 0) { TRACE(" avformat_seek_file() failed.\n"); // Try to fall back to av_seek_frame() timeStamp = _ConvertToStreamTimeBase(timeStamp); if (av_seek_frame(fContext, fStream->index, timeStamp, searchFlags) < 0) { TRACE(" avformat_seek_frame() failed as well.\n"); // Fall back to seeking to the beginning by bytes timeStamp = 0; if (av_seek_frame(fContext, fStream->index, timeStamp, AVSEEK_FLAG_BYTE) < 0) { TRACE(" avformat_seek_frame() by bytes failed as " "well.\n"); // Do not propagate error in any case. We fail if we can't // read another packet. } else *time = 0; } } // Our last packet is toast in any case. Read the next one so // we know where we really sought. bigtime_t foundTime = *time; fReusePacket = false; if (_NextPacket(true) == B_OK) { if (fPacket.pts != kNoPTSValue) foundTime = _ConvertFromStreamTimeBase(fPacket.pts); else TRACE_SEEK(" no PTS in packet after seeking\n"); } else TRACE_SEEK(" _NextPacket() failed!\n"); *time = foundTime; TRACE_SEEK(" sought time: %.2fs\n", *time / 1000000.0); *frame = (uint64)(*time * frameRate / 1000000.0 + 0.5); TRACE_SEEK(" sought frame: %lld\n", *frame); } return B_OK; }
int main(int argc, char **argv) { int opt, ret, stream, flags; const char *filename; AVFormatContext *avf = NULL; int64_t min_ts, max_ts, ts; AVPacket packet; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': usage(0); default: usage(1); } } argc -= optind; argv += optind; if (!argc) usage(1); filename = *argv; argv++; argc--; av_register_all(); if ((ret = avformat_open_input(&avf, filename, NULL, NULL)) < 0) { fprintf(stderr, "%s: %s\n", filename, av_err2str(ret)); return 1; } if ((ret = avformat_find_stream_info(avf, NULL)) < 0) { fprintf(stderr, "%s: could not find codec parameters: %s\n", filename, av_err2str(ret)); return 1; } for (; argc; argc--, argv++) { if (!strcmp(*argv, "read")) { ret = av_read_frame(avf, &packet); if (ret < 0) { printf("read: %d (%s)\n", ret, av_err2str(ret)); } else { AVRational *tb = &avf->streams[packet.stream_index]->time_base; printf("read: %d size=%d stream=%d dts=%s (%s) pts=%s (%s)\n", ret, packet.size, packet.stream_index, av_ts2str(packet.dts), av_ts2timestr(packet.dts, tb), av_ts2str(packet.pts), av_ts2timestr(packet.pts, tb)); av_packet_unref(&packet); } } else if (sscanf(*argv, "seek:%i:%"SCNi64":%"SCNi64":%"SCNi64":%i", &stream, &min_ts, &ts, &max_ts, &flags) == 5) { ret = avformat_seek_file(avf, stream, min_ts, ts, max_ts, flags); printf("seek: %d (%s)\n", ret, av_err2str(ret)); } else { fprintf(stderr, "'%s': unknown command\n", *argv); return 1; } } avformat_close_input(&avf); return 0; }
void ImageLoader::loadImage( const QString& fileName, const int index ) { const QString extension = QFileInfo(fileName).suffix().toLower(); if( extension == "dcx" ) { QImage img = createSlideImage_( flow_->slideSize(), fileName, false, Qt::darkCyan, Qt::cyan ); flow_->setSlide( index, img ); return; } if( extension == "pyr" ) { QImageReader reader( fileName + "amid/0.jpg" ); QImage img = reader.read(); img.setText( "source", fileName ); flow_->setSlide( index, img ); return; } QImageReader reader( fileName ); if( reader.canRead( )) { QImage img = reader.read(); img.setText( "source", fileName ); flow_->setSlide( index, img ); return; } else if( reader.error() != QImageReader::UnsupportedFormatError ) return; if( !MovieContent::getSupportedExtensions().contains( extension )) return; AVFormatContext* avFormatContext; if( avformat_open_input( &avFormatContext, fileName.toAscii(), 0, 0 ) != 0 ) return; if( avformat_find_stream_info( avFormatContext, 0 ) < 0 ) return; // find the first video stream int streamIdx = -1; for( unsigned int i = 0; i < avFormatContext->nb_streams; ++i ) { if( avFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) { streamIdx = i; break; } } if( streamIdx == -1 ) return; AVStream* videostream = avFormatContext->streams[streamIdx]; AVCodecContext* avCodecContext = videostream->codec; AVCodec* codec = avcodec_find_decoder( avCodecContext->codec_id ); if( !codec ) return; if( avcodec_open2( avCodecContext, codec, 0 ) < 0 ) return; AVFrame* avFrame = avcodec_alloc_frame(); AVFrame* avFrameRGB = avcodec_alloc_frame(); QImage image( avCodecContext->width, avCodecContext->height, QImage::Format_RGB32 ); int numBytes = avpicture_get_size( PIX_FMT_RGB24, image.width(), image.height( )); uint8_t* buffer = (uint8_t*)av_malloc( numBytes * sizeof(uint8_t)); avpicture_fill( (AVPicture*)avFrameRGB, buffer, PIX_FMT_RGB24, image.width(), image.height( )); SwsContext* swsContext = sws_getContext( avCodecContext->width, avCodecContext->height, avCodecContext->pix_fmt, image.width(), image.height(), PIX_FMT_RGB24, SWS_FAST_BILINEAR, 0, 0, 0 ); // seek to 10% of movie time const int64_t den2 = videostream->time_base.den * videostream->r_frame_rate.den; const int64_t num2 = videostream->time_base.num * videostream->r_frame_rate.num; const int64_t num_frames = av_rescale( videostream->duration, num2, den2 ); const int64_t desiredTimestamp = videostream->start_time + av_rescale( num_frames / 10, den2, num2 ); if( avformat_seek_file( avFormatContext, streamIdx, 0, desiredTimestamp, desiredTimestamp, 0 ) != 0 ) { return; } AVPacket packet; while( av_read_frame( avFormatContext, &packet ) >= 0 ) { if( packet.stream_index != streamIdx ) continue; int frameFinished; avcodec_decode_video2( avCodecContext, avFrame, &frameFinished, &packet ); if( !frameFinished ) continue; sws_scale( swsContext, avFrame->data, avFrame->linesize, 0, avCodecContext->height, avFrameRGB->data, avFrameRGB->linesize ); unsigned char* src = (unsigned char*)avFrameRGB->data[0]; for( int y = 0; y < image.height(); ++y ) { QRgb* scanLine = (QRgb*)image.scanLine(y); for( int x = 0; x < image.width(); ++x ) scanLine[x] = qRgb(src[3*x], src[3*x+1], src[3*x+2]); src += avFrameRGB->linesize[0]; } av_free_packet( &packet ); image.setText( "source", fileName ); flow_->setSlide( index, image ); break; } avformat_close_input( &avFormatContext ); sws_freeContext( swsContext ); av_free( avFrame ); av_free( avFrameRGB ); }
void sxpi_demuxing_run(struct demuxing_ctx *ctx) { int ret; int in_err, out_err; TRACE(ctx, "demuxing packets in queue %p", ctx->pkt_queue); for (;;) { AVPacket pkt; struct message msg; ret = av_thread_message_queue_recv(ctx->src_queue, &msg, AV_THREAD_MESSAGE_NONBLOCK); if (ret != AVERROR(EAGAIN)) { if (ret < 0) break; if (msg.type == MSG_SEEK) { av_assert0(!ctx->is_image); /* Make later modules stop working ASAP */ av_thread_message_flush(ctx->pkt_queue); /* do actual seek so the following packet that will be pulled in * this current thread will be at the (approximate) requested time */ const int64_t seek_to = *(int64_t *)msg.data; LOG(ctx, INFO, "Seek in media at ts=%s", PTS2TIMESTR(seek_to)); ret = avformat_seek_file(ctx->fmt_ctx, -1, INT64_MIN, seek_to, seek_to, 0); if (ret < 0) { sxpi_msg_free_data(&msg); break; } } /* Forward the message */ ret = av_thread_message_queue_send(ctx->pkt_queue, &msg, 0); if (ret < 0) { sxpi_msg_free_data(&msg); break; } } msg.type = MSG_PACKET; ret = pull_packet(ctx, &pkt); if (ret < 0) break; TRACE(ctx, "pulled a packet of size %d, sending to decoder", pkt.size); msg.data = av_memdup(&pkt, sizeof(pkt)); if (!msg.data) { av_packet_unref(&pkt); break; } ret = av_thread_message_queue_send(ctx->pkt_queue, &msg, 0); TRACE(ctx, "sent packet to decoder, ret=%s", av_err2str(ret)); if (ret < 0) { av_packet_unref(&pkt); av_freep(&msg.data); if (ret != AVERROR_EOF && ret != AVERROR_EXIT) LOG(ctx, ERROR, "Unable to send packet to decoder: %s", av_err2str(ret)); TRACE(ctx, "can't send pkt to decoder: %s", av_err2str(ret)); av_thread_message_queue_set_err_recv(ctx->pkt_queue, ret); break; } } if (ret < 0 && ret != AVERROR_EOF) { in_err = out_err = ret; } else { in_err = AVERROR_EXIT; out_err = AVERROR_EOF; } TRACE(ctx, "notify user with %s and decoder with %s", av_err2str(in_err), av_err2str(out_err)); av_thread_message_queue_set_err_send(ctx->src_queue, in_err); av_thread_message_flush(ctx->src_queue); av_thread_message_queue_set_err_recv(ctx->pkt_queue, out_err); }
void player_decode(void *data) { State *state = (State *) data; int ret; int eof = 0; for (;;) { if (state->abort_request) { break; } if (state->paused != state->last_paused) { state->last_paused = state->paused; if (state->paused) { state->read_pause_return = av_read_pause(state->pFormatCtx); } else { av_read_play(state->pFormatCtx); } } if (state->seek_req) { int64_t seek_target = state->seek_pos; int64_t seek_min = state->seek_rel > 0 ? seek_target - state->seek_rel + 2: INT64_MIN; int64_t seek_max = state->seek_rel < 0 ? seek_target - state->seek_rel - 2: INT64_MAX; ret = avformat_seek_file(state->pFormatCtx, -1, seek_min, seek_target, seek_max, state->seek_flags); if (ret < 0) { fprintf(stderr, "%s: error while seeking\n", state->pFormatCtx->filename); } else { if (state->audio_stream >= 0) { avcodec_flush_buffers(state->audio_st->codec); } state->notify_callback(state->clazz, MEDIA_SEEK_COMPLETE, 0, 0, FROM_THREAD); } state->seek_req = 0; eof = 0; } if (state->paused) { goto sleep; } AVPacket packet; memset(&packet, 0, sizeof(packet)); //make sure we can safely free it int i; for (i = 0; i < state->pFormatCtx->nb_streams; ++i) { //av_init_packet(&packet); ret = av_read_frame(state->pFormatCtx, &packet); if (ret < 0) { if (ret == AVERROR_EOF || url_feof(state->pFormatCtx->pb)) { eof = 1; break; } } int frame_size_ptr; ret = decode_frame_from_packet(state, &packet, &frame_size_ptr, FROM_THREAD); av_free_packet(&packet); if (ret != 0) { //an error or a frame decoded // TODO add this bacl=k } } if (eof) { break; } sleep: usleep(100); } if (eof) { state->notify_callback(state->clazz, MEDIA_PLAYBACK_COMPLETE, 0, 0, FROM_THREAD); } }
int getFrameAt(int64_t timeUs, int width, int height) { int ret = -1; AVFrame* pFrame = NULL; ret = avformat_seek_file(m_pFormatContext, -1, INT16_MIN, timeUs, INT16_MAX, 0); pFrame = av_frame_alloc(); m_pThumbFrame = av_frame_alloc(); ret = openDecoder(); if (ret != 0) { av_frame_free(&pFrame); av_frame_free(&m_pThumbFrame); return ret; } #ifdef DEBUG_SPEND_TIME #ifdef _WIN32 DWORD start_time = timeGetTime(); #else struct timeval start, end; gettimeofday(&start, NULL); #endif #endif ret = decodeOneFrame(pFrame); if (ret < 0) { av_frame_free(&pFrame); av_frame_free(&m_pThumbFrame); return ret; } #ifdef DEBUG_SPEND_TIME #ifdef _WIN32 DWORD end_time = timeGetTime(); printf("decodeOneFrame spend time = %d ms\n", end_time - start_time); #else gettimeofday(&end, NULL); int spend_time = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000; printf("spend_time = %d ms\n", spend_time); #endif #endif ret = getThumbnail(pFrame, m_pThumbFrame, width, height); if (ret < 0) { av_frame_free(&pFrame); av_frame_free(&m_pThumbFrame); return ret; } // save the rgb565 FILE *pFile = fopen(strThumbFileName, "ab"); if (pFile) { fwrite(m_pThumbFrame->data[0], 1, m_pThumbFrame->width * m_pThumbFrame->height * 2, pFile); fclose(pFile); } av_frame_free(&pFrame); av_frame_free(&m_pThumbFrame); closeDecoder(); return ret; }
bool Demuxer::didSeek(const Timer &timer, sf::Time oldPosition) { resetEndOfFileStatus(); sf::Time newPosition = timer.getOffset(); std::set< std::shared_ptr<Stream> > connectedStreams; if (m_connectedVideoStream) connectedStreams.insert(m_connectedVideoStream); if (m_connectedAudioStream) connectedStreams.insert(m_connectedAudioStream); if (m_connectedSubtitleStream) connectedStreams.insert(m_connectedSubtitleStream); CHECK(!connectedStreams.empty(), "Inconcistency error: seeking with no active stream"); // Trivial seeking to beginning if (newPosition == sf::Time::Zero) { int64_t timestamp = 0; if (m_formatCtx->iformat->flags & AVFMT_SEEK_TO_PTS && m_formatCtx->start_time != AV_NOPTS_VALUE) timestamp += m_formatCtx->start_time; // Flush all streams for (std::shared_ptr<Stream> stream : connectedStreams) stream->flushBuffers(); flushBuffers(); // Seek to beginning int err = avformat_seek_file(m_formatCtx, -1, INT64_MIN, timestamp, INT64_MAX, AVSEEK_FLAG_BACKWARD); if (err < 0) { sfeLogError("Error while seeking at time " + s(newPosition.asMilliseconds()) + "ms"); return false; } } else // Seeking to some other position { // Initial target seek point int64_t timestamp = newPosition.asSeconds() * AV_TIME_BASE; // < 0 = before seek point // > 0 = after seek point std::map< std::shared_ptr<Stream>, sf::Time> seekingGaps; static const float brokenSeekingThreshold = 60.f; // seconds bool didReseekBackward = false; bool didReseekForward = false; int tooEarlyCount = 0; int tooLateCount = 0; int brokenSeekingCount = 0; int ffmpegSeekFlags = AVSEEK_FLAG_BACKWARD; do { // Flush all streams for (std::shared_ptr<Stream> stream : connectedStreams) stream->flushBuffers(); flushBuffers(); // Seek to new estimated target if (m_formatCtx->iformat->flags & AVFMT_SEEK_TO_PTS && m_formatCtx->start_time != AV_NOPTS_VALUE) timestamp += m_formatCtx->start_time; int err = avformat_seek_file(m_formatCtx, -1, timestamp - 10 * AV_TIME_BASE, timestamp, timestamp, ffmpegSeekFlags); CHECK0(err, "avformat_seek_file failure"); // Compute the new gap for (std::shared_ptr<Stream> stream : connectedStreams) { sf::Time gap = stream->computeEncodedPosition() - newPosition; seekingGaps[stream] = gap; } tooEarlyCount = 0; tooLateCount = 0; brokenSeekingCount = 0; // Check the current situation for (std::pair< std::shared_ptr<Stream>, sf::Time>&& gapByStream : seekingGaps) { // < 0 = before seek point // > 0 = after seek point const sf::Time& gap = gapByStream.second; float absoluteDiff = fabs(gap.asSeconds()); // Before seek point if (gap < sf::Time::Zero) { if (absoluteDiff > brokenSeekingThreshold) { brokenSeekingCount++; tooEarlyCount++; } // else: a bit early but not too much, this is the final situation we want } // After seek point else if (gap > sf::Time::Zero) { tooLateCount++; if (absoluteDiff > brokenSeekingThreshold) brokenSeekingCount++; // TODO: unhandled for now => should seek to non-key frame } if (brokenSeekingCount > 0) sfeLogWarning("Seeking on " + gapByStream.first->description() + " is broken! Gap: " + s(gap.asSeconds()) + "s"); } CHECK(false == (tooEarlyCount && tooLateCount), "Both too late and too early for different streams, unhandled situation!"); // Define what to do next if (tooEarlyCount) { // Go forward by 1 sec timestamp += AV_TIME_BASE; didReseekForward = true; } else if (tooLateCount) { // Go backward by 1 sec timestamp -= AV_TIME_BASE; didReseekBackward = true; } if (brokenSeekingCount) { if (ffmpegSeekFlags & AVSEEK_FLAG_ANY) { sfeLogError("Seeking is really broken in the media, giving up"); return false; } else { // Try to seek to non-key frame before giving up // Image may be wrong but it's better than nothing :) ffmpegSeekFlags |= AVSEEK_FLAG_ANY; sfeLogError("Media has broken seeking index, trying to seek to non-key frame"); } } CHECK(!(didReseekBackward && didReseekForward), "infinitely seeking backward and forward"); } while (tooEarlyCount != 0 || tooLateCount != 0); } return true; }
jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *env, jclass clazz, jobject ptr, jobject bitmap, jintArray data) { if (ptr == NULL || bitmap == nullptr) { return 0; } VideoInfo *info = (VideoInfo *) ptr; int ret = 0; int got_frame = 0; while (true) { if (info->pkt.size == 0) { ret = av_read_frame(info->fmt_ctx, &info->pkt); //LOGD("got packet with size %d", info->pkt.size); if (ret >= 0) { info->orig_pkt = info->pkt; } } if (info->pkt.size > 0) { ret = decode_packet(info, &got_frame); if (ret < 0) { if (info->has_decoded_frames) { ret = 0; } info->pkt.size = 0; } else { //LOGD("read size %d from packet", ret); info->pkt.data += ret; info->pkt.size -= ret; } if (info->pkt.size == 0) { av_free_packet(&info->orig_pkt); } } else { info->pkt.data = NULL; info->pkt.size = 0; ret = decode_packet(info, &got_frame); if (ret < 0) { LOGE("can't decode packet flushed %s", info->src); return 0; } if (got_frame == 0) { if (info->has_decoded_frames) { //LOGD("file end reached %s", info->src); if ((ret = avformat_seek_file(info->fmt_ctx, -1, std::numeric_limits<int64_t>::min(), 0, std::numeric_limits<int64_t>::max(), 0)) < 0) { LOGE("can't seek to begin of file %s, %s", info->src, av_err2str(ret)); return 0; } else { avcodec_flush_buffers(info->video_dec_ctx); } } } } if (ret < 0) { return 0; } if (got_frame) { //LOGD("decoded frame with w = %d, h = %d, format = %d", info->frame->width, info->frame->height, info->frame->format); if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_BGRA || info->frame->format == AV_PIX_FMT_YUVJ420P) { jint *dataArr = env->GetIntArrayElements(data, 0); if (dataArr != nullptr) { dataArr[3] = (int) (1000 * info->frame->pkt_pts * av_q2d(info->video_stream->time_base)); env->ReleaseIntArrayElements(data, dataArr, 0); } void *pixels; if (AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0) { if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_YUVJ420P) { //LOGD("y %d, u %d, v %d, width %d, height %d", info->frame->linesize[0], info->frame->linesize[2], info->frame->linesize[1], info->frame->width, info->frame->height); libyuv::I420ToARGB(info->frame->data[0], info->frame->linesize[0], info->frame->data[2], info->frame->linesize[2], info->frame->data[1], info->frame->linesize[1], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height); } else if (info->frame->format == AV_PIX_FMT_BGRA) { libyuv::ABGRToARGB(info->frame->data[0], info->frame->linesize[0], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height); } AndroidBitmap_unlockPixels(env, bitmap); } } info->has_decoded_frames = true; av_frame_unref(info->frame); return 1; } } return 0; }
int decode_thread(void *arg) { VideoState *is = (VideoState *)arg; AVPacket pkt1, *packet = &pkt1; AVDictionary *io_dict = NULL; AVIOInterruptCB callback; int video_index = -1; int audio_index = -1; int i; int ret; int eof = 0; is->videoStream=-1; is->audioStream=-1; AVDictionary *options = NULL; av_dict_set(&options, "icy", "1", 0); av_dict_set(&options, "user-agent", "FFmpegMediaPlayer", 0); if (is->headers) { av_dict_set(&options, "headers", is->headers, 0); } if (is->offset > 0) { is->pFormatCtx = avformat_alloc_context(); is->pFormatCtx->skip_initial_bytes = is->offset; //is->pFormatCtx->iformat = av_find_input_format("mp3"); } // will interrupt blocking functions if we quit! callback.callback = decode_interrupt_cb; callback.opaque = is; if (avio_open2(&is->io_context, is->filename, 0, &callback, &io_dict)) { fprintf(stderr, "Unable to open I/O for %s\n", is->filename); return -1; } // Open video file if(avformat_open_input(&is->pFormatCtx, is->filename, NULL, &options)!=0) return -1; // Couldn't open file // Retrieve stream information if(avformat_find_stream_info(is->pFormatCtx, NULL)<0) return -1; // Couldn't find stream information // Dump information about file onto standard error av_dump_format(is->pFormatCtx, 0, is->filename, 0); // Find the first video stream for(i=0; i<is->pFormatCtx->nb_streams; i++) { if(is->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && video_index < 0) { video_index=i; } if(is->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && audio_index < 0) { audio_index=i; } set_codec(is->pFormatCtx, i); } if(audio_index >= 0) { stream_component_open(is, audio_index); } if(video_index >= 0) { stream_component_open(is, video_index); } if(is->videoStream < 0 && is->audioStream < 0) { //if(is->videoStream < 0 || is->audioStream < 0) { fprintf(stderr, "%s: could not open codecs\n", is->filename); notify(is, MEDIA_ERROR, 0, 0); return 0; } set_rotation(is->pFormatCtx, is->audio_st, is->video_st); set_framerate(is->pFormatCtx, is->audio_st, is->video_st); set_filesize(is->pFormatCtx); set_chapter_count(is->pFormatCtx); // main decode loop for(;;) { if(is->quit) { break; } /*if (is->paused != is->last_paused) { is->last_paused = is->paused; if (is->paused) is->read_pause_return = av_read_pause(is->pFormatCtx); else av_read_play(is->pFormatCtx); }*/ // seek stuff goes here if(is->seek_req) { int64_t seek_target = is->seek_pos; int64_t seek_min = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN; int64_t seek_max = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX; int ret = avformat_seek_file(is->pFormatCtx, -1, seek_min, seek_target, seek_max, is->seek_flags); if(ret < 0) { fprintf(stderr, "%s: error while seeking\n", is->pFormatCtx->filename); } else { if(is->audioStream >= 0) { packet_queue_flush(&is->audioq); packet_queue_put(is, &is->audioq, &is->flush_pkt); } if(is->videoStream >= 0) { packet_queue_flush(&is->videoq); packet_queue_put(is, &is->videoq, &is->flush_pkt); } notify(is, MEDIA_SEEK_COMPLETE, 0, 0); } is->seek_req = 0; eof = 0; } if (is->audioq.size >= MAX_AUDIOQ_SIZE && !is->prepared) { queueAudioSamples(&is->audio_player, is); notify(is, MEDIA_PREPARED, 0, 0); is->prepared = 1; } if(is->audioq.size > MAX_AUDIOQ_SIZE || is->videoq.size > MAX_VIDEOQ_SIZE) { SDL_Delay(10); continue; } if((ret = av_read_frame(is->pFormatCtx, packet)) < 0) { if (ret == AVERROR_EOF || !is->pFormatCtx->pb->eof_reached) { eof = 1; break; } if(is->pFormatCtx->pb->error == 0) { SDL_Delay(100); /* no error; wait for user input */ continue; } else { break; } } // Is this a packet from the video stream? if(packet->stream_index == is->videoStream) { packet_queue_put(is, &is->videoq, packet); } else if(packet->stream_index == is->audioStream) { packet_queue_put(is, &is->audioq, packet); } else { av_free_packet(packet); } if (eof) { break; } } if (eof) { notify(is, MEDIA_PLAYBACK_COMPLETE, 0, 0); } return 0; }
JNIEXPORT jint JNICALL Java_com_jiuan_it_ipc_utils_RtspFromFFMPEG_thread_1RecvPacket (JNIEnv *env, jobject obj, jlong seek_pos, jint seek_req, jint isAudio) { LOGD("%s-------%d进入该函数", __FUNCTION__, __LINE__); AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket)); av_init_packet(packet); int index = 0; int ret = -1; //调整视频的播放位置的处理(快进、后退) if (seek_req) { int64_t seek_target = seek_pos; int64_t seek_min = INT64_MIN; int64_t seek_max = INT64_MAX; //LOGE("--------*********-----------seek_pos:%lld\n", seek_pos); //把APP的时间戳转换成设备的时间戳 //int64_t seek_conv_target = av_rescale_q(seek_target, pCodecCtx_video->time_base, timebase_pri); //把设备的时间戳转换成APP的时间戳 int64_t seek_conv_target = av_rescale_q(seek_target, timebase_pri, pCodecCtx_video->time_base); LOGE("--------*********-----------timebase_pri:%d\n", timebase_pri.den); LOGE("--------*********-----------time_base:%d\n", pCodecCtx_video->time_base.den); ret = avformat_seek_file(pFormatCtx, videoindex, seek_min, seek_conv_target, seek_max, 0); LOGE("--------*********-----------seek_conv_target:%lld\n", seek_conv_target); if (ret >= 0) { AVPacket_cbuf_free(&m_cbuf_video); AVPacket_cbuf_free(&m_cbuf_audio); ret = 1; goto Errlab; } } LOGD("av_read_frameing................\n"); if (av_read_frame(pFormatCtx, packet)>=0) { if(packet->stream_index==videoindex) { //LOGE("packet_pts_video->pts = %f\n", packet.pts * av_q2d(pCodecCtx_video->time_base)); LOGD("av_read_frame:OK\n"); //AVPacket *packet_cpy = (AVPacket *)av_malloc(sizeof(AVPacket)); //av_copy_packet(packet_cpy, &packet); cbuf_enqueue(&m_cbuf_video, packet); //LOGE("thread_1RecvPacket.m_cbuf.size:%d\n", m_cbuf_video.size); //av_free_packet(&packet); } else if(packet->stream_index==audioindex && isAudio) { //LOGE("packet_pts_audio->pts = %f\n", packet.pts * av_q2d(pCodecCtx_audio->time_base)); LOGD("av_read_audio_frame:OK\n"); //AVPacket *packet_cpy = (AVPacket *)av_malloc(sizeof(AVPacket)); //av_copy_packet(packet_cpy, &packet); cbuf_enqueue(&m_cbuf_audio, packet); //LOGE("thread_1RecvPacket.m_cbuf.size:%d\n", m_cbuf_audio.size); //av_free_packet(&packet); } else { av_free_packet(packet); } ret = 0; } else { if (cbuf_empty(&m_cbuf_video)){ LOGE("cbuf_empty:OK\n"); ret = -1; }else { ret = 0; } } Errlab: return ret; }
void Editor::seek_time(float seconds, bool display) { int64_t ts_rel = seconds / m_videoTimeBase; int64_t req_ts = pts_val(m_timeStampStart + ts_rel); int64_t ts = pts_val(req_ts + m_timeFudge); int64_t min_ts = ts - 2.0 / m_videoTimeBase; int64_t max_ts = ts; int64_t pts_base = av_rescale_q(ts_rel + m_timeFudge, m_videoTimeBase_q, AV_TIME_BASE_Q); // From time to time, my receiver (Kathrein UFS-910) screws up // and gives me the start of the stream instead of the requested // offset. So we have to wrap this in a loop and try again... int tries; for(tries = 5; tries > 0; --tries) { loff_t byte_offset = (loff_t)-1; avcodec_flush_buffers(m_videoCodecCtx); // If we got an index file, use it if(m_indexFile) byte_offset = m_indexFile->bytePositionForPTS(pts_base); if(byte_offset != (loff_t)-1) { log_debug("Seeking to byte pos %'10lld", byte_offset); if(avformat_seek_file(m_stream, -1, 0, byte_offset, byte_offset, AVSEEK_FLAG_BYTE) < 0) { log_debug("Byte seeking to %fs failed", seconds); byte_offset = (loff_t)-1; } } // Fallback to binary search if(byte_offset == (loff_t)-1) { log_debug("Seeking to pts %'10lld", ts); if(avformat_seek_file(m_stream, m_videoID, min_ts, ts, max_ts, 0) < 0) { error("could not seek"); return; } } resetBuffer(); // Detect Kathrein bug if(byte_offset && seconds > 1.0 // not actually at start of stream && m_frameTimestamps[0] - m_timeStampFirstKey == 0) // bug { log_debug("KATHREIN BUG detected"); continue; } break; } if(!tries) QMessageBox::critical(this, tr("Error"), tr("Seeking failed, sorry.")); int64_t diff = req_ts - m_frameTimestamps[0]; if(llabs(diff) > 20.0 / m_videoTimeBase) log_debug("seek_time: missed destination (PTS: %'10lld) by more than 20 seconds, fudge = %'10lld", ts, m_frameTimestamps[0], diff, m_videoTimeBase * diff, m_timeFudge ); m_timeFudge += diff; if(llabs(m_timeFudge) > 10.0 / m_videoTimeBase) { log_debug("Resetting time fudge value"); m_timeFudge = 0; } if(display) displayCurrentFrame(); }
bool seekInternal(double t, int depth) { ResetRetries(); emptyFrameQueue(); audioHandler->clearQueue(); int64_t firstTs = getFirstSeekTs(); double backSeek = (double)depth * 2.0f + 1.0f; int64_t minTs = tsFromTime(t - backSeek - 2.5) + firstTs; int64_t ts = tsFromTime(t - backSeek) + firstTs; int64_t maxTs = tsFromTime(t - backSeek) + firstTs; // There is no discernible way to determine if negative timestamps are allowed // (or even required) to seek to low timestamps. // On some files you must seek to negative timestamps to be able to seek to 0 // but on other files you get weird results from seeking to below 0. // So, every other try, we will allow seeking to negative timestamps. if((depth % 2) == 1){ minTs = std::max((int64_t)0, minTs); ts = std::max((int64_t)0, minTs); maxTs = std::max((int64_t)0, minTs); } FlogD("Trying to seek to minTs: " << minTs << " ts: " << ts << " maxTs: " << maxTs << " with firsTs: " << firstTs); int flags = 0; if(ts < pFormatCtx->streams[videoStream]->cur_dts) flags |= AVSEEK_FLAG_BACKWARD; int seekRet = avformat_seek_file(pFormatCtx, videoStream, minTs, ts, maxTs, flags); if(seekRet > 0){ FlogD("avformat_seek_file failed, returned " << seekRet); return false; } avcodec_flush_buffers(pCodecCtx); double newTime = t + timeFromTs(firstPts); double actualTime = skipToTs(newTime); // consider the seek failed and try again if the actual time diffs more than .5 seconds // from the desired new time. FlogD("wanted to seek to " << newTime << " and ended up at " << actualTime); bool ret = true; if(fabsf(newTime - actualTime) > .5){ if(depth < 5){ FlogD("not good enough, trying again"); return seekInternal(t, depth + 1); } else{ ret = false; FlogW("seek failed, wanted to seek to " << newTime << " and ended up at " << actualTime); } } timeHandler->SetTime(actualTime); stepIntoQueue = true; audioHandler->onSeek(); return ret; }
SINT SoundSourceFFmpeg::seekSampleFrame(SINT frameIndex) { DEBUG_ASSERT(isValidFrameIndex(frameIndex)); int ret = 0; qint64 i = 0; struct ffmpegLocationObject *l_STestObj = nullptr; if (frameIndex < 0 || frameIndex < m_lCacheStartFrame) { // Seek to set (start of the stream which is FFmpeg frame 0) // because we are dealing with compressed audio FFmpeg takes // best of to seek that point (in this case 0 Is always there) // in every other case we should provide MIN and MAX tolerance // which we can take. // FFmpeg just just can't take zero as MAX tolerance so we try to // just make some tolerable (which is never used because zero point // should always be there) some number (which is 0xffff 65535) // that is chosen because in WMA frames can be that big and if it's // smaller than the frame we are seeking we can get into error ret = avformat_seek_file(m_pFormatCtx, m_iAudioStream, 0, 0, 0xffff, AVSEEK_FLAG_BACKWARD); if (ret < 0) { qDebug() << "SoundSourceFFmpeg::seek: Can't seek to 0 byte!"; return -1; } clearCache(); m_lCacheStartFrame = 0; m_lCacheEndFrame = 0; m_lCacheLastPos = 0; m_lCacheFramePos = 0; m_lStoredSeekPoint = -1; // Try to find some jump point near to // where we are located so we don't needed // to try guess it if (m_SJumpPoints.size() > 0) { l_STestObj = m_SJumpPoints.first(); if (frameIndex > l_STestObj->startFrame) { for (i = 0; i < m_SJumpPoints.size(); i++) { if (m_SJumpPoints[i]->startFrame >= frameIndex) { if (i > 0) { i--; } m_lCacheFramePos = m_SJumpPoints[i]->startFrame; m_lStoredSeekPoint = m_SJumpPoints[i]->pos; m_SStoredJumpPoint = m_SJumpPoints[i]; break; } } } } if (frameIndex == 0) { // Because we are in the beginning just read cache full // but leave 50 of just in case // -1 one means we are seeking from current position and // filling the cache readFramesToCache((AUDIOSOURCEFFMPEG_CACHESIZE - 50), AUDIOSOURCEFFMPEG_FILL_FROM_CURRENTPOS); } } if (m_lCacheEndFrame <= frameIndex) { // Cache tries to read until it gets to frameIndex // after that we still read 100 FFmpeg frames to memory // so we have good cache to go forward (100) and backward (900) // from the point readFramesToCache(100, frameIndex); } m_currentMixxxFrameIndex = frameIndex; m_bIsSeeked = true; return frameIndex; }