X-Git-Url: http://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fcinelerra%2Fffmpeg.C;h=f3f9dcc4cff4a4cd5cb86a8d78d0206475788c7a;hb=101d92b4c50a831fb00edd65df57de41b408c7b6;hp=74d1d74522528855c84bcd5bedf766ed99640070;hpb=9851b98978d94e4f6d35969851658f58120c6863;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/cinelerra/ffmpeg.C b/cinelerra-5.1/cinelerra/ffmpeg.C index 74d1d745..f3f9dcc4 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" @@ -240,7 +241,6 @@ FFStream::FFStream(FFMPEG *ffmpeg, AVStream *st, int fidx) nudge = AV_NOPTS_VALUE; seek_pos = curr_pos = 0; seeked = 1; eof = 0; - index_markers = 0; reading = writing = 0; flushed = 0; need_packet = 1; @@ -315,10 +315,10 @@ int FFStream::decode_activate() if( ret >= 0 ) reading = 1; else - eprintf("FFStream::decode_activate: open decoder failed\n"); + eprintf(_("open decoder failed\n")); } else - eprintf("FFStream::decode_activate: can't clone input file\n"); + eprintf(_("can't clone input file\n")); av_dict_free(&copts); ff_unlock(); } @@ -385,7 +385,7 @@ int FFStream::load_filter(AVFrame *frame) frame, AV_BUFFERSRC_FLAG_KEEP_REF); if( ret < 0 ) { av_frame_unref(frame); - eprintf("FFStream::load_filter: av_buffersrc_add_frame_flags failed\n"); + eprintf(_("av_buffersrc_add_frame_flags failed\n")); } return ret; } @@ -428,6 +428,8 @@ int FFStream::write_packet(FFPacket &pkt) int FFStream::flush() { + if( writing < 0 ) + return -1; int ret = 0; while( ret >= 0 ) { FFPacket pkt; @@ -447,6 +449,7 @@ int FFStream::seek(int64_t no, double rate) // default ffmpeg native seek int npkts = 1; int64_t pos = no, plmt = -1; + IndexMarks *index_markers = get_markers(); if( index_markers && index_markers->size() > 1 ) { IndexMarks &marks = *index_markers; int i = marks.find(pos); @@ -526,7 +529,7 @@ int FFAudioStream::get_samples(float *&samples, uint8_t **data, int len) { samples = *(float **)data; if( resample_context ) { - if( len > aud_bfr_sz ) { + if( len > aud_bfr_sz ) { delete [] aud_bfr; aud_bfr = 0; } @@ -703,11 +706,18 @@ int FFAudioStream::encode_frame(AVPacket *pkt, AVFrame *frame, int &got_packet) void FFAudioStream::load_markers() { IndexState *index_state = ffmpeg->file_base->asset->index_state; - if( index_state->marker_status == MARKERS_NOTTESTED ) return; if( !index_state || idx >= index_state->audio_markers.size() ) return; + if( index_state->marker_status == MARKERS_NOTTESTED ) return; FFStream::load_markers(*index_state->audio_markers[idx], sample_rate); } +IndexMarks *FFAudioStream::get_markers() +{ + IndexState *index_state = ffmpeg->file_base->asset->index_state; + if( !index_state || idx >= index_state->audio_markers.size() ) return 0; + return index_state->audio_markers[idx]; +} + FFVideoStream::FFVideoStream(FFMPEG *ffmpeg, AVStream *strm, int idx, int fidx) : FFStream(ffmpeg, strm, fidx) { @@ -716,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() @@ -754,10 +766,10 @@ int FFVideoStream::load(VFrame *vframe, int64_t pos) ret = read_frame(frame); if( ret > 0 ) ++curr_pos; } + if( frame->format == AV_PIX_FMT_NONE || frame->width <= 0 || frame->height <= 0 ) + ret = -1; if( ret >= 0 ) { - AVCodecContext *ctx = st->codec; - ret = convert_cmodel(vframe, frame, - ctx->pix_fmt, ctx->width, ctx->height); + ret = convert_cmodel(vframe, frame); } ret = ret > 0 ? 1 : ret < 0 ? -1 : 0; return ret; @@ -798,9 +810,7 @@ int FFVideoStream::encode(VFrame *vframe) if( ret >= 0 ) { AVFrame *frame = *picture; frame->pts = curr_pos; - AVCodecContext *ctx = st->codec; - ret = convert_pixfmt(vframe, frame, - ctx->pix_fmt, ctx->width, ctx->height); + ret = convert_pixfmt(vframe, frame); } if( ret >= 0 ) { picture->queue(curr_pos); @@ -815,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"); @@ -825,12 +839,15 @@ int FFVideoStream::encode_frame(AVPacket *pkt, AVFrame *frame, int &got_packet) AVPixelFormat FFVideoConvert::color_model_to_pix_fmt(int color_model) { - switch( color_model ) { + switch( color_model ) { case BC_YUV422: return AV_PIX_FMT_YUYV422; case BC_RGB888: return AV_PIX_FMT_RGB24; case BC_RGBA8888: return AV_PIX_FMT_RGBA; case BC_BGR8888: return AV_PIX_FMT_BGR0; case BC_BGR888: return AV_PIX_FMT_BGR24; + case BC_ARGB8888: return AV_PIX_FMT_ARGB; + case BC_ABGR8888: return AV_PIX_FMT_ABGR; + case BC_RGB8: return AV_PIX_FMT_RGB8; case BC_YUV420P: return AV_PIX_FMT_YUV420P; case BC_YUV422P: return AV_PIX_FMT_YUV422P; case BC_YUV444P: return AV_PIX_FMT_YUV444P; @@ -838,6 +855,7 @@ AVPixelFormat FFVideoConvert::color_model_to_pix_fmt(int color_model) case BC_RGB565: return AV_PIX_FMT_RGB565; case BC_RGB161616: return AV_PIX_FMT_RGB48LE; case BC_RGBA16161616: return AV_PIX_FMT_RGBA64LE; + case BC_AYUV16161616: return AV_PIX_FMT_AYUV64LE; default: break; } @@ -846,12 +864,15 @@ AVPixelFormat FFVideoConvert::color_model_to_pix_fmt(int color_model) int FFVideoConvert::pix_fmt_to_color_model(AVPixelFormat pix_fmt) { - switch (pix_fmt) { + switch (pix_fmt) { case AV_PIX_FMT_YUYV422: return BC_YUV422; case AV_PIX_FMT_RGB24: return BC_RGB888; case AV_PIX_FMT_RGBA: return BC_RGBA8888; case AV_PIX_FMT_BGR0: return BC_BGR8888; case AV_PIX_FMT_BGR24: return BC_BGR888; + case AV_PIX_FMT_ARGB: return BC_ARGB8888; + case AV_PIX_FMT_ABGR: return BC_ABGR8888; + case AV_PIX_FMT_RGB8: return BC_RGB8; case AV_PIX_FMT_YUV420P: return BC_YUV420P; case AV_PIX_FMT_YUV422P: return BC_YUV422P; case AV_PIX_FMT_YUV444P: return BC_YUV444P; @@ -859,45 +880,61 @@ int FFVideoConvert::pix_fmt_to_color_model(AVPixelFormat pix_fmt) case AV_PIX_FMT_RGB565: return BC_RGB565; case AV_PIX_FMT_RGB48LE: return BC_RGB161616; case AV_PIX_FMT_RGBA64LE: return BC_RGBA16161616; + case AV_PIX_FMT_AYUV64LE: return BC_AYUV16161616; default: break; } - return BC_TRANSPARENCY; + return -1; +} + +int FFVideoConvert::convert_picture_vframe(VFrame *frame, AVFrame *ip) +{ + AVFrame *ipic = av_frame_alloc(); + int ret = convert_picture_vframe(frame, ip, ipic); + av_frame_free(&ipic); + return ret; } -int FFVideoConvert::convert_picture_vframe(VFrame *frame, - AVFrame *ip, AVPixelFormat ifmt, int iw, int ih) +int FFVideoConvert::convert_picture_vframe(VFrame *frame, AVFrame *ip, AVFrame *ipic) { - AVFrame opic; int cmodel = frame->get_color_model(); AVPixelFormat ofmt = color_model_to_pix_fmt(cmodel); if( ofmt == AV_PIX_FMT_NB ) return -1; - int size = av_image_fill_arrays(opic.data, opic.linesize, + int size = av_image_fill_arrays(ipic->data, ipic->linesize, frame->get_data(), ofmt, frame->get_w(), frame->get_h(), 1); if( size < 0 ) return -1; - // transfer line sizes must match also - int planar = BC_CModels::is_planar(cmodel); - int packed_width = !planar ? frame->get_bytes_per_line() : - BC_CModels::calculate_pixelsize(cmodel) * frame->get_w(); - if( packed_width != opic.linesize[0] ) return -1; - - if( planar ) { + int bpp = BC_CModels::calculate_pixelsize(cmodel); + int ysz = bpp * frame->get_w(), usz = ysz; + switch( cmodel ) { + case BC_YUV410P: + case BC_YUV411P: + usz /= 2; + case BC_YUV420P: + case BC_YUV422P: + usz /= 2; + case BC_YUV444P: // override av_image_fill_arrays() for planar types - opic.data[0] = frame->get_y(); - opic.data[1] = frame->get_u(); - opic.data[2] = frame->get_v(); - } - - convert_ctx = sws_getCachedContext(convert_ctx, iw, ih, ifmt, - frame->get_w(), frame->get_h(), ofmt, SWS_BICUBIC, NULL, NULL, NULL); + ipic->data[0] = frame->get_y(); ipic->linesize[0] = ysz; + ipic->data[1] = frame->get_u(); ipic->linesize[1] = usz; + ipic->data[2] = frame->get_v(); ipic->linesize[2] = usz; + break; + default: + ipic->data[0] = frame->get_data(); + ipic->linesize[0] = frame->get_bytes_per_line(); + break; + } + + AVPixelFormat pix_fmt = (AVPixelFormat)ip->format; + convert_ctx = sws_getCachedContext(convert_ctx, ip->width, ip->height, pix_fmt, + frame->get_w(), frame->get_h(), ofmt, SWS_POINT, NULL, NULL, NULL); if( !convert_ctx ) { fprintf(stderr, "FFVideoConvert::convert_picture_frame:" " sws_getCachedContext() failed\n"); return -1; } - int ret = sws_scale(convert_ctx, ip->data, ip->linesize, 0, ih, - opic.data, opic.linesize); + int ret = sws_scale(convert_ctx, ip->data, ip->linesize, 0, ip->height, + ipic->data, ipic->linesize); if( ret < 0 ) { ff_err(ret, "FFVideoConvert::convert_picture_frame: sws_scale() failed\n"); return -1; @@ -905,34 +942,40 @@ int FFVideoConvert::convert_picture_vframe(VFrame *frame, return 0; } -int FFVideoConvert::convert_cmodel(VFrame *frame, - AVFrame *ip, AVPixelFormat ifmt, int iw, int ih) +int FFVideoConvert::convert_cmodel(VFrame *frame, AVFrame *ip) { // try direct transfer - if( !convert_picture_vframe(frame, ip, ifmt, iw, ih) ) return 1; + if( !convert_picture_vframe(frame, ip) ) return 1; // use indirect transfer + AVPixelFormat ifmt = (AVPixelFormat)ip->format; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(ifmt); int max_bits = 0; for( int i = 0; i nb_components; ++i ) { int bits = desc->comp[i].depth; if( bits > max_bits ) max_bits = bits; } -// from libavcodec/pixdesc.c -#define pixdesc_has_alpha(pixdesc) ((pixdesc)->nb_components == 2 || \ - (pixdesc)->nb_components == 4 || (pixdesc)->flags & AV_PIX_FMT_FLAG_PAL) - int icolor_model = pixdesc_has_alpha(desc) ? - (max_bits > 8 ? BC_RGBA16161616 : BC_RGBA8888) : - (max_bits > 8 ? BC_RGB161616 : BC_RGB888) ; - VFrame vframe(iw, ih, icolor_model); - if( convert_picture_vframe(&vframe, ip, ifmt, iw, ih) ) return -1; + int imodel = pix_fmt_to_color_model(ifmt); + int imodel_is_yuv = BC_CModels::is_yuv(imodel); + int cmodel = frame->get_color_model(); + int cmodel_is_yuv = BC_CModels::is_yuv(cmodel); + if( imodel < 0 || imodel_is_yuv != cmodel_is_yuv ) { + imodel = cmodel_is_yuv ? + (BC_CModels::has_alpha(cmodel) ? + BC_AYUV16161616 : + (max_bits > 8 ? BC_AYUV16161616 : BC_YUV444P)) : + (BC_CModels::has_alpha(cmodel) ? + (max_bits > 8 ? BC_RGBA16161616 : BC_RGBA8888) : + (max_bits > 8 ? BC_RGB161616 : BC_RGB888)) ; + } + VFrame vframe(ip->width, ip->height, imodel); + if( convert_picture_vframe(&vframe, ip) ) return -1; frame->transfer_from(&vframe); return 1; } -int FFVideoConvert::transfer_cmodel(VFrame *frame, - AVFrame *ifp, AVPixelFormat ifmt, int iw, int ih) +int FFVideoConvert::transfer_cmodel(VFrame *frame, AVFrame *ifp) { - int ret = convert_cmodel(frame, ifp, ifmt, iw, ih); + int ret = convert_cmodel(frame, ifp); if( ret > 0 ) { const AVDictionary *src = av_frame_get_metadata(ifp); AVDictionaryEntry *t = NULL; @@ -944,38 +987,53 @@ int FFVideoConvert::transfer_cmodel(VFrame *frame, return ret; } -int FFVideoConvert::convert_vframe_picture(VFrame *frame, - AVFrame *op, AVPixelFormat ofmt, int ow, int oh) +int FFVideoConvert::convert_vframe_picture(VFrame *frame, AVFrame *op) +{ + AVFrame *opic = av_frame_alloc(); + int ret = convert_vframe_picture(frame, op, opic); + av_frame_free(&opic); + return ret; +} + +int FFVideoConvert::convert_vframe_picture(VFrame *frame, AVFrame *op, AVFrame *opic) { - AVFrame opic; int cmodel = frame->get_color_model(); AVPixelFormat ifmt = color_model_to_pix_fmt(cmodel); if( ifmt == AV_PIX_FMT_NB ) return -1; - int size = av_image_fill_arrays(opic.data, opic.linesize, + int size = av_image_fill_arrays(opic->data, opic->linesize, frame->get_data(), ifmt, frame->get_w(), frame->get_h(), 1); if( size < 0 ) return -1; - // transfer line sizes must match also - int planar = BC_CModels::is_planar(cmodel); - int packed_width = !planar ? frame->get_bytes_per_line() : - BC_CModels::calculate_pixelsize(cmodel) * frame->get_w(); - if( packed_width != opic.linesize[0] ) return -1; - - if( planar ) { + int bpp = BC_CModels::calculate_pixelsize(cmodel); + int ysz = bpp * frame->get_w(), usz = ysz; + switch( cmodel ) { + case BC_YUV410P: + case BC_YUV411P: + usz /= 2; + case BC_YUV420P: + case BC_YUV422P: + usz /= 2; + case BC_YUV444P: // override av_image_fill_arrays() for planar types - opic.data[0] = frame->get_y(); - opic.data[1] = frame->get_u(); - opic.data[2] = frame->get_v(); - } - - convert_ctx = sws_getCachedContext(convert_ctx, frame->get_w(), frame->get_h(), ifmt, - ow, oh, ofmt, SWS_BICUBIC, NULL, NULL, NULL); + opic->data[0] = frame->get_y(); opic->linesize[0] = ysz; + opic->data[1] = frame->get_u(); opic->linesize[1] = usz; + opic->data[2] = frame->get_v(); opic->linesize[2] = usz; + break; + default: + opic->data[0] = frame->get_data(); + opic->linesize[0] = frame->get_bytes_per_line(); + break; + } + + AVPixelFormat ofmt = (AVPixelFormat)op->format; + convert_ctx = sws_getCachedContext(convert_ctx, frame->get_w(), frame->get_h(), + ifmt, op->width, op->height, ofmt, SWS_POINT, NULL, NULL, NULL); if( !convert_ctx ) { fprintf(stderr, "FFVideoConvert::convert_frame_picture:" " sws_getCachedContext() failed\n"); return -1; } - int ret = sws_scale(convert_ctx, opic.data, opic.linesize, 0, frame->get_h(), + int ret = sws_scale(convert_ctx, opic->data, opic->linesize, 0, frame->get_h(), op->data, op->linesize); if( ret < 0 ) { ff_err(ret, "FFVideoConvert::convert_frame_picture: sws_scale() failed\n"); @@ -984,28 +1042,36 @@ int FFVideoConvert::convert_vframe_picture(VFrame *frame, return 0; } -int FFVideoConvert::convert_pixfmt(VFrame *frame, - AVFrame *op, AVPixelFormat ofmt, int ow, int oh) +int FFVideoConvert::convert_pixfmt(VFrame *frame, AVFrame *op) { // try direct transfer - if( !convert_vframe_picture(frame, op, ofmt, ow, oh) ) return 1; + if( !convert_vframe_picture(frame, op) ) return 1; // use indirect transfer - int colormodel = frame->get_color_model(); - int bits = BC_CModels::calculate_pixelsize(colormodel) * 8; - bits /= BC_CModels::components(colormodel); - int icolor_model = BC_CModels::has_alpha(colormodel) ? - (bits > 8 ? BC_RGBA16161616 : BC_RGBA8888) : - (bits > 8 ? BC_RGB161616: BC_RGB888) ; - VFrame vframe(frame->get_w(), frame->get_h(), icolor_model); + int cmodel = frame->get_color_model(); + int max_bits = BC_CModels::calculate_pixelsize(cmodel) * 8; + max_bits /= BC_CModels::components(cmodel); + AVPixelFormat ofmt = (AVPixelFormat)op->format; + int imodel = pix_fmt_to_color_model(ofmt); + int imodel_is_yuv = BC_CModels::is_yuv(imodel); + int cmodel_is_yuv = BC_CModels::is_yuv(cmodel); + if( imodel < 0 || imodel_is_yuv != cmodel_is_yuv ) { + imodel = cmodel_is_yuv ? + (BC_CModels::has_alpha(cmodel) ? + BC_AYUV16161616 : + (max_bits > 8 ? BC_AYUV16161616 : BC_YUV444P)) : + (BC_CModels::has_alpha(cmodel) ? + (max_bits > 8 ? BC_RGBA16161616 : BC_RGBA8888) : + (max_bits > 8 ? BC_RGB161616 : BC_RGB888)) ; + } + VFrame vframe(frame->get_w(), frame->get_h(), imodel); vframe.transfer_from(frame); - if( !convert_vframe_picture(&vframe, op, ofmt, ow, oh) ) return 1; + if( !convert_vframe_picture(&vframe, op) ) return 1; return -1; } -int FFVideoConvert::transfer_pixfmt(VFrame *frame, - AVFrame *ofp, AVPixelFormat ofmt, int ow, int oh) +int FFVideoConvert::transfer_pixfmt(VFrame *frame, AVFrame *ofp) { - int ret = convert_pixfmt(frame, ofp, ofmt, ow, oh); + int ret = convert_pixfmt(frame, ofp); if( ret > 0 ) { BC_Hash *hp = frame->get_params(); AVDictionary **dict = avpriv_frame_get_metadatap(ofp); @@ -1021,10 +1087,17 @@ int FFVideoConvert::transfer_pixfmt(VFrame *frame, void FFVideoStream::load_markers() { IndexState *index_state = ffmpeg->file_base->asset->index_state; - if( idx >= index_state->video_markers.size() ) return; + if( !index_state || idx >= index_state->video_markers.size() ) return; FFStream::load_markers(*index_state->video_markers[idx], frame_rate); } +IndexMarks *FFVideoStream::get_markers() +{ + IndexState *index_state = ffmpeg->file_base->asset->index_state; + if( !index_state || idx >= index_state->video_markers.size() ) return 0; + return !index_state ? 0 : index_state->video_markers[idx]; +} + FFMPEG::FFMPEG(FileBase *file_base) { @@ -1106,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}; @@ -1120,13 +1193,15 @@ AVRational FFMPEG::to_time_base(int sample_rate) void FFMPEG::set_option_path(char *path, const char *fmt, ...) { - get_exe_path(path); - strcat(path, "/ffmpeg/"); + char *ep = path + BCTEXTLEN-1; + strncpy(path, File::get_cindat_path(), ep-path); + strncat(path, "/ffmpeg/", ep-path); path += strlen(path); va_list ap; va_start(ap, fmt); - vsprintf(path, fmt, ap); + path += vsnprintf(path, ep-path, fmt, ap); va_end(ap); + *path = 0; } void FFMPEG::get_option_path(char *path, const char *type, const char *spec) @@ -1137,7 +1212,7 @@ void FFMPEG::get_option_path(char *path, const char *type, const char *spec) set_option_path(path, "%s/%s", type, spec); } -int FFMPEG::get_format(char *format, const char *path, char *spec) +int FFMPEG::get_format(char *format, const char *path, const char *spec) { char option_path[BCTEXTLEN], line[BCTEXTLEN], codec[BCTEXTLEN]; get_option_path(option_path, path, spec); @@ -1153,22 +1228,62 @@ int FFMPEG::get_format(char *format, const char *path, char *spec) return ret; } -int FFMPEG::get_file_format() +int FFMPEG::get_codec(char *codec, const char *path, const char *spec) { + char option_path[BCTEXTLEN], line[BCTEXTLEN], format[BCTEXTLEN]; + get_option_path(option_path, path, spec); + FILE *fp = fopen(option_path,"r"); + if( !fp ) return 1; int ret = 0; + if( !fgets(line, sizeof(line), fp) ) ret = 1; + fclose(fp); + if( !ret ) { + line[sizeof(line)-1] = 0; + ret = scan_option_line(line, format, codec); + } + if( !ret ) { + char *vp = codec, *ep = vp+BCTEXTLEN-1; + while( vp < ep && *vp && *vp != '|' ) ++vp; + if( *vp == '|' ) --vp; + while( vp > codec && (*vp==' ' || *vp=='\t') ) *vp-- = 0; + } + return ret; +} + +int FFMPEG::get_file_format() +{ + 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; } @@ -1176,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++; @@ -1197,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); @@ -1209,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)); @@ -1232,12 +1348,11 @@ int FFMPEG::get_encoder(const char *options, { FILE *fp = fopen(options,"r"); if( !fp ) { - eprintf("FFMPEG::get_encoder: options open failed %s\n",options); + eprintf(_("options open failed %s\n"),options); return 1; } if( get_encoder(fp, format, codec, bsfilter, bsargs) ) - eprintf(_("FFMPEG::get_encoder:" - " err: format/codec not found %s\n"), options); + eprintf(_("format/codec not found %s\n"), options); fclose(fp); return 0; } @@ -1258,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; } @@ -1284,13 +1406,11 @@ 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]; if( scan_option_line(line, key, val) ) { - eprintf(_("FFMPEG::read_options:" - " err reading %s: line %d\n"), options, no); + eprintf(_("err reading %s: line %d\n"), options, no); ret = 1; } if( !ret ) { @@ -1358,7 +1478,7 @@ double FFMPEG::to_secs(int64_t time, AVRational time_base) { double base_time = time == AV_NOPTS_VALUE ? 0 : av_rescale_q(time, time_base, AV_TIME_BASE_Q); - return base_time / AV_TIME_BASE; + return base_time / AV_TIME_BASE; } int FFMPEG::info(char *text, int len) @@ -1483,8 +1603,7 @@ int FFMPEG::open_decoder() { struct stat st; if( stat(fmt_ctx->filename, &st) < 0 ) { - eprintf("FFMPEG::open_decoder: can't stat file: %s\n", - fmt_ctx->filename); + eprintf(_("can't stat file: %s\n"), fmt_ctx->filename); return 1; } @@ -1507,8 +1626,8 @@ int FFMPEG::open_decoder() printf("FFMPEG::open_decoder: some stream times estimated\n"); ff_lock("FFMPEG::open_decoder"); - int bad_time = 0; - for( int i=0; i<(int)fmt_ctx->nb_streams; ++i ) { + int ret = 0, bad_time = 0; + for( int i=0; !ret && i<(int)fmt_ctx->nb_streams; ++i ) { AVStream *st = fmt_ctx->streams[i]; if( st->duration == AV_NOPTS_VALUE ) bad_time = 1; AVCodecContext *avctx = st->codec; @@ -1533,7 +1652,7 @@ int FFMPEG::open_decoder() vid->nudge = st->start_time; vid->reading = -1; if( opt_video_filter ) - vid->create_filter(opt_video_filter, avctx,avctx); + ret = vid->create_filter(opt_video_filter, avctx,avctx); } else if( avctx->codec_type == AVMEDIA_TYPE_AUDIO ) { if( avctx->channels < 1 ) continue; @@ -1561,13 +1680,13 @@ int FFMPEG::open_decoder() aud->nudge = st->start_time; aud->reading = -1; if( opt_audio_filter ) - aud->create_filter(opt_audio_filter, avctx,avctx); + ret = aud->create_filter(opt_audio_filter, avctx,avctx); } } if( bad_time ) printf("FFMPEG::open_decoder: some stream have bad times\n"); ff_unlock(); - return 0; + return ret < 0 ? -1 : 0; } @@ -1576,24 +1695,27 @@ 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 ) { - eprintf("FFMPEG::init_encoder: bad file path: %s\n", filename); + eprintf(_("bad file path: %s\n"), filename); return 1; } ::close(fd); int ret = get_file_format(); if( ret > 0 ) { - eprintf("FFMPEG::init_encoder: bad file format: %s\n", filename); + eprintf(_("bad file format: %s\n"), filename); return 1; } if( ret < 0 ) { - eprintf("FFMPEG::init_encoder: mismatch audio/video file format: %s\n", filename); + eprintf(_("mismatch audio/video file format: %s\n"), filename); return 1; } 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("FFMPEG::init_encoder: failed: %s\n", filename); + eprintf(_("failed: %s\n"), filename); ret = 1; } if( !ret ) { @@ -1618,8 +1740,7 @@ int FFMPEG::open_encoder(const char *type, const char *spec) char format_name[BCSTRLEN], codec_name[BCTEXTLEN]; char bsfilter[BCSTRLEN], bsargs[BCTEXTLEN]; if( get_encoder(option_path, format_name, codec_name, bsfilter, bsargs) ) { - eprintf("FFMPEG::open_encoder: get_encoder failed %s:%s\n", - option_path, filename); + eprintf(_("get_encoder failed %s:%s\n"), option_path, filename); return 1; } @@ -1635,40 +1756,35 @@ int FFMPEG::open_encoder(const char *type, const char *spec) const AVCodecDescriptor *codec_desc = 0; AVCodec *codec = avcodec_find_encoder_by_name(codec_name); if( !codec ) { - eprintf("FFMPEG::open_encoder: cant find codec %s:%s\n", - codec_name, filename); + eprintf(_("cant find codec %s:%s\n"), codec_name, filename); ret = 1; } if( !ret ) { codec_desc = avcodec_descriptor_get(codec->id); if( !codec_desc ) { - eprintf("FFMPEG::open_encoder: unknown codec %s:%s\n", - codec_name, filename); + eprintf(_("unknown codec %s:%s\n"), codec_name, filename); ret = 1; } } if( !ret ) { st = avformat_new_stream(fmt_ctx, 0); if( !st ) { - eprintf("FFMPEG::open_encoder: cant create stream %s:%s\n", - codec_name, filename); + eprintf(_("cant create stream %s:%s\n"), codec_name, filename); ret = 1; } - } + } if( !ret ) { AVCodecContext *ctx = st->codec; switch( codec_desc->type ) { case AVMEDIA_TYPE_AUDIO: { if( has_audio ) { - eprintf("FFMPEG::open_encoder: duplicate audio %s:%s\n", - codec_name, filename); + eprintf(_("duplicate audio %s:%s\n"), codec_name, filename); ret = 1; break; } has_audio = 1; if( scan_options(asset->ff_audio_options, sopts, st) ) { - eprintf("FFMPEG::open_encoder: bad audio options %s:%s\n", - codec_name, filename); + eprintf(_("bad audio options %s:%s\n"), codec_name, filename); ret = 1; break; } @@ -1689,8 +1805,7 @@ int FFMPEG::open_encoder(const char *type, const char *spec) ctx->channel_layout = av_get_default_channel_layout(ctx->channels); ctx->sample_rate = check_sample_rate(codec, asset->sample_rate); if( !ctx->sample_rate ) { - eprintf("FFMPEG::open_encoder:" - " check_sample_rate failed %s\n", filename); + eprintf(_("check_sample_rate failed %s\n"), filename); ret = 1; break; } @@ -1706,15 +1821,13 @@ int FFMPEG::open_encoder(const char *type, const char *spec) break; } case AVMEDIA_TYPE_VIDEO: { if( has_video ) { - eprintf("FFMPEG::open_encoder: duplicate video %s:%s\n", - codec_name, filename); + eprintf(_("duplicate video %s:%s\n"), codec_name, filename); ret = 1; break; } has_video = 1; if( scan_options(asset->ff_video_options, sopts, st) ) { - eprintf("FFMPEG::open_encoder: bad video options %s:%s\n", - codec_name, filename); + eprintf(_("bad video options %s:%s\n"), codec_name, filename); ret = 1; break; } @@ -1751,18 +1864,19 @@ int FFMPEG::open_encoder(const char *type, const char *spec) 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("FFMPEG::open_encoder:" - " check_frame_rate failed %s\n", filename); + eprintf(_("check_frame_rate failed %s\n"), filename); ret = 1; break; } 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("FFMPEG::open_encoder: not audio/video, %s:%s\n", - codec_name, filename); + eprintf(_("not audio/video, %s:%s\n"), codec_name, filename); ret = 1; } } @@ -1773,8 +1887,7 @@ int FFMPEG::open_encoder(const char *type, const char *spec) ret = avcodec_open2(st->codec, codec, &sopts); if( ret < 0 ) { ff_err(ret,"FFMPEG::open_encoder"); - eprintf("FFMPEG::open_encoder: open failed %s:%s\n", - codec_name, filename); + eprintf(_("open failed %s:%s\n"), codec_name, filename); ret = 1; } else @@ -1785,9 +1898,10 @@ int FFMPEG::open_encoder(const char *type, const char *spec) fst->add_bsfilter(bsfilter, !bsargs[0] ? 0 : bsargs); } - ff_unlock(); if( !ret ) start_muxer(); + + ff_unlock(); av_dict_free(&sopts); return ret; } @@ -1906,20 +2020,55 @@ int FFMPEG::encode_activate() (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; + 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; + return -1; } + av_dict_free(&fopts); encoding = 1; } return encoding; @@ -2121,7 +2270,7 @@ const char* FFMPEG::ff_audio_format(int stream) AVStream *st = ffaudio[stream]->st; AVCodecID id = st->codec->codec_id; const AVCodecDescriptor *desc = avcodec_descriptor_get(id); - return desc ? desc->name : "Unknown"; + return desc ? desc->name : _("Unknown"); } int FFMPEG::ff_audio_pid(int stream) @@ -2231,7 +2380,7 @@ const char* FFMPEG::ff_video_format(int stream) AVStream *st = ffvideo[stream]->st; AVCodecID id = st->codec->codec_id; const AVCodecDescriptor *desc = avcodec_descriptor_get(id); - return desc ? desc->name : "Unknown"; + return desc ? desc->name : _("Unknown"); } double FFMPEG::ff_frame_rate(int stream) @@ -2259,7 +2408,12 @@ 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); + const char *sp = filter_spec; + char filter_name[BCSTRLEN], *np = filter_name; + 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); 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; @@ -2288,14 +2442,19 @@ int FFVideoStream::create_filter(const char *filter_spec, ff_err(ret, "FFVideoStream::create_filter"); else ret = FFStream::create_filter(filter_spec); - return ret >= 0 ? 0 : 1; + return ret >= 0 ? 0 : -1; } 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); + const char *sp = filter_spec; + char filter_name[BCSTRLEN], *np = filter_name; + 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); 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; @@ -2330,7 +2489,7 @@ int FFAudioStream::create_filter(const char *filter_spec, ff_err(ret, "FFAudioStream::create_filter"); else ret = FFStream::create_filter(filter_spec); - return ret >= 0 ? 0 : 1; + return ret >= 0 ? 0 : -1; } int FFStream::create_filter(const char *filter_spec) @@ -2355,8 +2514,11 @@ int FFStream::create_filter(const char *filter_spec) if( ret >= 0 ) ret = avfilter_graph_config(filter_graph, NULL); - if( ret < 0 ) + if( ret < 0 ) { ff_err(ret, "FFStream::create_filter"); + avfilter_graph_free(&filter_graph); + filter_graph = 0; + } avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); return ret; @@ -2408,7 +2570,8 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled) av_init_packet(&pkt); AVFrame *frame = av_frame_alloc(); if( !frame ) { - fprintf(stderr, "FFMPEG::scan: av_frame_alloc failed\n"); + fprintf(stderr,"FFMPEG::scan: "); + fprintf(stderr,_("av_frame_alloc failed\n")); return -1; } @@ -2421,8 +2584,10 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled) AVStream *st = fmt_ctx->streams[i]; AVCodecID codec_id = st->codec->codec_id; AVCodec *decoder = avcodec_find_decoder(codec_id); - if( avcodec_open2(st->codec, decoder, &copts) < 0 ) - fprintf(stderr, "FFMPEG::scan: codec open failed\n"); + if( avcodec_open2(st->codec, decoder, &copts) < 0 ) { + fprintf(stderr,"FFMPEG::scan: "); + fprintf(stderr,_("codec open failed\n")); + } av_dict_free(&copts); } int errs = 0; @@ -2434,7 +2599,7 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled) if( ret < 0 ) { if( ret == AVERROR_EOF ) break; if( ++errs > 100 ) { - ff_err(ret, "over 100 read_frame errs\n"); + ff_err(ret,_("over 100 read_frame errs\n")); break; } continue; @@ -2492,7 +2657,7 @@ int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled) int64_t pos = index_state->pos(ch); if( pos != aud->curr_pos ) { if( abs(pos-aud->curr_pos) > 1 ) -printf("audio%d pad %ld %ld (%ld)\n", aud->idx, pos, aud->curr_pos, pos-aud->curr_pos); +printf("audio%d pad %jd %jd (%jd)\n", aud->idx, pos, aud->curr_pos, pos-aud->curr_pos); index_state->pad_data(ch, nch, aud->curr_pos); } av_frame_unref(frame); @@ -2520,7 +2685,6 @@ printf("audio%d pad %ld %ld (%ld)\n", aud->idx, pos, aud->curr_pos, pos-aud->cur void FFStream::load_markers(IndexMarks &marks, double rate) { - index_markers = &marks; int in = 0; int64_t sz = marks.size(); int max_entries = fmt_ctx->max_index_size / sizeof(AVIndexEntry) - 1;