asset drag/drop to viewers, bluebanana bug, listbox fontlist highlight
[goodguy/history.git] / cinelerra-5.1 / cinelerra / ffmpeg.C
index 172252f694416953d027ac4921987a65b33ca0ef..44a337e60e7e4e84841a71a3a5f3d7db2a460a6c 100644 (file)
@@ -21,6 +21,7 @@
 #include "file.h"
 #include "ffmpeg.h"
 #include "indexfile.h"
+#include "interlacemodes.h"
 #include "libdv.h"
 #include "libmjpeg.h"
 #include "mainerror.h"
@@ -725,6 +726,8 @@ FFVideoStream::FFVideoStream(FFMPEG *ffmpeg, AVStream *strm, int idx, int fidx)
        frame_rate = 0;
        aspect_ratio = 0;
        length = 0;
+       interlaced = 0;
+       top_field_first = 0;
 }
 
 FFVideoStream::~FFVideoStream()
@@ -822,6 +825,10 @@ int FFVideoStream::encode(VFrame *vframe)
 
 int FFVideoStream::encode_frame(AVPacket *pkt, AVFrame *frame, int &got_packet)
 {
+       if( frame ) {
+               frame->interlaced_frame = interlaced;
+               frame->top_field_first = top_field_first;
+       }
        int ret = avcodec_encode_video2(st->codec, pkt, frame, &got_packet);
        if( ret < 0 ) {
                ff_err(ret, "FFVideoStream::encode_frame: encode video failed\n");
@@ -848,6 +855,7 @@ AVPixelFormat FFVideoConvert::color_model_to_pix_fmt(int color_model)
        case BC_RGB565:         return AV_PIX_FMT_RGB565;
        case BC_RGB161616:      return AV_PIX_FMT_RGB48LE;
        case BC_RGBA16161616:   return AV_PIX_FMT_RGBA64LE;
+       case BC_AYUV16161616:   return AV_PIX_FMT_AYUV64LE;
        default: break;
        }
 
@@ -872,6 +880,7 @@ int FFVideoConvert::pix_fmt_to_color_model(AVPixelFormat pix_fmt)
        case AV_PIX_FMT_RGB565:         return BC_RGB565;
        case AV_PIX_FMT_RGB48LE:        return BC_RGB161616;
        case AV_PIX_FMT_RGBA64LE:       return BC_RGBA16161616;
+       case AV_PIX_FMT_AYUV64LE:       return BC_AYUV16161616;
        default: break;
        }
 
@@ -918,7 +927,7 @@ int FFVideoConvert::convert_picture_vframe(VFrame *frame, AVFrame *ip, AVFrame *
 
        AVPixelFormat pix_fmt = (AVPixelFormat)ip->format;
        convert_ctx = sws_getCachedContext(convert_ctx, ip->width, ip->height, pix_fmt,
-               frame->get_w(), frame->get_h(), ofmt, SWS_BICUBIC, NULL, NULL, NULL);
+               frame->get_w(), frame->get_h(), ofmt, SWS_POINT, NULL, NULL, NULL);
        if( !convert_ctx ) {
                fprintf(stderr, "FFVideoConvert::convert_picture_frame:"
                                " sws_getCachedContext() failed\n");
@@ -938,20 +947,27 @@ int FFVideoConvert::convert_cmodel(VFrame *frame, AVFrame *ip)
        // try direct transfer
        if( !convert_picture_vframe(frame, ip) ) return 1;
        // use indirect transfer
-       AVPixelFormat pix_fmt = (AVPixelFormat)ip->format;
-       const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
+       AVPixelFormat ifmt = (AVPixelFormat)ip->format;
+       const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(ifmt);
        int max_bits = 0;
        for( int i = 0; i <desc->nb_components; ++i ) {
                int bits = desc->comp[i].depth;
                if( bits > max_bits ) max_bits = bits;
        }
-// from libavcodec/pixdesc.c
-#define pixdesc_has_alpha(pixdesc) ((pixdesc)->nb_components == 2 || \
- (pixdesc)->nb_components == 4 || (pixdesc)->flags & AV_PIX_FMT_FLAG_PAL)
-       int icolor_model = pixdesc_has_alpha(desc) ?
-               (max_bits > 8 ? BC_RGBA16161616 : BC_RGBA8888) :
-               (max_bits > 8 ? BC_RGB161616 : BC_RGB888) ;
-       VFrame vframe(ip->width, ip->height, icolor_model);
+       int imodel = pix_fmt_to_color_model(ifmt);
+       int imodel_is_yuv = BC_CModels::is_yuv(imodel);
+       int cmodel = frame->get_color_model();
+       int cmodel_is_yuv = BC_CModels::is_yuv(cmodel);
+       if( imodel < 0 || imodel_is_yuv != cmodel_is_yuv ) {
+               imodel = cmodel_is_yuv ?
+                   (BC_CModels::has_alpha(cmodel) ?
+                       BC_AYUV16161616 :
+                       (max_bits > 8 ? BC_AYUV16161616 : BC_YUV444P)) :
+                   (BC_CModels::has_alpha(cmodel) ?
+                       (max_bits > 8 ? BC_RGBA16161616 : BC_RGBA8888) :
+                       (max_bits > 8 ? BC_RGB161616 : BC_RGB888)) ;
+       }
+       VFrame vframe(ip->width, ip->height, imodel);
        if( convert_picture_vframe(&vframe, ip) ) return -1;
        frame->transfer_from(&vframe);
        return 1;
