prev/next label fix in viewer, inout highlight, modify folder layout, ffmpeg scan...
[goodguy/history.git] / cinelerra-5.1 / cinelerra / ffmpeg.C
index 10ad05036ee64b71a5cb8d7e2336434fe2e52108..2845788dc626178e0663e6dc7ddeb36625af0c9e 100644 (file)
@@ -7,6 +7,8 @@
 #include <stdarg.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <ctype.h>
+
 // work arounds (centos)
 #include <lzma.h>
 #ifndef INT64_MAX
 #include "asset.h"
 #include "bccmodels.h"
 #include "bchash.h"
-#include "fileffmpeg.h"
+#include "edl.h"
+#include "edlsession.h"
 #include "file.h"
+#include "fileffmpeg.h"
+#include "filesystem.h"
 #include "ffmpeg.h"
 #include "indexfile.h"
 #include "interlacemodes.h"
 #include "mwindow.h"
 #include "vframe.h"
 
+#ifdef FFMPEG3
+#define url filename
+#else
+#define av_register_all(s)
+#define avfilter_register_all(s)
+#endif
 
 #define VIDEO_INBUF_SIZE 0x10000
 #define AUDIO_INBUF_SIZE 0x10000
 #define VIDEO_REFILL_THRESH 0
 #define AUDIO_REFILL_THRESH 0x1000
+#define AUDIO_MIN_FRAME_SZ 128
 
 Mutex FFMPEG::fflock("FFMPEG::fflock");
 
@@ -148,10 +160,13 @@ void FFAudioStream::reset_history()
 {
        inp = outp = bfr;
        hpos = 0;
+       memset(bfr, 0, lmt-bfr);
 }
 
 void FFAudioStream::iseek(int64_t ofs)
 {
+       if( ofs > hpos ) ofs = hpos;
+       if( ofs > sz ) ofs = sz;
        outp = inp - ofs*nch;
        if( outp < bfr ) outp += sz*nch;
 }
@@ -247,6 +262,10 @@ FFStream::FFStream(FFMPEG *ffmpeg, AVStream *st, int fidx)
        need_packet = 1;
        frame = fframe = 0;
        bsfc = 0;
+       stats_fp = 0;
+       stats_filename = 0;
+       stats_in = 0;
+       pass = 0;
 }
 
 FFStream::~FFStream()
@@ -260,6 +279,9 @@ FFStream::~FFStream()
        if( frame ) av_frame_free(&frame);
        if( fframe ) av_frame_free(&fframe);
        delete frm_lock;
+       if( stats_fp ) fclose(stats_fp);
+       if( stats_in ) av_freep(&stats_in);
+       delete [] stats_filename;
 }
 
 void FFStream::ff_lock(const char *cp)
@@ -305,7 +327,8 @@ int FFStream::decode_activate()
                av_dict_copy(&copts, ffmpeg->opts, 0);
                int ret = 0;
                // this should be avformat_copy_context(), but no copy avail
-               ret = avformat_open_input(&fmt_ctx, ffmpeg->fmt_ctx->filename, NULL, &copts);
+               ret = avformat_open_input(&fmt_ctx,
+                       ffmpeg->fmt_ctx->url, ffmpeg->fmt_ctx->iformat, &copts);
                if( ret >= 0 ) {
                        ret = avformat_find_stream_info(fmt_ctx, 0);
                        st = fmt_ctx->streams[fidx];
@@ -321,10 +344,13 @@ int FFStream::decode_activate()
                        }
                        if( ret >= 0 ) {
                                avcodec_parameters_to_context(avctx, st->codecpar);
+                               if( !av_dict_get(copts, "threads", NULL, 0) )
+                                       avctx->thread_count = ffmpeg->ff_cpus();
                                ret = avcodec_open2(avctx, decoder, &copts);
                        }
-                       if( ret >= 0 )
+                       if( ret >= 0 ) {
                                reading = 1;
+                       }
                        else
                                eprintf(_("open decoder failed\n"));
                }
@@ -388,12 +414,9 @@ int FFStream::decode(AVFrame *frame)
 
 int FFStream::load_filter(AVFrame *frame)
 {
-       int ret = av_buffersrc_add_frame_flags(buffersrc_ctx,
-                       frame, AV_BUFFERSRC_FLAG_KEEP_REF);
-       if( ret < 0 ) {
-               av_frame_unref(frame);
+       int ret = av_buffersrc_add_frame_flags(buffersrc_ctx, frame, 0);
+       if( ret < 0 )
                eprintf(_("av_buffersrc_add_frame_flags failed\n"));
-       }
        return ret;
 }
 
@@ -411,6 +434,7 @@ int FFStream::read_filter(AVFrame *frame)
 
 int FFStream::read_frame(AVFrame *frame)
 {
+       av_frame_unref(frame);
        if( !filter_graph || !buffersrc_ctx || !buffersink_ctx )
                return decode(frame);
        if( !fframe && !(fframe=av_frame_alloc()) ) {
@@ -467,6 +491,10 @@ int FFStream::encode_frame(AVFrame *frame)
                ret = write_packet(opkt);
                if( ret < 0 ) break;
                ++pkts;
+               if( frame && stats_fp ) {
+                       ret = write_stats_file();
+                       if( ret < 0 ) break;
+               }
        }
        ff_err(ret, "FFStream::encode_frame: encode failed\n");
        return -1;
@@ -477,14 +505,77 @@ int FFStream::flush()
        if( writing < 0 )
                return -1;
        int ret = encode_frame(0);
+       if( ret >= 0 && stats_fp ) {
+               ret = write_stats_file();
+               close_stats_file();
+       }
        if( ret < 0 )
                ff_err(ret, "FFStream::flush");
        return ret >= 0 ? 0 : 1;
 }
 
