remove old quicktime, replaced with current ffmpeg
[goodguy/history.git] / cinelerra-5.0 / cinelerra / ffmpeg.C
index 26ed21a44d23649e147aeae1e5e58ea8dcce67a7..b1272467a1c1e04cab8432b214ee143cc3e3f303 100644 (file)
@@ -18,6 +18,8 @@
 #include "fileffmpeg.h"
 #include "file.h"
 #include "ffmpeg.h"
+#include "libdv.h"
+#include "libmjpeg.h"
 #include "mainerror.h"
 #include "mwindow.h"
 #include "vframe.h"
 
 Mutex FFMPEG::fflock("FFMPEG::fflock");
 
-static void ff_err(int ret, const char *msg)
+static void ff_err(int ret, const char *fmt, ...)
 {
-       char errmsg[BCSTRLEN];  av_strerror(ret, errmsg, sizeof(errmsg));
-       fprintf(stderr,"%s: %s\n",msg, errmsg);
+       char msg[BCTEXTLEN];
+       va_list ap;
+       va_start(ap, fmt);
+       vsnprintf(msg, sizeof(msg), fmt, ap);
+       va_end(ap);
+       char errmsg[BCSTRLEN];
+       av_strerror(ret, errmsg, sizeof(errmsg));
+       fprintf(stderr,_("%s  err: %s\n"),msg, errmsg);
 }
 
 FFPacket::FFPacket()
@@ -321,7 +329,7 @@ int FFStream::read_packet()
        if( ret >= 0 ) return 1;
        st_eof(1);
        if( ret == AVERROR_EOF ) return 0;
-       fprintf(stderr, "FFStream::read_packet: av_read_frame failed\n");
+       ff_err(ret, "FFStream::read_packet: av_read_frame failed\n");
        flushed = 1;
        return -1;
 }
@@ -381,7 +389,7 @@ int FFStream::read_filter(AVFrame *frame)
        if( ret < 0 ) {
                if( ret == AVERROR(EAGAIN) ) return 0;
                if( ret == AVERROR_EOF ) { st_eof(1); return -1; }
-               fprintf(stderr, "FFStream::read_filter: av_buffersink_get_frame failed\n");
+               ff_err(ret, "FFStream::read_filter: av_buffersink_get_frame failed\n");
                return ret;
        }
        return 1;
@@ -403,6 +411,29 @@ int FFStream::read_frame(AVFrame *frame)
        return ret;
 }
 
+int FFStream::write_packet(FFPacket &pkt)
+{
+       bs_filter(pkt);
+       av_packet_rescale_ts(pkt, st->codec->time_base, st->time_base);
+       pkt->stream_index = st->index;
+       return av_interleaved_write_frame(ffmpeg->fmt_ctx, pkt);
+}
+
+int FFStream::flush()
+{
+       int ret = 0;
+       while( ret >= 0 ) {
+               FFPacket pkt;
+               int got_packet = 0;
+               ret = encode_frame(pkt, 0, got_packet);
+               if( ret < 0 || !got_packet ) break;
+               ret = write_packet(pkt);
+       }
+       if( ret < 0 )
+               ff_err(ret, "FFStream::flush");
+       return ret >= 0 ? 0 : 1;
+}
+
 FFAudioStream::FFAudioStream(FFMPEG *ffmpeg, AVStream *strm, int idx)
  : FFStream(ffmpeg, strm, idx)
 {
@@ -447,7 +478,7 @@ int FFAudioStream::load_history(uint8_t **data, int len)
                int ret = swr_convert(resample_context,
                        (uint8_t**)&aud_bfr, aud_bfr_sz, (const uint8_t**)data, len);
                if( ret < 0 ) {
-                       fprintf(stderr, "FFAudioStream::load_history: swr_convert failed\n");
+                       ff_err(ret, "FFAudioStream::load_history: swr_convert failed\n");
                        return -1;
                }
                samples = aud_bfr;
@@ -463,7 +494,7 @@ int FFAudioStream::decode_frame(AVFrame *frame, int &got_frame)
 {
        int ret = avcodec_decode_audio4(st->codec, frame, &got_frame, ipkt);
        if( ret < 0 ) {
-               fprintf(stderr, "FFAudioStream::decode_frame: Could not read audio frame\n");
+               ff_err(ret, "FFAudioStream::decode_frame: Could not read audio frame\n");
                return -1;
        }
        return ret;
@@ -512,7 +543,7 @@ int FFAudioStream::init_frame(AVFrame *frame)
        frame->sample_rate = ctx->sample_rate;
        int ret = av_frame_get_buffer(frame, 0);
        if (ret < 0)
-               fprintf(stderr, "FFAudioStream::init_frame: av_frame_get_buffer failed\n");
+               ff_err(ret, "FFAudioStream::init_frame: av_frame_get_buffer failed\n");
        return ret;
 }
 
@@ -533,7 +564,7 @@ int FFAudioStream::load(int64_t pos, int len)
                        curr_pos += frame->nb_samples;
                }
        }