@@ -1011,7 +1027,7 @@ int FFVideoConvert::convert_vframe_picture(VFrame *frame, AVFrame *op, AVFrame *
 
        AVPixelFormat ofmt = (AVPixelFormat)op->format;
        convert_ctx = sws_getCachedContext(convert_ctx, frame->get_w(), frame->get_h(),
-               ifmt, op->width, op->height, ofmt, SWS_BICUBIC, NULL, NULL, NULL);
+               ifmt, op->width, op->height, ofmt, SWS_POINT, NULL, NULL, NULL);
        if( !convert_ctx ) {
                fprintf(stderr, "FFVideoConvert::convert_frame_picture:"
                                " sws_getCachedContext() failed\n");
@@ -1031,13 +1047,23 @@ int FFVideoConvert::convert_pixfmt(VFrame *frame, AVFrame *op)
        // try direct transfer
        if( !convert_vframe_picture(frame, op) ) return 1;
        // use indirect transfer
-       int colormodel = frame->get_color_model();
-       int bits = BC_CModels::calculate_pixelsize(colormodel) * 8;
-       bits /= BC_CModels::components(colormodel);
-       int icolor_model =  BC_CModels::has_alpha(colormodel) ?
-               (bits > 8 ? BC_RGBA16161616 : BC_RGBA8888) :
-               (bits > 8 ? BC_RGB161616: BC_RGB888) ;
-       VFrame vframe(frame->get_w(), frame->get_h(), icolor_model);
+       int cmodel = frame->get_color_model();
+       int max_bits = BC_CModels::calculate_pixelsize(cmodel) * 8;
+       max_bits /= BC_CModels::components(cmodel);
+       AVPixelFormat ofmt = (AVPixelFormat)op->format;
+       int imodel = pix_fmt_to_color_model(ofmt);
+       int imodel_is_yuv = BC_CModels::is_yuv(imodel);
+       int cmodel_is_yuv = BC_CModels::is_yuv(cmodel);
+       if( imodel < 0 || imodel_is_yuv != cmodel_is_yuv ) {
+               imodel = cmodel_is_yuv ?
+                   (BC_CModels::has_alpha(cmodel) ?
+                       BC_AYUV16161616 :
+                       (max_bits > 8 ? BC_AYUV16161616 : BC_YUV444P)) :
+                   (BC_CModels::has_alpha(cmodel) ?
+                       (max_bits > 8 ? BC_RGBA16161616 : BC_RGBA8888) :
+                       (max_bits > 8 ? BC_RGB161616 : BC_RGB888)) ;
+       }
+       VFrame vframe(frame->get_w(), frame->get_h(), imodel);
        vframe.transfer_from(frame);
        if( !convert_vframe_picture(&vframe, op) ) return 1;
        return -1;
@@ -1153,7 +1179,7 @@ AVRational FFMPEG::to_sample_aspect_ratio(Asset *asset)
        int width = 1000000, height = width * sample_aspect + 0.5;
        float w, h;
        MWindow::create_aspect_ratio(w, h, width, height);
-       return (AVRational){(int)h, (int)w};
+       return (AVRational){(int)w, (int)h};
 #else
 // square pixels
        return (AVRational){1, 1};
@@ -1226,20 +1252,38 @@ int FFMPEG::get_codec(char *codec, const char *path, const char *spec)
 
 int FFMPEG::get_file_format()
 {
-       int ret = 0;
+       char audio_muxer[BCSTRLEN], video_muxer[BCSTRLEN];
        char audio_format[BCSTRLEN], video_format[BCSTRLEN];
-       file_format[0] = audio_format[0] = video_format[0] = 0;
+       audio_muxer[0] = audio_format[0] = 0;
+       video_muxer[0] = video_format[0] = 0;
        Asset *asset = file_base->asset;
-       if( !ret && asset->audio_data )
-               ret = get_format(audio_format, "audio", asset->acodec);
-       if( !ret && asset->video_data )
-               ret = get_format(video_format, "video", asset->vcodec);
-       if( !ret && !audio_format[0] && !video_format[0] )
+       int ret = asset ? 0 : 1;
+       if( !ret && asset->audio_data ) {
+               if( !(ret=get_format(audio_format, "audio", asset->acodec)) ) {
+                       if( get_format(audio_muxer, "format", audio_format) ) {
+                               strcpy(audio_muxer, audio_format);
+                               audio_format[0] = 0;
+                       }
+               }
+       }
+       if( !ret && asset->video_data ) {
+               if( !(ret=get_format(video_format, "video", asset->vcodec)) ) {
+                       if( get_format(video_muxer, "format", video_format) ) {
+                               strcpy(video_muxer, video_format);
+                               video_format[0] = 0;
+                       }
+               }
+       }
+       if( !ret && !audio_muxer[0] && !video_muxer[0] )
                ret = 1;
+       if( !ret && audio_muxer[0] && video_muxer[0] &&
+           strcmp(audio_muxer, video_muxer) ) ret = -1;
        if( !ret && audio_format[0] && video_format[0] &&
            strcmp(audio_format, video_format) ) ret = -1;
        if( !ret )
-               strcpy(file_format, audio_format[0] ? audio_format : video_format);
+               strcpy(file_format, !audio_format[0] && !video_format[0] ?
+                       (audio_muxer[0] ? audio_muxer : video_muxer) :
+                       (audio_format[0] ? audio_format : video_format));
        return ret;
 }
 
@@ -1247,7 +1291,7 @@ int FFMPEG::scan_option_line(char *cp, char *tag, char *val)
 {
        while( *cp == ' ' || *cp == '\t' ) ++cp;
        char *bp = cp;
-       while( *cp && *cp != ' ' && *cp != '\t' && *cp != '=' ) ++cp;
+       while( *cp && *cp != ' ' && *cp != '\t' && *cp != '=' && *cp != '\n' ) ++cp;
        int len = cp - bp;
        if( !len || len > BCSTRLEN-1 ) return 1;
        while( bp < cp ) *tag++ = *bp++;
@@ -1268,7 +1312,7 @@ int FFMPEG::load_defaults(const char *path, const char *type,
                 char *codec, char *codec_options, int len)
 {
        char default_file[BCTEXTLEN];
-       FFMPEG::set_option_path(default_file, "%s/%s.dfl", path, type);
+       set_option_path(default_file, "%s/%s.dfl", path, type);
        FILE *fp = fopen(default_file,"r");
        if( !fp ) return 1;
        fgets(codec, BCSTRLEN, fp);
@@ -1280,14 +1324,15 @@ int FFMPEG::load_defaults(const char *path, const char *type,
                codec_options += n;  len -= n;
        }
        fclose(fp);
-       FFMPEG::set_option_path(default_file, "%s/%s", path, codec);
-       return FFMPEG::load_options(default_file, codec_options, len);
+       set_option_path(default_file, "%s/%s", path, codec);
+       return load_options(default_file, codec_options, len);
 }
 
 void FFMPEG::set_asset_format(Asset *asset, const char *text)
 {
        if( asset->format != FILE_FFMPEG ) return;
-       strcpy(asset->fformat, text);
+       if( text != asset->fformat )
+               strcpy(asset->fformat, text);
        if( !asset->ff_audio_options[0] ) {
                asset->audio_data = !load_defaults("audio", text, asset->acodec,
                        asset->ff_audio_options, sizeof(asset->ff_audio_options));
@@ -1328,11 +1373,18 @@ int FFMPEG::get_encoder(FILE *fp,
        return 0;
 }
 
-int FFMPEG::read_options(const char *options, AVDictionary *&opts)
+int FFMPEG::read_options(const char *options, AVDictionary *&opts, int skip)
 {
        FILE *fp = fopen(options,"r");
        if( !fp ) return 1;
-       int ret = read_options(fp, options, opts);
+       int ret = 0;
+       while( !ret && --skip >= 0 ) {
+               int ch = getc(fp);
+               while( ch >= 0 && ch != '\n' ) ch = getc(fp);
+               if( ch < 0 ) ret = 1;
+       }
+       if( !ret )
+               ret = read_options(fp, options, opts);
        fclose(fp);
        return ret;
 }
@@ -1354,7 +1406,6 @@ int FFMPEG::read_options(FILE *fp, const char *options, AVDictionary *&opts)
        char line[BCTEXTLEN];
        while( !ret && fgets(line, sizeof(line), fp) ) {
                line[sizeof(line)-1] = 0;
-               ++no;
                if( line[0] == '#' ) continue;
                if( line[0] == '\n' ) continue;
                char key[BCSTRLEN], val[BCTEXTLEN];
@@ -1659,7 +1710,10 @@ int FFMPEG::init_encoder(const char *filename)
        }
        ff_lock("FFMPEG::init_encoder");
        av_register_all();
-       avformat_alloc_output_context2(&fmt_ctx, 0, file_format, filename);
+       char format[BCSTRLEN];
+       if( get_format(format, "format", file_format) )
+               strcpy(format, file_format);
+       avformat_alloc_output_context2(&fmt_ctx, 0, format, filename);
        if( !fmt_ctx ) {
                eprintf(_("failed: %s\n"), filename);
                ret = 1;
@@ -1783,7 +1837,7 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                                sprintf(arg, "%d", asset->ff_video_bitrate);
                                av_dict_set(&sopts, "b", arg, 0);
                        }
-                       else if( asset->ff_video_quality > 0 ) {
+                       else if( asset->ff_video_quality >= 0 ) {
                                ctx->global_quality = asset->ff_video_quality * FF_QP2LAMBDA;
                                ctx->qmin    = ctx->qmax =  asset->ff_video_quality;
                                ctx->mb_lmin = ctx->qmin * FF_QP2LAMBDA;
@@ -1817,6 +1871,9 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                        ctx->time_base = (AVRational) { frame_rate.den, frame_rate.num };
                        st->time_base = ctx->time_base;
                        vid->writing = -1;
+                       vid->interlaced = asset->interlace_mode == ILACE_MODE_TOP_FIRST ||
+                               asset->interlace_mode == ILACE_MODE_BOTTOM_FIRST ? 1 : 0;
+                       vid->top_field_first = asset->interlace_mode == ILACE_MODE_TOP_FIRST ? 1 : 0;
                        break; }
                default:
                        eprintf(_("not audio/video, %s:%s\n"), codec_name, filename);
@@ -1827,6 +1884,9 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                if( fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER )
                        st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
 
+               av_dict_set(&sopts, "cin_bitrate", 0, 0);
+               av_dict_set(&sopts, "cin_quality", 0, 0);
+
                ret = avcodec_open2(st->codec, codec, &sopts);
                if( ret < 0 ) {
                        ff_err(ret,"FFMPEG::open_encoder");
@@ -1870,30 +1930,33 @@ int FFMPEG::decode_activate()
                for( int aidx=0; aidx<ffaudio.size(); ++aidx )
                        ffaudio[aidx]->nudge = AV_NOPTS_VALUE;
                // set nudges for each program stream set
+               const int64_t min_nudge = INT64_MIN+1;
                int npgrms = fmt_ctx->nb_programs;
                for( int i=0; i<npgrms; ++i ) {
                        AVProgram *pgrm = fmt_ctx->programs[i];
                        // first start time video stream
-                       int64_t vstart_time = -1, astart_time = -1;
+                       int64_t vstart_time = min_nudge, astart_time = min_nudge;
                        for( int j=0; j<(int)pgrm->nb_stream_indexes; ++j ) {
                                int fidx = pgrm->stream_index[j];
                                AVStream *st = fmt_ctx->streams[fidx];
                                AVCodecContext *avctx = st->codec;
                                if( avctx->codec_type == AVMEDIA_TYPE_VIDEO ) {
                                        if( st->start_time == AV_NOPTS_VALUE ) continue;
-                                       if( vstart_time > st->start_time ) continue;
-                                       vstart_time = st->start_time;
+                                       if( vstart_time < st->start_time )
+                                               vstart_time = st->start_time;
                                        continue;
                                }
-                               if( avctx->codec_type == AVMEDIA_TYPE_VIDEO ) {
+                               if( avctx->codec_type == AVMEDIA_TYPE_AUDIO ) {
                                        if( st->start_time == AV_NOPTS_VALUE ) continue;
-                                       if( astart_time > st->start_time ) continue;
-                                       astart_time = st->start_time;
+                                       if( astart_time < st->start_time )
+                                               astart_time = st->start_time;
                                        continue;
                                }
                        }
-                       // match program streams to max start_time
-                       int64_t nudge = vstart_time > astart_time ? vstart_time : astart_time;
+                       //since frame rate is much more grainy than sample rate, it is better to
+                       // align using video, so that total absolute error is minimized.
+                       int64_t nudge = vstart_time > min_nudge ? vstart_time :
+                               astart_time > min_nudge ? astart_time : AV_NOPTS_VALUE;
                        for( int j=0; j<(int)pgrm->nb_stream_indexes; ++j ) {
                                int fidx = pgrm->stream_index[j];
                                AVStream *st = fmt_ctx->streams[fidx];
@@ -1915,7 +1978,7 @@ int FFMPEG::decode_activate()
                        }
                }
                // set nudges for any streams not yet set
-               int64_t vstart_time = 0, astart_time = 0;
+               int64_t vstart_time = min_nudge, astart_time = min_nudge;
                int nstreams = fmt_ctx->nb_streams;
                for( int i=0; i<nstreams; ++i ) {
                        AVStream *st = fmt_ctx->streams[i];
@@ -1926,28 +1989,29 @@ int FFMPEG::decode_activate()
                                int vidx = ffvideo.size();
                                while( --vidx >= 0 && ffvideo[vidx]->fidx != i );
                                if( vidx >= 0 && ffvideo[vidx]->nudge != AV_NOPTS_VALUE ) continue;
-                               if( vstart_time >= st->start_time ) continue;
-                               vstart_time = st->start_time;
+                               if( vstart_time < st->start_time )
+                                       vstart_time = st->start_time;
                                break; }
                        case AVMEDIA_TYPE_AUDIO: {
                                if( st->start_time == AV_NOPTS_VALUE ) continue;
                                int aidx = ffaudio.size();
                                while( --aidx >= 0 && ffaudio[aidx]->fidx != i );
                                if( aidx >= 0 && ffaudio[aidx]->nudge != AV_NOPTS_VALUE ) continue;
-                               if( astart_time >= st->start_time ) continue;
-                               astart_time = st->start_time;
+                               if( astart_time < st->start_time )
+                                       astart_time = st->start_time;
                                break; }
                        default: break;
                        }
                }
-               int64_t nudge = vstart_time > astart_time ? vstart_time : astart_time;
+               int64_t nudge = vstart_time > min_nudge ? vstart_time :
+                       astart_time > min_nudge ? astart_time : AV_NOPTS_VALUE;
                for( int vidx=0; vidx<ffvideo.size(); ++vidx ) {
-                       if( ffvideo[vidx]->nudge != AV_NOPTS_VALUE ) continue;
-                       ffvideo[vidx]->nudge = nudge;
+                       if( ffvideo[vidx]->nudge == AV_NOPTS_VALUE )
+                               ffvideo[vidx]->nudge = nudge;
                }
                for( int aidx=0; aidx<ffaudio.size(); ++aidx ) {
-                       if( ffaudio[aidx]->nudge != AV_NOPTS_VALUE ) continue;
-                       ffaudio[aidx]->nudge = nudge;
+                       if( ffaudio[aidx]->nudge == AV_NOPTS_VALUE )
+                               ffaudio[aidx]->nudge = nudge;
                }
                decoding = 1;
        }
@@ -1966,17 +2030,52 @@ int FFMPEG::encode_activate()
                        return -1;
                }
 
+               int prog_id = 1;
+               AVProgram *prog = av_new_program(fmt_ctx, prog_id);
+               for( int i=0; i< ffvideo.size(); ++i )
+                       av_program_add_stream_index(fmt_ctx, prog_id, ffvideo[i]->fidx);
+               for( int i=0; i< ffaudio.size(); ++i )
+                       av_program_add_stream_index(fmt_ctx, prog_id, ffaudio[i]->fidx);
+               int pi = fmt_ctx->nb_programs;
+               while(  --pi >= 0 && fmt_ctx->programs[pi]->id != prog_id );
+               AVDictionary **meta = &prog->metadata;
+               av_dict_set(meta, "service_provider", "cin5", 0);
+               const char *path = fmt_ctx->filename, *bp = strrchr(path,'/');
+               if( bp ) path = bp + 1;
+               av_dict_set(meta, "title", path, 0);
+
+               if( ffaudio.size() ) {
+                       const char *ep = getenv("CIN_AUDIO_LANG"), *lp = 0;
+                       if( !ep && (lp=getenv("LANG")) ) { // some are guesses
+                               static struct { const char lc[3], lng[4]; } lcode[] = {
+                                       { "en", "eng" }, { "de", "ger" }, { "es", "spa" },
+                                       { "eu", "bas" }, { "fr", "fre" }, { "el", "gre" },
+                                       { "hi", "hin" }, { "it", "ita" }, { "ja", "jap" },
+                                       { "ko", "kor" }, { "du", "dut" }, { "pl", "pol" },
+                                       { "pt", "por" }, { "ru", "rus" }, { "sl", "slv" },
+                                       { "uk", "ukr" }, { "vi", "vie" }, { "zh", "chi" },
+                               };
+                               for( int i=sizeof(lcode)/sizeof(lcode[0]); --i>=0 && !ep; )
+                                       if( !strncmp(lcode[i].lc,lp,2) ) ep = lcode[i].lng;
+                       }
+                       if( !ep ) ep = "und";
+                       char lang[5];
+                       strncpy(lang,ep,3);  lang[3] = 0;
+                       AVStream *st = ffaudio[0]->st;
+                       av_dict_set(&st->metadata,"language",lang,0);
+               }
+
                AVDictionary *fopts = 0;
                char option_path[BCTEXTLEN];
                set_option_path(option_path, "format/%s", file_format);
-               read_options(option_path, fopts);
+               read_options(option_path, fopts, 1);
                ret = avformat_write_header(fmt_ctx, &fopts);
-               av_dict_free(&fopts);
                if( ret < 0 ) {
                        ff_err(ret, "FFMPEG::encode_activate: write header failed %s\n",
                                fmt_ctx->filename);
                        return -1;
                }
+               av_dict_free(&fopts);
                encoding = 1;
        }
        return encoding;
@@ -2498,6 +2597,23 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled)
                }
                av_dict_free(&copts);
        }