+
+int FFStream::open_stats_file()
+{
+       stats_fp = fopen(stats_filename,"w");
+       return stats_fp ? 0 : AVERROR(errno);
+}
+
+int FFStream::close_stats_file()
+{
+       if( stats_fp ) {
+               fclose(stats_fp);  stats_fp = 0;
+       }
+       return 0;
+}
+
+int FFStream::read_stats_file()
+{
+       int64_t len = 0;  struct stat stats_st;
+       int fd = open(stats_filename, O_RDONLY);
+       int ret = fd >= 0 ? 0: ENOENT;
+       if( !ret && fstat(fd, &stats_st) )
+               ret = EINVAL;
+       if( !ret ) {
+               len = stats_st.st_size;
+               stats_in = (char *)av_malloc(len+1);
+               if( !stats_in )
+                       ret = ENOMEM;
+       }
+       if( !ret && read(fd, stats_in, len+1) != len )
+               ret = EIO;
+       if( !ret ) {
+               stats_in[len] = 0;
+               avctx->stats_in = stats_in;
+       }
+       if( fd >= 0 )
+               close(fd);
+       return !ret ? 0 : AVERROR(ret);
+}
+
+int FFStream::write_stats_file()
+{
+       int ret = 0;
+       if( avctx->stats_out && (ret=strlen(avctx->stats_out)) > 0 ) {
+               int len = fwrite(avctx->stats_out, 1, ret, stats_fp);
+               if( ret != len )
+                       ff_err(ret = AVERROR(errno), "FFStream::write_stats_file");
+       }
+       return ret;
+}
+
+int FFStream::init_stats_file()
+{
+       int ret = 0;
+       if( (pass & 2) && (ret = read_stats_file()) < 0 )
+               ff_err(ret, "stat file read: %s", stats_filename);
+       if( (pass & 1) && (ret=open_stats_file()) < 0 )
+               ff_err(ret, "stat file open: %s", stats_filename);
+       return ret >= 0 ? 0 : ret;
+}
+
 int FFStream::seek(int64_t no, double rate)
 {
-       int64_t tstmp = -INT64_MAX+1;
 // default ffmpeg native seek
        int npkts = 1;
        int64_t pos = no, pkt_pos = -1;
@@ -501,11 +592,25 @@ int FFStream::seek(int64_t no, double rate)
                        npkts = MAX_RETRY;
                }
        }
-       if( pos > 0 && st->time_base.num > 0 ) {
-               double secs = pos / rate;
-               tstmp = secs * st->time_base.den / st->time_base.num;
-               if( nudge != AV_NOPTS_VALUE ) tstmp += nudge;
-       }
+       if( pos == curr_pos ) return 0;
+       double secs = pos < 0 ? 0. : pos / rate;
+       AVRational time_base = st->time_base;
+       int64_t tstmp = time_base.num > 0 ? secs * time_base.den/time_base.num : 0;
+       if( !tstmp ) {
+               if( st->nb_index_entries > 0 ) tstmp = st->index_entries[0].timestamp;
+               else if( st->start_time != AV_NOPTS_VALUE ) tstmp = st->start_time;
+               else if( st->first_dts != AV_NOPTS_VALUE ) tstmp = st->first_dts;
+               else tstmp = INT64_MIN+1;
+       }
+       else if( nudge != AV_NOPTS_VALUE ) tstmp += nudge;
+       int idx = st->index;
+#if 0
+// seek all streams using the default timebase.
+//   this is how ffmpeg and ffplay work.  stream seeks are less tested.
+       tstmp = av_rescale_q(tstmp, time_base, AV_TIME_BASE_Q);
+       idx = -1;
+#endif
+
        avcodec_flush_buffers(avctx);
        avformat_flush(fmt_ctx);
 #if 0
@@ -515,9 +620,11 @@ int FFStream::seek(int64_t no, double rate)
                seek = pkt_pos;
                flags = AVSEEK_FLAG_BYTE;
        }
-        int ret = avformat_seek_file(fmt_ctx, st->index, -INT64_MAX, seek, INT64_MAX, flags);
+       int ret = avformat_seek_file(fmt_ctx, st->index, -INT64_MAX, seek, INT64_MAX, flags);
 #else
-        int ret = av_seek_frame(fmt_ctx, st->index, tstmp, AVSEEK_FLAG_ANY);
+// finds the first index frame below the target time
+       int flags = AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY;
+       int ret = av_seek_frame(fmt_ctx, idx, tstmp, flags);
 #endif
        int retry = MAX_RETRY;
        while( ret >= 0 ) {
@@ -549,9 +656,9 @@ int FFStream::seek(int64_t no, double rate)
                }
        }
        if( ret < 0 ) {
-printf("** seek fail %ld, %ld\n", pos, tstmp);
+printf("** seek fail %jd, %jd\n", pos, tstmp);
                seeked = need_packet = 0;
-               st_eof(flushed=1);
+               st_eof(flushed=1);
                return -1;
        }
 //printf("seeked pos = %ld, %ld\n", pos, tstmp);
@@ -566,8 +673,10 @@ FFAudioStream::FFAudioStream(FFMPEG *ffmpeg, AVStream *strm, int idx, int fidx)
        channel0 = channels = 0;
        sample_rate = 0;
        mbsz = 0;
+       frame_sz = AUDIO_MIN_FRAME_SZ;
        length = 0;
        resample_context = 0;
+       swr_ichs = swr_ifmt = swr_irate = 0;
 
        aud_bfr_sz = 0;
        aud_bfr = 0;
@@ -588,6 +697,28 @@ FFAudioStream::~FFAudioStream()
        delete [] bfr;
 }
 
+void FFAudioStream::init_swr(int ichs, int ifmt, int irate)
+{
+       if( resample_context ) {
+               if( swr_ichs == ichs && swr_ifmt == ifmt && swr_irate == irate )
+                       return;
+               swr_free(&resample_context);
+       }
+       swr_ichs = ichs;  swr_ifmt = ifmt;  swr_irate = irate;
+       if( ichs == channels && ifmt == AV_SAMPLE_FMT_FLT && irate == sample_rate )
+               return;
+       uint64_t ilayout = av_get_default_channel_layout(ichs);
+       if( !ilayout ) ilayout = ((uint64_t)1<<ichs) - 1;
+       uint64_t olayout = av_get_default_channel_layout(channels);
+       if( !olayout ) olayout = ((uint64_t)1<<channels) - 1;
+       resample_context = swr_alloc_set_opts(NULL,
+               olayout, AV_SAMPLE_FMT_FLT, sample_rate,
+               ilayout, (AVSampleFormat)ifmt, irate,
+               0, NULL);
+       if( resample_context )
+               swr_init(resample_context);
+}
+
 int FFAudioStream::get_samples(float *&samples, uint8_t **data, int len)
 {
        samples = *(float **)data;
@@ -634,7 +765,7 @@ int FFAudioStream::decode_frame(AVFrame *frame)
                ff_err(ret, "FFAudioStream::decode_frame: Could not read audio frame\n");
                return -1;
        }
-       int64_t pkt_ts = av_frame_get_best_effort_timestamp(frame);
+       int64_t pkt_ts = frame->best_effort_timestamp;
        if( pkt_ts != AV_NOPTS_VALUE )
                curr_pos = ffmpeg->to_secs(pkt_ts - nudge, st->time_base) * sample_rate + 0.5;
        return 1;
