+int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled)
+{
+ AVPacket pkt;
+ av_init_packet(&pkt);
+ AVFrame *frame = av_frame_alloc();
+ if( !frame ) {
+ fprintf(stderr, "FFMPEG::scan: av_frame_alloc failed\n");
+ return -1;
+ }
+
+ index_state->add_video_markers(ffvideo.size());
+ index_state->add_audio_markers(ffaudio.size());
+
+ for( int i=0; i<(int)fmt_ctx->nb_streams; ++i ) {
+ AVDictionary *copts = 0;
+ av_dict_copy(&copts, opts, 0);
+ AVStream *st = fmt_ctx->streams[i];
+ AVCodecID codec_id = st->codec->codec_id;
+ AVCodec *decoder = avcodec_find_decoder(codec_id);
+ if( avcodec_open2(st->codec, decoder, &copts) < 0 )
+ fprintf(stderr, "FFMPEG::scan: codec open failed\n");
+ av_dict_free(&copts);
+ }
+ int errs = 0;
+ for( int64_t count=0; !*canceled; ++count ) {
+ av_packet_unref(&pkt);
+ pkt.data = 0; pkt.size = 0;
+
+ int ret = av_read_frame(fmt_ctx, &pkt);
+ if( ret < 0 ) {
+ if( ret == AVERROR_EOF ) break;
+ if( ++errs > 100 ) {
+ ff_err(ret, "over 100 read_frame errs\n");
+ break;
+ }
+ continue;
+ }
+ if( !pkt.data ) continue;
+ int i = pkt.stream_index;
+ if( i < 0 || i >= (int)fmt_ctx->nb_streams ) continue;
+ AVStream *st = fmt_ctx->streams[i];
+ AVCodecContext *avctx = st->codec;
+ if( pkt.pos > *scan_position ) *scan_position = pkt.pos;
+
+ switch( avctx->codec_type ) {
+ case AVMEDIA_TYPE_VIDEO: {
+ int vidx = ffvideo.size();
+ while( --vidx>=0 && ffvideo[vidx]->fidx != i );
+ if( vidx < 0 ) break;
+ FFVideoStream *vid = ffvideo[vidx];
+ int64_t tstmp = pkt.dts;
+ if( tstmp == AV_NOPTS_VALUE ) tstmp = pkt.pts;
+ if( tstmp != AV_NOPTS_VALUE && (pkt.flags & AV_PKT_FLAG_KEY) && pkt.pos > 0 ) {
+ if( vid->nudge != AV_NOPTS_VALUE ) tstmp -= vid->nudge;
+ double secs = to_secs(tstmp, st->time_base);
+ int64_t frm = secs * vid->frame_rate + 0.5;
+ if( frm < 0 ) frm = 0;
+ index_state->put_video_mark(vidx, frm, pkt.pos);
+ }
+#if 0
+ while( pkt.size > 0 ) {
+ av_frame_unref(frame);
+ int got_frame = 0;
+ int ret = vid->decode_frame(&pkt, frame, got_frame);
+ if( ret <= 0 ) break;
+// if( got_frame ) {}
+ pkt.data += ret;
+ pkt.size -= ret;
+ }
+#endif
+ break; }
+ case AVMEDIA_TYPE_AUDIO: {
+ int aidx = ffaudio.size();
+ while( --aidx>=0 && ffaudio[aidx]->fidx != i );
+ if( aidx < 0 ) break;
+ FFAudioStream *aud = ffaudio[aidx];
+ int64_t tstmp = pkt.pts;
+ if( tstmp == AV_NOPTS_VALUE ) tstmp = pkt.dts;
+ if( tstmp != AV_NOPTS_VALUE && (pkt.flags & AV_PKT_FLAG_KEY) && pkt.pos > 0 ) {
+ if( aud->nudge != AV_NOPTS_VALUE ) tstmp -= aud->nudge;
+ double secs = to_secs(tstmp, st->time_base);
+ int64_t sample = secs * aud->sample_rate + 0.5;
+ if( sample < 0 ) sample = 0;
+ index_state->put_audio_mark(aidx, sample, pkt.pos);
+ }
+ while( pkt.size > 0 ) {
+ int ch = aud->channel0, nch = aud->channels;
+ int64_t pos = index_state->pos(ch);
+ if( pos != aud->curr_pos ) {
+if( abs(pos-aud->curr_pos) > 1 )
+printf("audio%d pad %ld %ld (%ld)\n", aud->idx, pos, aud->curr_pos, pos-aud->curr_pos);
+ index_state->pad_data(ch, nch, aud->curr_pos);
+ }
+ av_frame_unref(frame);
+ int got_frame = 0;
+ int ret = aud->decode_frame(&pkt, frame, got_frame);
+ if( ret <= 0 ) break;
+ if( got_frame ) {
+ float *samples;
+ int len = aud->get_samples(samples,
+ &frame->extended_data[0], frame->nb_samples);
+ for( int i=0; i<nch; ++i )
+ index_state->put_data(ch+i,nch,samples+i,len);
+ aud->curr_pos += len;
+ }
+ pkt.data += ret;
+ pkt.size -= ret;
+ }
+ break; }
+ default: break;
+ }
+ }
+ av_frame_free(&frame);
+ return 0;
+}
+
+void FFStream::load_markers(IndexMarks &marks, double rate)
+{
+ index_markers = &marks;
+ int in = 0;
+ int64_t sz = marks.size();
+ int max_entries = fmt_ctx->max_index_size / sizeof(AVIndexEntry) - 1;
+ int nb_ent = st->nb_index_entries;
+// some formats already have an index
+ if( nb_ent > 0 ) {
+ AVIndexEntry *ep = &st->index_entries[nb_ent-1];
+ int64_t tstmp = ep->timestamp;
+ if( nudge != AV_NOPTS_VALUE ) tstmp -= nudge;
+ double secs = ffmpeg->to_secs(tstmp, st->time_base);
+ int64_t no = secs * rate;
+ while( in < sz && marks[in].no <= no ) ++in;
+ }
+ int64_t len = sz - in;
+ int64_t count = max_entries - nb_ent;
+ if( count > len ) count = len;
+ for( int i=0; i<count; ++i ) {
+ int k = in + i * len / count;
+ int64_t no = marks[k].no, pos = marks[k].pos;
+ double secs = (double)no / rate;
+ int64_t tstmp = secs * st->time_base.den / st->time_base.num;
+ if( nudge != AV_NOPTS_VALUE ) tstmp += nudge;
+ av_add_index_entry(st, pos, tstmp, 0, 0, AVINDEX_KEYFRAME);
+ }
+}
+