+
+       decode_activate();
+       for( int i=0; i<(int)fmt_ctx->nb_streams; ++i ) {
+               AVStream *st = fmt_ctx->streams[i];
+               AVCodecContext *avctx = st->codec;
+               if( avctx->codec_type != AVMEDIA_TYPE_AUDIO ) continue;
+               int64_t tstmp = st->start_time;
+               if( tstmp == AV_NOPTS_VALUE ) continue;
+               int aidx = ffaudio.size();
+               while( --aidx>=0 && ffaudio[aidx]->fidx != i );
+               if( aidx < 0 ) continue;
+               FFAudioStream *aud = ffaudio[aidx];
+               tstmp -= aud->nudge;
+               double secs = to_secs(tstmp, st->time_base);
+               aud->curr_pos = secs * aud->sample_rate + 0.5;
+       }
+
        int errs = 0;
        for( int64_t count=0; !*canceled; ++count ) {
                av_packet_unref(&pkt);
@@ -2557,8 +2673,8 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled)
                                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);
+                               if( sample >= 0 )
+                                       index_state->put_audio_mark(aidx, sample, pkt.pos);
                        }
                        while( pkt.size > 0 ) {
                                int ch = aud->channel0,  nch = aud->channels;
@@ -2576,9 +2692,15 @@ printf("audio%d pad %jd %jd (%jd)\n", aud->idx, pos, aud->curr_pos, pos-aud->cur
                                        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;
+                                       pos = aud->curr_pos;
+                                       if( (aud->curr_pos += len) >= 0 ) {
+                                               if( pos < 0 ) {
+                                                       samples += -pos * nch;
+                                                       len = aud->curr_pos;
+                                               }
+                                               for( int i=0; i<nch; ++i )
+                                                       index_state->put_data(ch+i,nch,samples+i,len);
+                                       }
                                }
                                pkt.data += ret;
                                pkt.size -= ret;