@@ -643,7 +774,8 @@ int FFAudioStream::decode_frame(AVFrame *frame)
 int FFAudioStream::encode_activate()
 {
        if( writing >= 0 ) return writing;
-       frame_sz = avctx->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE ?
+       if( !avctx->codec ) return writing = 0;
+       frame_sz = avctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE ?
                10000 : avctx->frame_size;
        return FFStream::encode_activate();
 }
@@ -687,10 +819,11 @@ int FFAudioStream::load(int64_t pos, int len)
        }
        if( mbsz < len ) mbsz = len;
        int64_t end_pos = pos + len;
-       int ret = 0;
-       for( int i=0; ret>=0 && !flushed && curr_pos<end_pos && i<MAX_RETRY; ++i ) {
+       int ret = 0, i = len / frame_sz + MAX_RETRY;
+       while( ret>=0 && !flushed && curr_pos<end_pos && --i>=0 ) {
                ret = read_frame(frame);
-               if( ret > 0 ) {
+               if( ret > 0 && frame->nb_samples > 0 ) {
+                       init_swr(frame->channels, frame->format, frame->sample_rate);
                        load_history(&frame->extended_data[0], frame->nb_samples);
                        curr_pos += frame->nb_samples;
                }
@@ -706,7 +839,7 @@ int FFAudioStream::load(int64_t pos, int len)
 
 int FFAudioStream::audio_seek(int64_t pos)
 {
-       if( decode_activate() < 0 ) return -1;
+       if( decode_activate() <= 0 ) return -1;
        if( !st->codecpar ) return -1;
        if( in_history(pos) ) return 0;
        if( pos == curr_pos ) return 0;
@@ -759,6 +892,11 @@ int FFAudioStream::encode_frame(AVFrame *frame)
        return FFStream::encode_frame(frame);
 }
 
+int FFAudioStream::write_packet(FFPacket &pkt)
+{
+       return FFStream::write_packet(pkt);
+}
+
 void FFAudioStream::load_markers()
 {
        IndexState *index_state = ffmpeg->file_base->asset->index_state;
@@ -801,7 +939,7 @@ int FFVideoStream::decode_frame(AVFrame *frame)
                ff_err(ret, "FFVideoStream::decode_frame: Could not read video frame\n");
                return -1;
        }
-       int64_t pkt_ts = av_frame_get_best_effort_timestamp(frame);
+       int64_t pkt_ts = frame->best_effort_timestamp;
        if( pkt_ts != AV_NOPTS_VALUE )
                curr_pos = ffmpeg->to_secs(pkt_ts - nudge, st->time_base) * frame_rate + 0.5;
        return 1;
@@ -815,7 +953,8 @@ int FFVideoStream::load(VFrame *vframe, int64_t pos)
                fprintf(stderr, "FFVideoStream::load: av_frame_alloc failed\n");
                return -1;
        }
-       for( int i=0; ret>=0 && !flushed && curr_pos<=pos && i<MAX_RETRY; ++i ) {
+       int i = MAX_RETRY + pos - curr_pos;
+       while( ret>=0 && !flushed && curr_pos<=pos && --i>=0 ) {
                ret = read_frame(frame);
                if( ret > 0 ) ++curr_pos;
        }
@@ -830,7 +969,7 @@ int FFVideoStream::load(VFrame *vframe, int64_t pos)
 
 int FFVideoStream::video_seek(int64_t pos)
 {
-       if( decode_activate() < 0 ) return -1;
+       if( decode_activate() <= 0 ) return -1;
        if( !st->codecpar ) return -1;
        if( pos == curr_pos-1 && !seeked ) return 0;
 // if close enough, just read up to current
@@ -889,6 +1028,13 @@ int FFVideoStream::encode_frame(AVFrame *frame)
        return FFStream::encode_frame(frame);
 }
 
+int FFVideoStream::write_packet(FFPacket &pkt)
+{
+       if( !(ffmpeg->fmt_ctx->oformat->flags & AVFMT_VARIABLE_FPS) )
+               pkt->duration = 1;
+       return FFStream::write_packet(pkt);
+}
+
 AVPixelFormat FFVideoConvert::color_model_to_pix_fmt(int color_model)
 {
        switch( color_model ) {
@@ -908,6 +1054,7 @@ AVPixelFormat FFVideoConvert::color_model_to_pix_fmt(int color_model)
        case BC_RGB161616:      return AV_PIX_FMT_RGB48LE;
        case BC_RGBA16161616:   return AV_PIX_FMT_RGBA64LE;
        case BC_AYUV16161616:   return AV_PIX_FMT_AYUV64LE;
+       case BC_GBRP:           return AV_PIX_FMT_GBRP;
        default: break;
        }
 
@@ -933,6 +1080,7 @@ int FFVideoConvert::pix_fmt_to_color_model(AVPixelFormat pix_fmt)
        case AV_PIX_FMT_RGB48LE:        return BC_RGB161616;
        case AV_PIX_FMT_RGBA64LE:       return BC_RGBA16161616;
        case AV_PIX_FMT_AYUV64LE:       return BC_AYUV16161616;
+       case AV_PIX_FMT_GBRP:           return BC_GBRP;
        default: break;
        }
 
@@ -1029,7 +1177,7 @@ int FFVideoConvert::transfer_cmodel(VFrame *frame, AVFrame *ifp)
 {
        int ret = convert_cmodel(frame, ifp);
        if( ret > 0 ) {
-               const AVDictionary *src = av_frame_get_metadata(ifp);
+               const AVDictionary *src = ifp->metadata;
                AVDictionaryEntry *t = NULL;
                BC_Hash *hp = frame->get_params();
                //hp->clear();
@@ -1126,7 +1274,7 @@ int FFVideoConvert::transfer_pixfmt(VFrame *frame, AVFrame *ofp)
        int ret = convert_pixfmt(frame, ofp);
        if( ret > 0 ) {
                BC_Hash *hp = frame->get_params();
-               AVDictionary **dict = avpriv_frame_get_metadatap(ofp);
+               AVDictionary **dict = &ofp->metadata;
                //av_dict_free(dict);
                for( int i=0; i<hp->size(); ++i ) {
                        char *key = hp->get_key(i), *val = hp->get_value(i);
@@ -1243,6 +1391,37 @@ AVRational FFMPEG::to_time_base(int sample_rate)
        return (AVRational){1, sample_rate};
 }
 
+int FFMPEG::get_fmt_score(AVSampleFormat dst_fmt, AVSampleFormat src_fmt)
+{
+       int score = 0;
+       int dst_planar = av_sample_fmt_is_planar(dst_fmt);
+       int src_planar = av_sample_fmt_is_planar(src_fmt);
+       if( dst_planar != src_planar ) ++score;
+       int dst_bytes = av_get_bytes_per_sample(dst_fmt);
+       int src_bytes = av_get_bytes_per_sample(src_fmt);
+       score += (src_bytes > dst_bytes ? 100 : -10) * (src_bytes - dst_bytes);
+       int src_packed = av_get_packed_sample_fmt(src_fmt);
+       int dst_packed = av_get_packed_sample_fmt(dst_fmt);
+       if( dst_packed == AV_SAMPLE_FMT_S32 && src_packed == AV_SAMPLE_FMT_FLT ) score += 20;
+       if( dst_packed == AV_SAMPLE_FMT_FLT && src_packed == AV_SAMPLE_FMT_S32 ) score += 2;
+       return score;
+}
+
+AVSampleFormat FFMPEG::find_best_sample_fmt_of_list(
+               const AVSampleFormat *sample_fmts, AVSampleFormat src_fmt)
+{
+       AVSampleFormat best = AV_SAMPLE_FMT_NONE;
+       int best_score = get_fmt_score(best, src_fmt);
+       for( int i=0; sample_fmts[i] >= 0; ++i ) {
+               AVSampleFormat sample_fmt = sample_fmts[i];
+               int score = get_fmt_score(sample_fmt, src_fmt);
+               if( score >= best_score ) continue;
+               best = sample_fmt;  best_score = score;
+       }
+       return best;
+}
+
+
 void FFMPEG::set_option_path(char *path, const char *fmt, ...)
 {
        char *ep = path + BCTEXTLEN-1;
@@ -1339,10 +1518,10 @@ int FFMPEG::get_file_format()
        return ret;
 }
 
-int FFMPEG::scan_option_line(char *cp, char *tag, char *val)
+int FFMPEG::scan_option_line(const char *cp, char *tag, char *val)
 {
        while( *cp == ' ' || *cp == '\t' ) ++cp;
-       char *bp = cp;
+       const char *bp = cp;
        while( *cp && *cp != ' ' && *cp != '\t' && *cp != '=' && *cp != '\n' ) ++cp;
        int len = cp - bp;
        if( !len || len > BCSTRLEN-1 ) return 1;
@@ -1360,6 +1539,123 @@ int FFMPEG::scan_option_line(char *cp, char *tag, char *val)
        return 0;
 }
 
+int FFMPEG::can_render(const char *fformat, const char *type)
+{
+       FileSystem fs;
+       char option_path[BCTEXTLEN];
+       FFMPEG::set_option_path(option_path, type);
+       fs.update(option_path);
+       int total_files = fs.total_files();
+       for( int i=0; i<total_files; ++i ) {
+               const char *name = fs.get_entry(i)->get_name();
+               const char *ext = strrchr(name,'.');
+               if( !ext ) continue;
+               if( !strcmp(fformat, ++ext) ) return 1;
+       }
+       return 0;
+}
+
+int FFMPEG::get_ff_option(const char *nm, const char *options, char *value)
+{
+        for( const char *cp=options; *cp!=0; ) {
+                char line[BCTEXTLEN], *bp = line, *ep = bp+sizeof(line)-1;
+                while( bp < ep && *cp && *cp!='\n' ) *bp++ = *cp++;
+                if( *cp ) ++cp;
+                *bp = 0;
+                if( !line[0] || line[0] == '#' || line[0] == ';' ) continue;
+                char key[BCSTRLEN], val[BCTEXTLEN];
+                if( FFMPEG::scan_option_line(line, key, val) ) continue;
+                if( !strcmp(key, nm) ) {
+                        strncpy(value, val, BCSTRLEN);
+                        return 0;
+                }
+        }
+        return 1;
+}
+
+void FFMPEG::scan_audio_options(Asset *asset, EDL *edl)
+{
+       char cin_sample_fmt[BCSTRLEN];
+       int cin_fmt = AV_SAMPLE_FMT_NONE;
+       const char *options = asset->ff_audio_options;
+       if( !get_ff_option("cin_sample_fmt", options, cin_sample_fmt) )
+               cin_fmt = (int)av_get_sample_fmt(cin_sample_fmt);
+       if( cin_fmt < 0 ) {
+               char audio_codec[BCSTRLEN]; audio_codec[0] = 0;
+               AVCodec *av_codec = !FFMPEG::get_codec(audio_codec, "audio", asset->acodec) ?
+                       avcodec_find_encoder_by_name(audio_codec) : 0;
+               if( av_codec && av_codec->sample_fmts )
+                       cin_fmt = find_best_sample_fmt_of_list(av_codec->sample_fmts, AV_SAMPLE_FMT_FLT);
+       }
+       if( cin_fmt < 0 ) cin_fmt = AV_SAMPLE_FMT_S16;
+       const char *name = av_get_sample_fmt_name((AVSampleFormat)cin_fmt);
+       if( !name ) name = _("None");
+       strcpy(asset->ff_sample_format, name);
+
+       char value[BCSTRLEN];
+       if( !get_ff_option("cin_bitrate", options, value) )
+               asset->ff_audio_bitrate = atoi(value);
+       if( !get_ff_option("cin_quality", options, value) )
+               asset->ff_audio_quality = atoi(value);
+}
+
+void FFMPEG::load_audio_options(Asset *asset, EDL *edl)
+{
+       char options_path[BCTEXTLEN];
+       set_option_path(options_path, "audio/%s", asset->acodec);
+        if( !load_options(options_path,
+                       asset->ff_audio_options,
+                       sizeof(asset->ff_audio_options)) )
+               scan_audio_options(asset, edl);
+}
+
+void FFMPEG::scan_video_options(Asset *asset, EDL *edl)
+{
+       char cin_pix_fmt[BCSTRLEN];
+       int cin_fmt = AV_PIX_FMT_NONE;
+       const char *options = asset->ff_video_options;
+       if( !get_ff_option("cin_pix_fmt", options, cin_pix_fmt) )
+                       cin_fmt = (int)av_get_pix_fmt(cin_pix_fmt);
+       if( cin_fmt < 0 ) {
+               char video_codec[BCSTRLEN];  video_codec[0] = 0;
+               AVCodec *av_codec = !get_codec(video_codec, "video", asset->vcodec) ?
+                       avcodec_find_encoder_by_name(video_codec) : 0;
+               if( av_codec && av_codec->pix_fmts ) {
+                       if( 0 && edl ) { // frequently picks a bad answer
+                               int color_model = edl->session->color_model;
+                               int max_bits = BC_CModels::calculate_pixelsize(color_model) * 8;
+                               max_bits /= BC_CModels::components(color_model);
+                               cin_fmt = avcodec_find_best_pix_fmt_of_list(av_codec->pix_fmts,
+                                       (BC_CModels::is_yuv(color_model) ?
+                                               (max_bits > 8 ? AV_PIX_FMT_AYUV64LE : AV_PIX_FMT_YUV444P) :
+                                               (max_bits > 8 ? AV_PIX_FMT_RGB48LE : AV_PIX_FMT_RGB24)), 0, 0);
+                       }
+                       else
+                               cin_fmt = av_codec->pix_fmts[0];
+               }
+       }
+       if( cin_fmt < 0 ) cin_fmt = AV_PIX_FMT_YUV420P;
+       const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get((AVPixelFormat)cin_fmt);
+       const char *name = desc ? desc->name : _("None");
+       strcpy(asset->ff_pixel_format, name);
+
+       char value[BCSTRLEN];
+       if( !get_ff_option("cin_bitrate", options, value) )
+               asset->ff_video_bitrate = atoi(value);
+       if( !get_ff_option("cin_quality", options, value) )
+               asset->ff_video_quality = atoi(value);
+}
+
+void FFMPEG::load_video_options(Asset *asset, EDL *edl)
+{
+       char options_path[BCTEXTLEN];
+       set_option_path(options_path, "video/%s", asset->vcodec);
+        if( !load_options(options_path,
+                       asset->ff_video_options,
+                       sizeof(asset->ff_video_options)) )
+               scan_video_options(asset, edl);
+}
+
 int FFMPEG::load_defaults(const char *path, const char *type,
                 char *codec, char *codec_options, int len)
 {
@@ -1380,18 +1676,24 @@ int FFMPEG::load_defaults(const char *path, const char *type,
        return load_options(default_file, codec_options, len);
 }
 
-void FFMPEG::set_asset_format(Asset *asset, const char *text)
+void FFMPEG::set_asset_format(Asset *asset, EDL *edl, const char *text)
 {
        if( asset->format != FILE_FFMPEG ) return;
        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));
+       if( asset->audio_data && !asset->ff_audio_options[0] ) {
+               if( !load_defaults("audio", text, asset->acodec,
+                               asset->ff_audio_options, sizeof(asset->ff_audio_options)) )
+                       scan_audio_options(asset, edl);
+               else
+                       asset->audio_data = 0;
        }
-       if( !asset->ff_video_options[0] ) {
-               asset->video_data = !load_defaults("video", text, asset->vcodec,
-                       asset->ff_video_options, sizeof(asset->ff_video_options));
+       if( asset->video_data && !asset->ff_video_options[0] ) {
+               if( !load_defaults("video", text, asset->vcodec,
+                               asset->ff_video_options, sizeof(asset->ff_video_options)) )
+                       scan_video_options(asset, edl);
+               else
+                       asset->video_data = 0;
        }
 }
 
@@ -1403,19 +1705,18 @@ int FFMPEG::get_encoder(const char *options,
                eprintf(_("options open failed %s\n"),options);
                return 1;
        }
-       if( get_encoder(fp, format, codec, bsfilter) )
+       char line[BCTEXTLEN];
+       if( !fgets(line, sizeof(line), fp) ||
+           scan_encoder(line, format, codec, bsfilter) )
                eprintf(_("format/codec not found %s\n"), options);
        fclose(fp);
        return 0;
 }
 
-int FFMPEG::get_encoder(FILE *fp,
+int FFMPEG::scan_encoder(const char *line,
                char *format, char *codec, char *bsfilter)
 {
        format[0] = codec[0] = bsfilter[0] = 0;
-       char line[BCTEXTLEN];
-       if( !fgets(line, sizeof(line), fp) ) return 1;
-       line[sizeof(line)-1] = 0;
        if( scan_option_line(line, format, codec) ) return 1;
        char *cp = codec;
        while( *cp && *cp != '|' ) ++cp;
@@ -1634,7 +1935,9 @@ int FFMPEG::init_decoder(const char *filename)
        char file_opts[BCTEXTLEN];
        char *bp = strrchr(strcpy(file_opts, filename), '/');
        char *sp = strrchr(!bp ? file_opts : bp, '.');
+       if( !sp ) sp = bp + strlen(bp);
        FILE *fp = 0;
+       AVInputFormat *ifmt = 0;
        if( sp ) {
                strcpy(sp, ".opts");
                fp = fopen(file_opts, "r");
@@ -1642,12 +1945,16 @@ int FFMPEG::init_decoder(const char *filename)
        if( fp ) {
                read_options(fp, file_opts, opts);
                fclose(fp);
+               AVDictionaryEntry *tag;
+               if( (tag=av_dict_get(opts, "format", NULL, 0)) != 0 ) {
+                       ifmt = av_find_input_format(tag->value);
+               }
        }
        else
                load_options("decode.opts", opts);
        AVDictionary *fopts = 0;
        av_dict_copy(&fopts, opts, 0);
-       int ret = avformat_open_input(&fmt_ctx, filename, NULL, &fopts);
+       int ret = avformat_open_input(&fmt_ctx, filename, ifmt, &fopts);
        av_dict_free(&fopts);
        if( ret >= 0 )
                ret = avformat_find_stream_info(fmt_ctx, NULL);
@@ -1661,8 +1968,8 @@ int FFMPEG::init_decoder(const char *filename)
 int FFMPEG::open_decoder()
 {
        struct stat st;
-       if( stat(fmt_ctx->filename, &st) < 0 ) {
-               eprintf(_("can't stat file: %s\n"), fmt_ctx->filename);
+       if( stat(fmt_ctx->url, &st) < 0 ) {
+               eprintf(_("can't stat file: %s\n"), fmt_ctx->url);
                return 1;
        }
 
@@ -1681,8 +1988,11 @@ int FFMPEG::open_decoder()
                        estimated = 1;
                }
        }
-       if( estimated )
+       static int notified = 0;
+       if( !notified && estimated ) {
+               notified = 1;
                printf("FFMPEG::open_decoder: some stream times estimated\n");
+       }
 
        ff_lock("FFMPEG::open_decoder");
        int ret = 0, bad_time = 0;
@@ -1728,16 +2038,7 @@ int FFMPEG::open_decoder()
                        aud->sample_rate = avpar->sample_rate;
                        double secs = to_secs(st->duration, st->time_base);
                        aud->length = secs * aud->sample_rate;
-                       if( avpar->format != AV_SAMPLE_FMT_FLT ) {
-                               uint64_t layout = av_get_default_channel_layout(avpar->channels);
-                               if( !layout ) layout = ((uint64_t)1<<aud->channels) - 1;
-                               AVSampleFormat sample_format = (AVSampleFormat)avpar->format;
-                               aud->resample_context = swr_alloc_set_opts(NULL,
-                                       layout, AV_SAMPLE_FMT_FLT, avpar->sample_rate,
-                                       layout, sample_format, avpar->sample_rate,
-                                       0, NULL);
-                               swr_init(aud->resample_context);
-                       }
+                       aud->init_swr(aud->channels, avpar->format, aud->sample_rate);
                        aud->nudge = st->start_time;
                        aud->reading = -1;
                        if( opt_audio_filter )
@@ -1755,14 +2056,18 @@ int FFMPEG::open_decoder()
 
 int FFMPEG::init_encoder(const char *filename)
 {
-       int fd = ::open(filename,O_WRONLY);
-       if( fd < 0 ) fd = open(filename,O_WRONLY+O_CREAT,0666);
-       if( fd < 0 ) {
+// try access first for named pipes
+       int ret = access(filename, W_OK);
+       if( ret ) {
+               int fd = ::open(filename,O_WRONLY);
+               if( fd < 0 ) fd = open(filename,O_WRONLY+O_CREAT,0666);
+               if( fd >= 0 ) { close(fd);  ret = 0; }
+       }
+       if( ret ) {
                eprintf(_("bad file path: %s\n"), filename);
                return 1;
        }
-       ::close(fd);
-       int ret = get_file_format();
+       ret = get_file_format();
        if( ret > 0 ) {
                eprintf(_("bad file format: %s\n"), filename);
                return 1;
@@ -1806,7 +2111,9 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                return 1;
        }
 
+#ifdef HAVE_DV
        if( !strcmp(codec_name, CODEC_TAG_DVSD) ) strcpy(codec_name, "dv");
+#endif
        else if( !strcmp(codec_name, CODEC_TAG_MJPEG) ) strcpy(codec_name, "mjpeg");
        else if( !strcmp(codec_name, CODEC_TAG_JPEG) ) strcpy(codec_name, "jpeg");
 
@@ -1857,6 +2164,19 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                                sprintf(arg, "%d", asset->ff_audio_bitrate);
                                av_dict_set(&sopts, "b", arg, 0);
                        }
+                       else if( asset->ff_audio_quality >= 0 ) {
+                               ctx->global_quality = asset->ff_audio_quality * FF_QP2LAMBDA;
+                               ctx->qmin    = ctx->qmax =  asset->ff_audio_quality;
+                               ctx->mb_lmin = ctx->qmin * FF_QP2LAMBDA;
+                               ctx->mb_lmax = ctx->qmax * FF_QP2LAMBDA;
+                               ctx->flags |= AV_CODEC_FLAG_QSCALE;
+                               char arg[BCSTRLEN];
+                               av_dict_set(&sopts, "flags", "+qscale", 0);
+                               sprintf(arg, "%d", asset->ff_audio_quality);
+                               av_dict_set(&sopts, "qscale", arg, 0);
+                               sprintf(arg, "%d", ctx->global_quality);
+                               av_dict_set(&sopts, "global_quality", arg, 0);
+                       }
                        int aidx = ffaudio.size();
                        int fidx = aidx + ffvideo.size();
                        FFAudioStream *aud = new FFAudioStream(this, st, aidx, fidx);
@@ -1873,7 +2193,10 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                                break;
                        }
                        ctx->time_base = st->time_base = (AVRational){1, aud->sample_rate};
-                       ctx->sample_fmt = codec->sample_fmts[0];
+                       AVSampleFormat sample_fmt = av_get_sample_fmt(asset->ff_sample_format);
+                       if( sample_fmt == AV_SAMPLE_FMT_NONE )
+                               sample_fmt = codec->sample_fmts ? codec->sample_fmts[0] : AV_SAMPLE_FMT_S16;
+                       ctx->sample_fmt = sample_fmt;
                        uint64_t layout = av_get_default_channel_layout(ctx->channels);
                        aud->resample_context = swr_alloc_set_opts(NULL,
                                layout, ctx->sample_fmt, aud->sample_rate,
@@ -1906,7 +2229,7 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                                ctx->qmin    = ctx->qmax =  asset->ff_video_quality;
                                ctx->mb_lmin = ctx->qmin * FF_QP2LAMBDA;
                                ctx->mb_lmax = ctx->qmax * FF_QP2LAMBDA;
-                               ctx->flags |= CODEC_FLAG_QSCALE;
+                               ctx->flags |= AV_CODEC_FLAG_QSCALE;
                                char arg[BCSTRLEN];
                                av_dict_set(&sopts, "flags", "+qscale", 0);
                                sprintf(arg, "%d", asset->ff_video_quality);
@@ -1920,19 +2243,30 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                        vstrm_index.append(ffidx(vidx, 0));
                        vid->avctx = ctx;  ffvideo.append(vid);  fst = vid;
                        vid->width = asset->width;
-                       ctx->width = (vid->width+3) & ~3;
                        vid->height = asset->height;
-                       ctx->height = (vid->height+3) & ~3;
                        vid->frame_rate = asset->frame_rate;
+
+                       AVPixelFormat pix_fmt = av_get_pix_fmt(asset->ff_pixel_format);
+                       if( pix_fmt == AV_PIX_FMT_NONE )
+                               pix_fmt = codec->pix_fmts ? codec->pix_fmts[0] : AV_PIX_FMT_YUV420P;
+                       ctx->pix_fmt = pix_fmt;
+                       const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
+                       int mask_w = (1<<desc->log2_chroma_w)-1;
+                       ctx->width = (vid->width+mask_w) & ~mask_w;
+                       int mask_h = (1<<desc->log2_chroma_h)-1;
+                       ctx->height = (vid->height+mask_h) & ~mask_h;
                        ctx->sample_aspect_ratio = to_sample_aspect_ratio(asset);
-                       ctx->pix_fmt = codec->pix_fmts ? codec->pix_fmts[0] : AV_PIX_FMT_YUV420P;
                        AVRational frame_rate = check_frame_rate(codec, vid->frame_rate);
                        if( !frame_rate.num || !frame_rate.den ) {
                                eprintf(_("check_frame_rate failed %s\n"), filename);
                                ret = 1;
                                break;
                        }
+                       av_reduce(&frame_rate.num, &frame_rate.den,
+                               frame_rate.num, frame_rate.den, INT_MAX);
+                       ctx->framerate = (AVRational) { frame_rate.num, frame_rate.den };
                        ctx->time_base = (AVRational) { frame_rate.den, frame_rate.num };
+                       st->avg_frame_rate = frame_rate;
                        st->time_base = ctx->time_base;
                        vid->writing = -1;
                        vid->interlaced = asset->interlace_mode == ILACE_MODE_TOP_FIRST ||
@@ -1943,20 +2277,62 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                        eprintf(_("not audio/video, %s:%s\n"), codec_name, filename);
                        ret = 1;
                }
+
+               if( ctx ) {
+                       AVDictionaryEntry *tag;
+                       if( (tag=av_dict_get(sopts, "cin_stats_filename", NULL, 0)) != 0 ) {
+                               char suffix[BCSTRLEN];  sprintf(suffix,"-%d.log",fst->fidx);
+                               fst->stats_filename = cstrcat(2, tag->value, suffix);
+                       }
+                       if( (tag=av_dict_get(sopts, "flags", NULL, 0)) != 0 ) {
+                               int pass = fst->pass;
+                               char *cp = tag->value;
+                               while( *cp ) {
+                                       int ch = *cp++, pfx = ch=='-' ? -1 : ch=='+' ? 1 : 0;
+                                       if( !isalnum(!pfx ? ch : (ch=*cp++)) ) continue;
+                                       char id[BCSTRLEN], *bp = id, *ep = bp+sizeof(id)-1;
+                                       for( *bp++=ch; isalnum(ch=*cp); ++cp )
+                                               if( bp < ep ) *bp++ = ch;
+                                       *bp = 0;
+                                       if( !strcmp(id, "pass1") ) {
+                                               pass = pfx<0 ? (pass&~1) : pfx>0 ? (pass|1) : 1;
+                                       }
+                                       else if( !strcmp(id, "pass2") ) {
+                                               pass = pfx<0 ? (pass&~2) : pfx>0 ? (pass|2) : 2;
+                                       }
+                               }
+                               if( (fst->pass=pass) ) {
+                                       if( pass & 1 ) ctx->flags |= AV_CODEC_FLAG_PASS1;
+                                       if( pass & 2 ) ctx->flags |= AV_CODEC_FLAG_PASS2;
+                               }
+                       }
+               }
        }
        if( !ret ) {
                if( fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER )
-                       ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
-
+                       ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
+               if( fst->stats_filename && (ret=fst->init_stats_file()) )
+                       eprintf(_("error: stats file = %s\n"), fst->stats_filename);
+       }
+       if( !ret ) {
                av_dict_set(&sopts, "cin_bitrate", 0, 0);
                av_dict_set(&sopts, "cin_quality", 0, 0);
 
+               if( !av_dict_get(sopts, "threads", NULL, 0) )
+                       ctx->thread_count = ff_cpus();
                ret = avcodec_open2(ctx, codec, &sopts);
                if( ret >= 0 ) {
                        ret = avcodec_parameters_from_context(st->codecpar, ctx);
                        if( ret < 0 )
                                fprintf(stderr, "Could not copy the stream parameters\n");
                }
+               if( ret >= 0 ) {
+_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+                       ret = avcodec_copy_context(st->codec, ctx);
+_Pragma("GCC diagnostic warning \"-Wdeprecated-declarations\"")
+                       if( ret < 0 )
+                               fprintf(stderr, "Could not copy the stream context\n");
+               }
                if( ret < 0 ) {
                        ff_err(ret,"FFMPEG::open_encoder");
                        eprintf(_("open failed %s:%s\n"), codec_name, filename);
@@ -2063,7 +2439,8 @@ int FFMPEG::decode_activate()
                                if( st->start_time == AV_NOPTS_VALUE ) continue;
                                int vidx = ffvideo.size();
                                while( --vidx >= 0 && ffvideo[vidx]->fidx != i );
-                               if( vidx >= 0 && ffvideo[vidx]->nudge != AV_NOPTS_VALUE ) continue;
+                               if( vidx < 0 ) continue;
+                               if( ffvideo[vidx]->nudge != AV_NOPTS_VALUE ) continue;
                                if( vstart_time < st->start_time )
                                        vstart_time = st->start_time;
                                break; }
@@ -2071,7 +2448,10 @@ int FFMPEG::decode_activate()
                                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( aidx < 0 ) continue;
+                               if( ffaudio[aidx]->frame_sz < avpar->frame_size )
+                                       ffaudio[aidx]->frame_sz = avpar->frame_size;
+                               if( ffaudio[aidx]->nudge != AV_NOPTS_VALUE ) continue;
                                if( astart_time < st->start_time )
                                        astart_time = st->start_time;
                                break; }
@@ -2079,7 +2459,7 @@ int FFMPEG::decode_activate()
                        }
                }
                int64_t nudge = vstart_time > min_nudge ? vstart_time :
-                       astart_time > min_nudge ? astart_time : AV_NOPTS_VALUE;
+                       astart_time > min_nudge ? astart_time : 0;
                for( int vidx=0; vidx<ffvideo.size(); ++vidx ) {
                        if( ffvideo[vidx]->nudge == AV_NOPTS_VALUE )
                                ffvideo[vidx]->nudge = nudge;
@@ -2099,9 +2479,9 @@ int FFMPEG::encode_activate()
        if( encoding < 0 ) {
                encoding = 0;
                if( !(fmt_ctx->flags & AVFMT_NOFILE) &&
-                   (ret=avio_open(&fmt_ctx->pb, fmt_ctx->filename, AVIO_FLAG_WRITE)) < 0 ) {
+                   (ret=avio_open(&fmt_ctx->pb, fmt_ctx->url, AVIO_FLAG_WRITE)) < 0 ) {
                        ff_err(ret, "FFMPEG::encode_activate: err opening : %s\n",
-                               fmt_ctx->filename);
+                               fmt_ctx->url);
                        return -1;
                }
 
@@ -2115,7 +2495,7 @@ int FFMPEG::encode_activate()
                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,'/');
+               const char *path = fmt_ctx->url, *bp = strrchr(path,'/');
                if( bp ) path = bp + 1;
                av_dict_set(meta, "title", path, 0);
 
@@ -2147,7 +2527,7 @@ int FFMPEG::encode_activate()
                ret = avformat_write_header(fmt_ctx, &fopts);
                if( ret < 0 ) {
                        ff_err(ret, "FFMPEG::encode_activate: write header failed %s\n",
-                               fmt_ctx->filename);
+                               fmt_ctx->url);
                        return -1;
                }
                av_dict_free(&fopts);
@@ -2474,6 +2854,10 @@ int FFMPEG::ff_video_pid(int stream)
        return ffvideo[stream]->st->id;
 }
 
+int FFMPEG::ff_video_mpeg_color_range(int stream)
+{
+       return ffvideo[stream]->st->codecpar->color_range == AVCOL_RANGE_MPEG ? 1 : 0;
+}
 
 int FFMPEG::ff_cpus()
 {
@@ -2488,14 +2872,14 @@ int FFVideoStream::create_filter(const char *filter_spec, AVCodecParameters *avp
        int i = sizeof(filter_name);
        while( --i>=0 && *sp!=0 && !strchr(" \t:=,",*sp) ) *np++ = *sp++;
        *np = 0;
-       AVFilter *filter = !filter_name[0] ? 0 : avfilter_get_by_name(filter_name);
+       const AVFilter *filter = !filter_name[0] ? 0 : avfilter_get_by_name(filter_name);
        if( !filter || avfilter_pad_get_type(filter->inputs,0) != AVMEDIA_TYPE_VIDEO ) {
                ff_err(AVERROR(EINVAL), "FFVideoStream::create_filter: %s\n", filter_spec);
                return -1;
        }
        filter_graph = avfilter_graph_alloc();
-       AVFilter *buffersrc = avfilter_get_by_name("buffer");
-       AVFilter *buffersink = avfilter_get_by_name("buffersink");
+       const AVFilter *buffersrc = avfilter_get_by_name("buffer");
+       const AVFilter *buffersink = avfilter_get_by_name("buffersink");
 
        int ret = 0;  char args[BCTEXTLEN];
        AVPixelFormat pix_fmt = (AVPixelFormat)avpar->format;
@@ -2529,14 +2913,14 @@ int FFAudioStream::create_filter(const char *filter_spec, AVCodecParameters *avp
        int i = sizeof(filter_name);
        while( --i>=0 && *sp!=0 && !strchr(" \t:=,",*sp) ) *np++ = *sp++;
        *np = 0;
-       AVFilter *filter = !filter_name[0] ? 0 : avfilter_get_by_name(filter_name);
+       const AVFilter *filter = !filter_name[0] ? 0 : avfilter_get_by_name(filter_name);
        if( !filter || avfilter_pad_get_type(filter->inputs,0) != AVMEDIA_TYPE_AUDIO ) {
                ff_err(AVERROR(EINVAL), "FFAudioStream::create_filter: %s\n", filter_spec);
                return -1;
        }
        filter_graph = avfilter_graph_alloc();
-       AVFilter *buffersrc = avfilter_get_by_name("abuffer");
-       AVFilter *buffersink = avfilter_get_by_name("abuffersink");
+       const AVFilter *buffersrc = avfilter_get_by_name("abuffer");
+       const AVFilter *buffersink = avfilter_get_by_name("abuffersink");
        int ret = 0;  char args[BCTEXTLEN];
        AVSampleFormat sample_fmt = (AVSampleFormat)avpar->format;
        snprintf(args, sizeof(args),
@@ -2608,6 +2992,7 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled)
        if( !frame ) {
                fprintf(stderr,"FFMPEG::scan: ");
                fprintf(stderr,_("av_frame_alloc failed\n"));
+               fprintf(stderr,"FFMPEG::scan:file=%s\n", file_base->asset->path);
                return -1;
        }
 
@@ -2628,30 +3013,33 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled)
                }
                if( ret >= 0 ) {
                        avcodec_parameters_to_context(avctx, st->codecpar);
+                       if( !av_dict_get(copts, "threads", NULL, 0) )
+                               avctx->thread_count = ff_cpus();
                        ret = avcodec_open2(avctx, decoder, &copts);
                }
                av_dict_free(&copts);
-               if( ret < 0 ) {
-                       fprintf(stderr,"FFMPEG::scan: ");
-                       fprintf(stderr,_("codec open failed\n"));
-                       continue;
-               }
-               AVCodecParameters *avpar = st->codecpar;
-               switch( avpar->codec_type ) {
-               case AVMEDIA_TYPE_VIDEO: {
-                       int vidx = ffvideo.size();
-                       while( --vidx>=0 && ffvideo[vidx]->fidx != i );
-                       if( vidx < 0 ) break;
-                       ffvideo[vidx]->avctx = avctx;
-                       break; }
-               case AVMEDIA_TYPE_AUDIO: {
-                       int aidx = ffaudio.size();
-                       while( --aidx>=0 && ffaudio[aidx]->fidx != i );
-                       if( aidx < 0 ) continue;
-                       ffaudio[aidx]->avctx = avctx;
-                       break; }
-               default: break;
+               if( ret >= 0 ) {
+                       AVCodecParameters *avpar = st->codecpar;
+                       switch( avpar->codec_type ) {
+                       case AVMEDIA_TYPE_VIDEO: {
+                               int vidx = ffvideo.size();
+                               while( --vidx>=0 && ffvideo[vidx]->fidx != i );
+                               if( vidx < 0 ) break;
+                               ffvideo[vidx]->avctx = avctx;
+                               continue; }
+                       case AVMEDIA_TYPE_AUDIO: {
+                               int aidx = ffaudio.size();
+                               while( --aidx>=0 && ffaudio[aidx]->fidx != i );
+                               if( aidx < 0 ) break;
+                               ffaudio[aidx]->avctx = avctx;
+                               continue; }
+                       default: break;
+                       }
                }
+               fprintf(stderr,"FFMPEG::scan: ");
+               fprintf(stderr,_("codec open failed\n"));
+               fprintf(stderr,"FFMPEG::scan:file=%s\n", file_base->asset->path);
+               avcodec_free_context(&avctx);
        }
 
        decode_activate();
@@ -2697,6 +3085,7 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled)
                        while( --vidx>=0 && ffvideo[vidx]->fidx != i );
                        if( vidx < 0 ) break;
                        FFVideoStream *vid = ffvideo[vidx];
+                       if( !vid->avctx ) break;
                        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 ) {
@@ -2717,6 +3106,7 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled)
                        while( --aidx>=0 && ffaudio[aidx]->fidx != i );
                        if( aidx < 0 ) break;
                        FFAudioStream *aud = ffaudio[aidx];
+                       if( !aud->avctx ) break;
                        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 ) {
@@ -2736,7 +3126,8 @@ printf("audio%d pad %jd %jd (%jd)\n", aud->idx, pos, aud->curr_pos, pos-aud->cur
                                index_state->pad_data(ch, nch, aud->curr_pos);
                        }
                        while( (ret=aud->decode_frame(frame)) > 0 ) {
-                               if( frame->channels != nch ) break;
+                               //if( frame->channels != nch ) break;
+                               aud->init_swr(frame->channels, frame->format, frame->sample_rate);
                                float *samples;
                                int len = aud->get_samples(samples,
                                         &frame->extended_data[0], frame->nb_samples);