-       if( flushed && end_pos > curr_pos ) {
+       if( end_pos > curr_pos ) {
                zero(end_pos - curr_pos);
                curr_pos = end_pos;
        }
@@ -548,14 +579,15 @@ int FFAudioStream::audio_seek(int64_t pos)
                return 0;
        }
        if( pos == curr_pos ) return 0;
+       if( !st->codec || !st->codec->codec ) return -1;
+       avcodec_flush_buffers(st->codec);
        double secs = (double)pos / sample_rate;
        int64_t tstmp = secs * st->time_base.den / st->time_base.num;
        if( nudge != AV_NOPTS_VALUE ) tstmp += nudge;
        avformat_seek_file(fmt_ctx, st->index, -INT64_MAX, tstmp, INT64_MAX, 0);
        seek_pos = curr_pos = pos;
-       mbsz = 0;
-       reset();
-       st_eof(0);
+       reset();  st_eof(0);
+       mbsz = 0; flushed = 0;  need_packet = 1;
        return 1;
 }
 
@@ -576,7 +608,7 @@ int FFAudioStream::encode(double **samples, int len)
                        (uint8_t **)frame->extended_data, frame_sz,
                        (const uint8_t **)&bfrp, frame_sz);
                if( ret < 0 ) {
-                       fprintf(stderr, "FFAudioStream::encode: swr_convert failed\n");
+                       ff_err(ret, "FFAudioStream::encode: swr_convert failed\n");
                        break;
                }
                frm->queue(curr_pos);
@@ -589,6 +621,16 @@ int FFAudioStream::encode(double **samples, int len)
        return ret >= 0 ? 0 : 1;
 }
 
