X-Git-Url: http://git.cinelerra-gg.org/git/?p=goodguy%2Fhistory.git;a=blobdiff_plain;f=cinelerra-5.1%2Fcinelerra%2Fffmpeg.C;h=44a337e60e7e4e84841a71a3a5f3d7db2a460a6c;hp=474d7c135da3f26cb3dbef4f8eee8c18803e7921;hb=a4de4732339bf38b5b225c533be1bdf60748f04a;hpb=7c199a42936462d662ee405fe0b4df5ac74850dc diff --git a/cinelerra-5.1/cinelerra/ffmpeg.C b/cinelerra-5.1/cinelerra/ffmpeg.C index 474d7c13..44a337e6 100644 --- a/cinelerra-5.1/cinelerra/ffmpeg.C +++ b/cinelerra-5.1/cinelerra/ffmpeg.C @@ -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"); @@ -948,9 +955,11 @@ int FFVideoConvert::convert_cmodel(VFrame *frame, AVFrame *ip) if( bits > max_bits ) max_bits = bits; } int imodel = pix_fmt_to_color_model(ifmt); - if( imodel < 0 ) { - int cmodel = frame->get_color_model(); - BC_CModels::is_yuv(cmodel) ? + 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)) : @@ -1039,19 +1048,20 @@ int FFVideoConvert::convert_pixfmt(VFrame *frame, AVFrame *op) if( !convert_vframe_picture(frame, op) ) return 1; // use indirect transfer int cmodel = frame->get_color_model(); - int bits = BC_CModels::calculate_pixelsize(cmodel) * 8; - bits /= BC_CModels::components(cmodel); + 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); - if( imodel < 0 ) { - imodel = - BC_CModels::is_yuv(cmodel) ? - (BC_CModels::has_alpha(cmodel) ? + 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 : - (bits > 8 ? BC_AYUV16161616 : BC_YUV444P)) : + (max_bits > 8 ? BC_AYUV16161616 : BC_YUV444P)) : (BC_CModels::has_alpha(cmodel) ? - (bits > 8 ? BC_RGBA16161616 : BC_RGBA8888) : - (bits > 8 ? BC_RGB161616 : BC_RGB888)) ; + (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); @@ -1169,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}; @@ -1242,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; } @@ -1263,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++; @@ -1284,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); @@ -1296,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)); @@ -1344,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; } @@ -1370,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]; @@ -1675,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; @@ -1799,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; @@ -1833,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); @@ -1843,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"); @@ -1886,30 +1930,33 @@ int FFMPEG::decode_activate() for( int aidx=0; aidxnudge = 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; iprograms[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]; @@ -1931,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; istreams[i]; @@ -1942,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; vidxnudge != AV_NOPTS_VALUE ) continue; - ffvideo[vidx]->nudge = nudge; + if( ffvideo[vidx]->nudge == AV_NOPTS_VALUE ) + ffvideo[vidx]->nudge = nudge; } for( int aidx=0; aidxnudge != AV_NOPTS_VALUE ) continue; - ffaudio[aidx]->nudge = nudge; + if( ffaudio[aidx]->nudge == AV_NOPTS_VALUE ) + ffaudio[aidx]->nudge = nudge; } decoding = 1; } @@ -1982,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; @@ -2514,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); @@ -2573,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; @@ -2592,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; iput_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; iput_data(ch+i,nch,samples+i,len); + } } pkt.data += ret; pkt.size -= ret;