#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
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];
ret = AVERROR(ENOMEM);
}
if( ret >= 0 ) {
- av_codec_set_pkt_timebase(avctx, st->time_base);
- if( decoder->capabilities & AV_CODEC_CAP_DR1 )
- avctx->flags |= CODEC_FLAG_EMU_EDGE;
avcodec_parameters_to_context(avctx, st->codecpar);
if( !av_dict_get(copts, "threads", NULL, 0) )
avctx->thread_count = ffmpeg->ff_cpus();
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;
{
if( writing >= 0 ) return writing;
if( !avctx->codec ) return writing = 0;
- frame_sz = avctx->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE ?
+ frame_sz = avctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE ?
10000 : avctx->frame_size;
return FFStream::encode_activate();
}
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;
{
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();
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);
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;
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;
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)
{
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;
}
}
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;
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");
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);
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;
}
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;
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");
ctx->qmin = ctx->qmax = asset->ff_audio_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_audio_quality);
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,
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);
vid->width = asset->width;
vid->height = asset->height;
vid->frame_rate = asset->frame_rate;
- AVPixelFormat pix_fmt = codec->pix_fmts ?
- codec->pix_fmts[0] : AV_PIX_FMT_YUV420P;
- AVDictionaryEntry *tag = av_dict_get(sopts, "cin_pix_fmt", NULL, 0);
- if( tag != 0 ) {
- int avfmt = av_get_pix_fmt(tag->value);
- if( avfmt < 0 ) {
- eprintf(_("cin_pix_fmt unknown = %s\n"), tag->value);
- ret = 1;
- break;
- }
- pix_fmt = (AVPixelFormat)avfmt;
- }
+
+ 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;
}
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( 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;
}
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);
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);
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;
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),