+int FFAudioStream::encode_frame(FFPacket &pkt, AVFrame *frame, int &got_packet)
+{
+       int ret = avcodec_encode_audio2(st->codec, pkt, frame, &got_packet);
+       if( ret < 0 ) {
+               ff_err(ret, "FFAudioStream::encode_frame: encode audio failed\n");
+               return -1;
+       }
+       return ret;
+}
+
 FFVideoStream::FFVideoStream(FFMPEG *ffmpeg, AVStream *strm, int idx)
  : FFStream(ffmpeg, strm, idx)
 {
@@ -609,7 +651,7 @@ int FFVideoStream::decode_frame(AVFrame *frame, int &got_frame)
 {
        int ret = avcodec_decode_video2(st->codec, frame, &got_frame, ipkt);
        if( ret < 0 ) {
-               fprintf(stderr, "FFVideoStream::decode_frame: Could not read video frame\n");
+               ff_err(ret, "FFVideoStream::decode_frame: Could not read video frame\n");
                return -1;
        }
        if( got_frame )
@@ -619,12 +661,12 @@ int FFVideoStream::decode_frame(AVFrame *frame, int &got_frame)
 
 int FFVideoStream::load(VFrame *vframe, int64_t pos)
 {
-       if( video_seek(pos) < 0 ) return -1;
+       int ret = video_seek(pos);
+       if( ret < 0 ) return -1;
        if( !frame && !(frame=av_frame_alloc()) ) {
                fprintf(stderr, "FFVideoStream::load: av_frame_alloc failed\n");
                return -1;
        }
-       int ret = 0;
        for( int i=0; ret>=0 && !flushed && curr_pos<=pos && i<1000; ++i ) {
                ret = read_frame(frame);
        }
@@ -646,6 +688,9 @@ int FFVideoStream::video_seek(int64_t pos)
        if( gop < 4 ) gop = 4;
        if( gop > 64 ) gop = 64;
        if( pos >= curr_pos && pos <= curr_pos + gop ) return 0;
+       if( pos == curr_pos-1 && curr_pos > seek_pos ) return 1;
+       if( !st->codec || !st->codec->codec ) return -1;
+       avcodec_flush_buffers(st->codec);
 // back up a few frames to read up to current to help repair damages
        if( (pos-=gop) < 0 ) pos = 0;
        double secs = (double)pos / frame_rate;
@@ -654,6 +699,7 @@ int FFVideoStream::video_seek(int64_t pos)
        avformat_seek_file(fmt_ctx, st->index, -INT64_MAX, tstmp, INT64_MAX, 0);
        seek_pos = curr_pos = pos;
        st_eof(0);
+       flushed = 0;  need_packet = 1;
        return 1;
 }
 
@@ -691,6 +737,15 @@ int FFVideoStream::encode(VFrame *vframe)
        return ret >= 0 ? 0 : 1;
 }
 
+int FFVideoStream::encode_frame(FFPacket &pkt, AVFrame *frame, int &got_packet)
+{
+       int ret = avcodec_encode_video2(st->codec, pkt, frame, &got_packet);
+       if( ret < 0 ) {
+               ff_err(ret, "FFVideoStream::encode_frame: encode video failed\n");
+               return -1;
+       }
+       return ret;
+}
 
 PixelFormat FFVideoStream::color_model_to_pix_fmt(int color_model)
 {
@@ -765,9 +820,10 @@ int FFVideoStream::convert_picture_vframe(VFrame *frame,
                                " sws_getCachedContext() failed\n");
                return 1;
        }
-       if( sws_scale(convert_ctx, ip->data, ip->linesize, 0, ih,
-           opic.data, opic.linesize) < 0 ) {
-               fprintf(stderr, "FFVideoStream::convert_picture_frame: sws_scale() failed\n");
+       int ret = sws_scale(convert_ctx, ip->data, ip->linesize, 0, ih,
+           opic.data, opic.linesize);
+       if( ret < 0 ) {
+               ff_err(ret, "FFVideoStream::convert_picture_frame: sws_scale() failed\n");
                return 1;
        }
        return 0;
@@ -828,9 +884,10 @@ int FFVideoStream::convert_vframe_picture(VFrame *frame,
                                " sws_getCachedContext() failed\n");
                return 1;
        }
-       if( sws_scale(convert_ctx, opic.data, opic.linesize, 0, frame->get_h(),
-                       op->data, op->linesize) < 0 ) {
-               fprintf(stderr, "FFVideoStream::convert_frame_picture: sws_scale() failed\n");
+       int ret = sws_scale(convert_ctx, opic.data, opic.linesize, 0, frame->get_h(),
+                       op->data, op->linesize);
+       if( ret < 0 ) {
+               ff_err(ret, "FFVideoStream::convert_frame_picture: sws_scale() failed\n");
                return 1;
        }
        return 0;
@@ -886,8 +943,8 @@ FFMPEG::~FFMPEG()
        delete flow_lock;
        delete mux_lock;
        av_dict_free(&opts);
-       delete opt_video_filter;
-       delete opt_audio_filter;
+       delete [] opt_video_filter;
+       delete [] opt_audio_filter;
 }
 
 int FFMPEG::check_sample_rate(AVCodec *codec, int sample_rate)
@@ -929,10 +986,15 @@ AVRational FFMPEG::check_frame_rate(AVCodec *codec, double frame_rate)
 
 AVRational FFMPEG::to_sample_aspect_ratio(double aspect_ratio)
 {
+#if 1
        int height = 1000000, width = height * aspect_ratio;
        float w, h;
        MWindow::create_aspect_ratio(w, h, width, height);
        return (AVRational){(int)w, (int)h};
+#else
+// square pixels
+       return (AVRational){1, 1};
+#endif
 }
 
 AVRational FFMPEG::to_time_base(int sample_rate)
@@ -940,8 +1002,6 @@ AVRational FFMPEG::to_time_base(int sample_rate)
        return (AVRational){1, sample_rate};
 }
 
-extern void get_exe_path(char *result); // from main.C
-
 void FFMPEG::set_option_path(char *path, const char *fmt, ...)
 {
        get_exe_path(path);
@@ -1026,8 +1086,8 @@ int FFMPEG::get_encoder(const char *options,
                return 1;
        }
        if( get_encoder(fp, format, codec, bsfilter, bsargs) )
-               eprintf("FFMPEG::get_encoder:"
-                       " err: format/codec not found %s\n", options);
+               eprintf(_("FFMPEG::get_encoder:"
+                         " err: format/codec not found %s\n"), options);
        fclose(fp);
        return 0;
 }
@@ -1057,12 +1117,14 @@ int FFMPEG::read_options(const char *options, AVDictionary *&opts)
        return ret;
 }
 
-int FFMPEG::scan_options(const char *options, AVDictionary *&opts)
+int FFMPEG::scan_options(const char *options, AVDictionary *&opts, AVStream *st)
 {
        FILE *fp = fmemopen((void *)options,strlen(options),"r");
        if( !fp ) return 0;
        int ret = read_options(fp, options, opts);
        fclose(fp);
+       AVDictionaryEntry *tag = av_dict_get(opts, "id", NULL, 0);
+       if( tag ) st->id = strtol(tag->value,0,0);
        return ret;
 }
 
@@ -1077,8 +1139,8 @@ int FFMPEG::read_options(FILE *fp, const char *options, AVDictionary *&opts)
                if( line[0] == '\n' ) continue;
                char key[BCSTRLEN], val[BCTEXTLEN];
                if( scan_option_line(line, key, val) ) {
-                       eprintf("FFMPEG::read_options:"
-                               " err reading %s: line %d\n", options, no);
+                       eprintf(_("FFMPEG::read_options:"
+                                 " err reading %s: line %d\n"), options, no);
                        ret = 1;
                }
                if( !ret ) {
@@ -1157,7 +1219,7 @@ int FFMPEG::info(char *text, int len)
        for( int i=0; i<(int)fmt_ctx->nb_streams; ++i ) {
                AVStream *st = fmt_ctx->streams[i];
                AVCodecContext *avctx = st->codec;
-               report("stream %d,  id 0x%06x:\n", i, avctx->codec_id);
+               report(_("stream %d,  id 0x%06x:\n"), i, avctx->codec_id);
                const AVCodecDescriptor *desc = avcodec_descriptor_get(avctx->codec_id);
                if( avctx->codec_type == AVMEDIA_TYPE_VIDEO ) {
                        AVRational framerate = av_guess_frame_rate(fmt_ctx, st, 0);
@@ -1189,7 +1251,7 @@ int FFMPEG::info(char *text, int len)
                        report("  %d:%02d:%05.2f\n", hrs, mins, secs);
                }
                else
-                       report("  codec_type unknown\n");
+                       report(_("  codec_type unknown\n"));
        }
        report("\n");
        for( int i=0; i<(int)fmt_ctx->nb_programs; ++i ) {
@@ -1380,6 +1442,10 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                return 1;
        }
 
+       if( !strcmp(codec_name, CODEC_TAG_DVSD) ) strcpy(codec_name, "dv");
+       else if( !strcmp(codec_name, CODEC_TAG_MJPEG) ) strcpy(codec_name, "mjpeg");
+       else if( !strcmp(codec_name, CODEC_TAG_JPEG) ) strcpy(codec_name, "jpeg");
+
        int ret = 0;
        ff_lock("FFMPEG::open_encoder");
        FFStream *fst = 0;
@@ -1419,13 +1485,14 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                                break;
                        }
                        has_audio = 1;
-                       if( scan_options(asset->ff_audio_options, sopts) ) {
+                       if( scan_options(asset->ff_audio_options, sopts, st) ) {
                                eprintf("FFMPEG::open_encoder: bad audio options %s:%s\n",
                                        codec_name, filename);
                                ret = 1;
                                break;
                        }
                        if( asset->ff_audio_bitrate > 0 ) {
+                               ctx->bit_rate = asset->ff_audio_bitrate;
                                char arg[BCSTRLEN];
                                sprintf(arg, "%d", asset->ff_audio_bitrate);
                                av_dict_set(&sopts, "b", arg, 0);
@@ -1464,13 +1531,14 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                                break;
                        }
                        has_video = 1;
-                       if( scan_options(asset->ff_video_options, sopts) ) {
+                       if( scan_options(asset->ff_video_options, sopts, st) ) {
                                eprintf("FFMPEG::open_encoder: bad video options %s:%s\n",
                                        codec_name, filename);
                                ret = 1;
                                break;
                        }
                        if( asset->ff_video_bitrate > 0 ) {
+                               ctx->bit_rate = asset->ff_video_bitrate;
                                char arg[BCSTRLEN];
                                sprintf(arg, "%d", asset->ff_video_bitrate);
                                av_dict_set(&sopts, "b", arg, 0);
@@ -1518,6 +1586,9 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                }
        }
        if( !ret ) {
+               if( fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER )
+                       st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
                ret = avcodec_open2(st->codec, codec, &sopts);
                if( ret < 0 ) {
                        ff_err(ret,"FFMPEG::open_encoder");
@@ -1529,8 +1600,6 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
                        ret = 0;
        }
        if( !ret ) {
-               if( fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER )
-                       st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
                if( fst && bsfilter[0] )
                        fst->add_bsfilter(bsfilter, !bsargs[0] ? 0 : bsargs);
        }
@@ -1638,11 +1707,12 @@ int FFMPEG::decode_activate()
 
 int FFMPEG::encode_activate()
 {
+       int ret = 0;
        if( encoding < 0 ) {
                encoding = 0;
                if( !(fmt_ctx->flags & AVFMT_NOFILE) &&
-                   avio_open(&fmt_ctx->pb, fmt_ctx->filename, AVIO_FLAG_WRITE) < 0 ) {
-                       fprintf(stderr, "FFMPEG::encode_activate: err opening : %s\n",
+                   (ret=avio_open(&fmt_ctx->pb, fmt_ctx->filename, AVIO_FLAG_WRITE)) < 0 ) {
+                       ff_err(ret, "FFMPEG::encode_activate: err opening : %s\n",
                                fmt_ctx->filename);
                        return 1;
                }
@@ -1651,10 +1721,10 @@ int FFMPEG::encode_activate()
                char option_path[BCTEXTLEN];
                set_option_path(option_path, "format/%s", file_format);
                read_options(option_path, fopts);
-               int ret = avformat_write_header(fmt_ctx, &fopts);
+               ret = avformat_write_header(fmt_ctx, &fopts);
                av_dict_free(&fopts);
                if( ret < 0 ) {
-                       fprintf(stderr, "FFMPEG::encode_activate: write header failed %s\n",
+                       ff_err(ret, "FFMPEG::encode_activate: write header failed %s\n",
                                fmt_ctx->filename);
                        return 1;
                }
@@ -1755,19 +1825,15 @@ void FFMPEG::flow_ctl()
 int FFMPEG::mux_audio(FFrame *frm)
 {
        FFPacket pkt;
-       AVStream *st = frm->fst->st;
-       AVCodecContext *ctx = st->codec;
+       FFStream *fst = frm->fst;
+       AVCodecContext *ctx = fst->st->codec;
        AVFrame *frame = *frm;
        AVRational tick_rate = {1, ctx->sample_rate};
        frame->pts = av_rescale_q(frm->position, tick_rate, ctx->time_base);
        int got_packet = 0;
-       int ret = avcodec_encode_audio2(ctx, pkt, frame, &got_packet);
-       if( ret >= 0 && got_packet ) {
-               frm->fst->bs_filter(pkt);
-               av_packet_rescale_ts(pkt, ctx->time_base, st->time_base);
-               pkt->stream_index = st->index;
-               ret = av_interleaved_write_frame(fmt_ctx, pkt);
-       }
+       int ret = fst->encode_frame(pkt, frame, got_packet);
+       if( ret >= 0 && got_packet )
+               ret = fst->write_packet(pkt);
        if( ret < 0 )
                ff_err(ret, "FFMPEG::mux_audio");
        return ret >= 0 ? 0 : 1;
@@ -1776,14 +1842,14 @@ int FFMPEG::mux_audio(FFrame *frm)
 int FFMPEG::mux_video(FFrame *frm)
 {
        FFPacket pkt;
-       AVStream *st = frm->fst->st;
+       FFStream *fst = frm->fst;
        AVFrame *frame = *frm;
        frame->pts = frm->position;
        int ret = 1, got_packet = 0;
        if( fmt_ctx->oformat->flags & AVFMT_RAWPICTURE ) {
                /* a hack to avoid data copy with some raw video muxers */
                pkt->flags |= AV_PKT_FLAG_KEY;
-               pkt->stream_index  = st->index;
+               pkt->stream_index  = fst->st->index;
                AVPicture *picture = (AVPicture *)frame;
                pkt->data = (uint8_t *)picture;
                pkt->size = sizeof(AVPicture);
@@ -1791,13 +1857,9 @@ int FFMPEG::mux_video(FFrame *frm)
                got_packet = 1;
        }
        else
-               ret = avcodec_encode_video2(st->codec, pkt, frame, &got_packet);
-       if( ret >= 0 && got_packet ) {
-               frm->fst->bs_filter(pkt);
-               av_packet_rescale_ts(pkt, st->codec->time_base, st->time_base);
-               pkt->stream_index = st->index;
-               ret = av_interleaved_write_frame(fmt_ctx, pkt);
-       }
+               ret = fst->encode_frame(pkt, frame, got_packet);
+       if( ret >= 0 && got_packet )
+               ret = fst->write_packet(pkt);
        if( ret < 0 )
                ff_err(ret, "FFMPEG::mux_video");
        return ret >= 0 ? 0 : 1;
@@ -1845,6 +1907,10 @@ void FFMPEG::run()
                if( !done ) mux();
        }
        mux();
+       for( int i=0; i<ffaudio.size(); ++i )
+               ffaudio[i]->flush();
+       for( int i=0; i<ffvideo.size(); ++i )
+               ffvideo[i]->flush();
 }
 
 
@@ -2011,6 +2077,11 @@ int FFVideoStream::create_filter(const char *filter_spec,
                AVCodecContext *src_ctx, AVCodecContext *sink_ctx)
 {
        avfilter_register_all();
+       AVFilter *filter = avfilter_get_by_name(filter_spec);
+       if( !filter || filter->inputs->type != 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");
@@ -2042,6 +2113,11 @@ int FFAudioStream::create_filter(const char *filter_spec,
                AVCodecContext *src_ctx, AVCodecContext *sink_ctx)
 {
        avfilter_register_all();
+       AVFilter *filter = avfilter_get_by_name(filter_spec);
+       if( !filter || filter->inputs->type != 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");