From fa7f91658c01ba88aab006beff8b167a4bbb7085 Mon Sep 17 00:00:00 2001 From: Good Guy Date: Fri, 25 Sep 2015 14:19:56 -0600 Subject: [PATCH] add ffmpeg indexing --- cinelerra-5.0/cinelerra/asset.C | 24 - cinelerra-5.0/cinelerra/asset.h | 11 +- cinelerra-5.0/cinelerra/audioalsa.C | 3 + cinelerra-5.0/cinelerra/awindowgui.C | 1 + cinelerra-5.0/cinelerra/cwindowgui.C | 1 + cinelerra-5.0/cinelerra/deleteallindexes.C | 4 +- cinelerra-5.0/cinelerra/edl.C | 5 +- cinelerra-5.0/cinelerra/ffmpeg.C | 594 +++++++++++++------ cinelerra-5.0/cinelerra/ffmpeg.h | 55 +- cinelerra-5.0/cinelerra/fileac3.C | 6 +- cinelerra-5.0/cinelerra/fileffmpeg.C | 89 +++ cinelerra-5.0/cinelerra/fileffmpeg.h | 17 +- cinelerra-5.0/cinelerra/filempeg.C | 59 +- cinelerra-5.0/cinelerra/filexml.C | 30 +- cinelerra-5.0/cinelerra/filexml.h | 6 +- cinelerra-5.0/cinelerra/indexable.C | 20 +- cinelerra-5.0/cinelerra/indexable.h | 5 +- cinelerra-5.0/cinelerra/indexfile.C | 106 ++-- cinelerra-5.0/cinelerra/indexfile.h | 1 + cinelerra-5.0/cinelerra/indexfile.inc | 7 - cinelerra-5.0/cinelerra/indexstate.C | 427 ++++++++----- cinelerra-5.0/cinelerra/indexstate.h | 170 +++++- cinelerra-5.0/cinelerra/indexstate.inc | 14 +- cinelerra-5.0/cinelerra/indexthread.C | 140 +---- cinelerra-5.0/cinelerra/mbuttons.C | 3 +- cinelerra-5.0/cinelerra/mediadb.C | 4 +- cinelerra-5.0/cinelerra/mwindow.C | 3 + cinelerra-5.0/cinelerra/mwindowgui.C | 4 +- cinelerra-5.0/cinelerra/nestededls.C | 2 +- cinelerra-5.0/cinelerra/performanceprefs.C | 22 + cinelerra-5.0/cinelerra/performanceprefs.h | 12 + cinelerra-5.0/cinelerra/performanceprefs.inc | 1 + cinelerra-5.0/cinelerra/preferences.C | 24 +- cinelerra-5.0/cinelerra/preferences.h | 2 + cinelerra-5.0/cinelerra/record.C | 1 + cinelerra-5.0/cinelerra/removefile.C | 4 +- cinelerra-5.0/guicast/arraylist.h | 1 + cinelerra-5.0/guicast/bcwindowbase.C | 2 + cinelerra-5.0/guicast/test4.C | 4 +- cinelerra-5.0/guicast/thread.C | 10 - cinelerra-5.0/guicast/thread.h | 7 +- 41 files changed, 1191 insertions(+), 710 deletions(-) diff --git a/cinelerra-5.0/cinelerra/asset.C b/cinelerra-5.0/cinelerra/asset.C index c7ebcdd2..42b5752a 100644 --- a/cinelerra-5.0/cinelerra/asset.C +++ b/cinelerra-5.0/cinelerra/asset.C @@ -201,12 +201,6 @@ void Asset::boundaries() //printf("Asset::boundaries %d %d %f\n", __LINE__, sample_rate, frame_rate); } - -void Asset::reset_index() -{ - index_state->reset(); -} - void Asset::copy_from(Asset *asset, int do_index) { copy_location(asset); @@ -574,11 +568,6 @@ int Asset::read_index(FileXML *file) return 0; } -int Asset::write_index(const char *path, int data_bytes) -{ - return index_state->write_index(path, data_bytes, this, audio_length); -} - // Output path is the path of the output file if name truncation is desired. // It is a "" if complete names should be used. @@ -723,7 +712,6 @@ int Asset::write_index(FileXML *file) - const char* Asset::construct_param(const char *param, const char *prefix, char *return_value) @@ -1034,18 +1022,6 @@ void Asset::save_defaults(BC_Hash *defaults, -int Asset::update_path(char *new_path) -{ - strcpy(path, new_path); - return 0; -} - -void Asset::update_index(Asset *asset) -{ - index_state->copy_from(asset->index_state); -} - - int Asset::dump(FILE *fp) { fprintf(fp," asset::dump\n"); diff --git a/cinelerra-5.0/cinelerra/asset.h b/cinelerra-5.0/cinelerra/asset.h index 8fe38692..30523acd 100644 --- a/cinelerra-5.0/cinelerra/asset.h +++ b/cinelerra-5.0/cinelerra/asset.h @@ -30,6 +30,7 @@ #include "filexml.inc" #include "indexable.h" #include "indexfile.inc" +#include "indexstate.inc" #include "linklist.h" #include "pluginserver.inc" @@ -79,7 +80,6 @@ public: // Executed during index building only - void update_index(Asset *asset); int equivalent(Asset &asset, int test_audio, int test_video); @@ -96,17 +96,11 @@ public: int read_index(FileXML *xml); void reset_audio(); void reset_video(); - void reset_index(); // When the index file is wrong, reset the asset values int reset_timecode(); // Output path is the path of the output file if name truncation is desired. // It is a "" if; complete names should be used. - int write(FileXML *file, - int include_index, - const char *output_path); -// Write the index data and asset info. Used by IndexThread. - int write_index(const char *path, int data_bytes); - + int write(FileXML *file, int include_index, const char *output_path); // For Indexable int get_audio_channels(); @@ -126,7 +120,6 @@ public: int write_audio(FileXML *xml); int write_video(FileXML *xml); int write_index(FileXML *xml); - int update_path(char *new_path); diff --git a/cinelerra-5.0/cinelerra/audioalsa.C b/cinelerra-5.0/cinelerra/audioalsa.C index 3ec41711..60604d43 100644 --- a/cinelerra-5.0/cinelerra/audioalsa.C +++ b/cinelerra-5.0/cinelerra/audioalsa.C @@ -524,6 +524,9 @@ int AudioALSA::write_buffer(char *buffer, int size) int ret = snd_pcm_avail_update(get_output()); if( ret >= period_size ) { if( ret > count ) ret = count; +//FILE *alsa_fp = 0; +//if( !alsa_fp ) alsa_fp = fopen("/tmp/alsa.raw","w"); +//if( alsa_fp ) fwrite(buffer, sample_size, ret, alsa_fp); //printf("AudioALSA::snd_pcm_writei start %d\n",count); ret = snd_pcm_writei(get_output(),buffer,ret); //printf("AudioALSA::snd_pcm_writei done %d\n", ret); diff --git a/cinelerra-5.0/cinelerra/awindowgui.C b/cinelerra-5.0/cinelerra/awindowgui.C index 704d24b2..70df0cce 100644 --- a/cinelerra-5.0/cinelerra/awindowgui.C +++ b/cinelerra-5.0/cinelerra/awindowgui.C @@ -113,6 +113,7 @@ AssetPicon::~AssetPicon() if(edl) edl->remove_user(); if( icon && !gui->protected_pixmap(icon) ) { delete icon; + if( !plugin ) delete icon_vframe; } } diff --git a/cinelerra-5.0/cinelerra/cwindowgui.C b/cinelerra-5.0/cinelerra/cwindowgui.C index 470d0b02..95097523 100644 --- a/cinelerra-5.0/cinelerra/cwindowgui.C +++ b/cinelerra-5.0/cinelerra/cwindowgui.C @@ -122,6 +122,7 @@ CWindowGUI::~CWindowGUI() delete zoom_panel; delete active; delete inactive; + delete orig_mask_keyframe; } void CWindowGUI::create_objects() diff --git a/cinelerra-5.0/cinelerra/deleteallindexes.C b/cinelerra-5.0/cinelerra/deleteallindexes.C index 4160b1e6..4941e093 100644 --- a/cinelerra-5.0/cinelerra/deleteallindexes.C +++ b/cinelerra-5.0/cinelerra/deleteallindexes.C @@ -68,6 +68,7 @@ void DeleteAllIndexes::run() // prepare filter const char *filter1 = ".idx"; const char *filter2 = ".toc"; + const char *filter3 = ".mkr"; // pwindow->disable_window(); sprintf(string, _("Delete all indexes in %s?"), string1); @@ -85,7 +86,8 @@ void DeleteAllIndexes::run() sprintf(string2, "%s%s", string1, dir.dir_list.values[i]->name); // test filter if(test_filter(string2, filter1) || - test_filter(string2, filter2)) + test_filter(string2, filter2) || + test_filter(string2, filter3)) { remove(string2); printf("DeleteAllIndexes::run %s\n", string2); diff --git a/cinelerra-5.0/cinelerra/edl.C b/cinelerra-5.0/cinelerra/edl.C index 05e7618c..8ef59f2b 100644 --- a/cinelerra-5.0/cinelerra/edl.C +++ b/cinelerra-5.0/cinelerra/edl.C @@ -394,8 +394,7 @@ int EDL::save_xml(FileXML *file, int EDL::copy_all(EDL *edl) { if(this == edl) return 0; - - index_state->copy_from(edl->index_state); + update_index(edl); nested_edls->clear(); copy_session(edl); copy_assets(edl); @@ -1201,7 +1200,7 @@ void EDL::insert_asset(Asset *asset, void EDL::set_index_file(Indexable *indexable) { - if(indexable->is_asset) + if(indexable->is_asset) assets->update_index((Asset*)indexable); else nested_edls->update_index((EDL*)indexable); diff --git a/cinelerra-5.0/cinelerra/ffmpeg.C b/cinelerra-5.0/cinelerra/ffmpeg.C index 2f1a1987..efb11be3 100644 --- a/cinelerra-5.0/cinelerra/ffmpeg.C +++ b/cinelerra-5.0/cinelerra/ffmpeg.C @@ -18,6 +18,7 @@ #include "fileffmpeg.h" #include "file.h" #include "ffmpeg.h" +#include "indexfile.h" #include "libdv.h" #include "libmjpeg.h" #include "mainerror.h" @@ -97,35 +98,35 @@ int FFAudioStream::read(float *fp, long len) return len; } -void FFAudioStream::realloc(long sz, int nch, long len) +void FFAudioStream::realloc(long nsz, int nch, long len) { - long bsz = sz * nch; + long bsz = nsz * nch; float *np = new float[bsz]; inp = np + read(np, len) * nch; outp = np; lmt = np + bsz; this->nch = nch; - this->sz = sz; + sz = nsz; delete [] bfr; bfr = np; } -void FFAudioStream::realloc(long sz, int nch) +void FFAudioStream::realloc(long nsz, int nch) { - if( sz > this->sz || this->nch != nch ) { - long len = this->nch != nch ? 0 : curr_pos - seek_pos; - if( len > this->sz ) len = this->sz; + if( nsz > sz || this->nch != nch ) { + long len = this->nch != nch ? 0 : hpos; + if( len > sz ) len = sz; iseek(len); - realloc(sz, nch, len); + realloc(nsz, nch, len); } } -void FFAudioStream::reserve(long sz, int nch) +void FFAudioStream::reserve(long nsz, int nch) { long len = (inp - outp) / nch; - sz += len; - if( sz > this->sz || this->nch != nch ) { + nsz += len; + if( nsz > sz || this->nch != nch ) { if( this->nch != nch ) len = 0; - realloc(sz, nch, len); + realloc(nsz, nch, len); return; } if( (len*=nch) > 0 && bfr != outp ) @@ -146,9 +147,10 @@ long FFAudioStream::avail() long len = outp >= in1 ? outp-in1 : outp-bfr + lmt-in1; return len / nch; } -void FFAudioStream::reset() // clear bfr +void FFAudioStream::reset_history() { inp = outp = bfr; + hpos = 0; } void FFAudioStream::iseek(int64_t ofs) @@ -182,6 +184,7 @@ int FFAudioStream::write(const float *fp, long len) if( ip >= lmt ) ip = bfr; } inp = ip; + hpos += len; return len; } @@ -197,6 +200,7 @@ int FFAudioStream::zero(long len) if( ip >= lmt ) ip = bfr; } inp = ip; + hpos += len; return len; } @@ -226,11 +230,11 @@ int FFAudioStream::write(const double *dp, long len, int ch) } -FFStream::FFStream(FFMPEG *ffmpeg, AVStream *st, int idx) +FFStream::FFStream(FFMPEG *ffmpeg, AVStream *st, int fidx) { this->ffmpeg = ffmpeg; this->st = st; - this->idx = idx; + this->fidx = fidx; frm_lock = new Mutex("FFStream::frm_lock"); fmt_ctx = 0; filter_graph = 0; @@ -238,10 +242,12 @@ FFStream::FFStream(FFMPEG *ffmpeg, AVStream *st, int idx) buffersink_ctx = 0; frm_count = 0; nudge = AV_NOPTS_VALUE; - eof = 0; + seek_pos = curr_pos = 0; + seeked = 1; eof = 0; + index_markers = 0; reading = writing = 0; - need_packet = 1; flushed = 0; + need_packet = 1; frame = fframe = 0; } @@ -303,7 +309,8 @@ int FFStream::decode_activate() ret = avformat_open_input(&fmt_ctx, ffmpeg->fmt_ctx->filename, NULL, &copts); if( ret >= 0 ) { ret = avformat_find_stream_info(fmt_ctx, 0); - st = fmt_ctx->streams[idx]; + st = fmt_ctx->streams[fidx]; + load_markers(); } if( ret >= 0 ) { AVCodecID codec_id = st->codec->codec_id; @@ -326,35 +333,38 @@ int FFStream::read_packet() { av_packet_unref(ipkt); int ret = av_read_frame(fmt_ctx, ipkt); - if( ret >= 0 ) return 1; - st_eof(1); - if( ret == AVERROR_EOF ) return 0; - ff_err(ret, "FFStream::read_packet: av_read_frame failed\n"); - flushed = 1; - return -1; + if( ret < 0 ) { + st_eof(1); + if( ret == AVERROR_EOF ) { + ipkt->stream_index = st->index; + return 0; + } + ff_err(ret, "FFStream::read_packet: av_read_frame failed\n"); + flushed = 1; + return -1; + } + return 1; } int FFStream::decode(AVFrame *frame) { int ret = 0; - int retries = 100; + int retries = 1000; int got_frame = 0; while( ret >= 0 && !flushed && --retries >= 0 && !got_frame ) { if( need_packet ) { need_packet = 0; - ret = read_packet(); - if( ret < 0 ) break; - if( !ret ) ipkt->stream_index = st->index; + if( (ret=read_packet()) < 0 ) break; } if( ipkt->stream_index == st->index ) { while( (ipkt->size > 0 || !ipkt->data) && !got_frame ) { - ret = decode_frame(frame, got_frame); - if( ret < 0 || !ipkt->data ) break; + ret = decode_frame(ipkt, frame, got_frame); + if( ret <= 0 || !ipkt->data ) break; ipkt->data += ret; ipkt->size -= ret; } - retries = 100; + retries = 1000; } if( !got_frame ) { need_packet = 1; @@ -434,13 +444,57 @@ int FFStream::flush() return ret >= 0 ? 0 : 1; } -FFAudioStream::FFAudioStream(FFMPEG *ffmpeg, AVStream *strm, int idx) - : FFStream(ffmpeg, strm, idx) +int FFStream::seek(int64_t no, double rate) +{ + if( no < 0 ) no = 0; +// default ffmpeg native seek + int npkts = 1; + int64_t pos = no, plmt = -1; + if( index_markers && index_markers->size() > 1 ) { + IndexMarks &marks = *index_markers; + int i = marks.find(pos); + int64_t n = i < 0 ? (i=0) : marks[i].no; +// if indexed seek point not too far away (<30 secs), use index + if( no-n < 30*rate ) { + if( n < 0 ) n = 0; + pos = n; + plmt = marks[i].pos; + npkts = 1000; + } + } + double secs = pos / rate; + int64_t tstmp = secs * st->time_base.den / st->time_base.num; + if( nudge != AV_NOPTS_VALUE ) tstmp += nudge; + if( avformat_seek_file(fmt_ctx, st->index, + -INT64_MAX, tstmp, INT64_MAX, AVSEEK_FLAG_ANY) < 0 ) return -1; + avcodec_flush_buffers(st->codec); + need_packet = 0; flushed = 0; + seeked = 1; st_eof(0); + int64_t pkt_ts = AV_NOPTS_VALUE; + int ret = 1, retry = 1000; + +// read up to retry packets, limited to npkts in stream, and not past pkt.pos plmt + while( ret > 0 && npkts > 0 && --retry >= 0 ) { + if( (ret=read_packet()) <= 0 ) break; + if( ipkt->stream_index != st->index ) continue; + if( (pkt_ts=ipkt->dts) == AV_NOPTS_VALUE ) pkt_ts = ipkt->pts; + if( pkt_ts != AV_NOPTS_VALUE && pkt_ts >= tstmp ) break; + if( plmt >= 0 && ipkt->pos >= plmt ) break; + --npkts; + } + + if( ret <= 0 || retry < 0 ) return -1; + seek_pos = curr_pos = pos; + return npkts > 0 ? 1 : 0; +} + +FFAudioStream::FFAudioStream(FFMPEG *ffmpeg, AVStream *strm, int idx, int fidx) + : FFStream(ffmpeg, strm, fidx) { + this->idx = idx; channel0 = channels = 0; sample_rate = 0; mbsz = 0; - seek_pos = curr_pos = 0; length = 0; resample_context = 0; @@ -452,8 +506,8 @@ FFAudioStream::FFAudioStream(FFMPEG *ffmpeg, AVStream *strm, int idx) sz = 0x10000; long bsz = sz * nch; bfr = new float[bsz]; - inp = outp = bfr; lmt = bfr + bsz; + reset_history(); } FFAudioStream::~FFAudioStream() @@ -463,9 +517,9 @@ FFAudioStream::~FFAudioStream() delete [] bfr; } -int FFAudioStream::load_history(uint8_t **data, int len) +int FFAudioStream::get_samples(float *&samples, uint8_t **data, int len) { - float *samples = *(float **)data; + samples = *(float **)data; if( resample_context ) { if( len > aud_bfr_sz ) { delete [] aud_bfr; @@ -478,25 +532,41 @@ 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 ) { - ff_err(ret, "FFAudioStream::load_history: swr_convert failed\n"); + ff_err(ret, "FFAudioStream::get_samples: swr_convert failed\n"); return -1; } samples = aud_bfr; len = ret; } - // biggest user bfr since seek + frame - realloc(mbsz + len + 1, channels); - write(samples, len); return len; } -int FFAudioStream::decode_frame(AVFrame *frame, int &got_frame) +int FFAudioStream::load_history(uint8_t **data, int len) { - int ret = avcodec_decode_audio4(st->codec, frame, &got_frame, ipkt); + float *samples; + len = get_samples(samples, data, len); + if( len > 0 ) { + // biggest user bfr since seek + frame + realloc(mbsz + len + 1, channels); + write(samples, len); + } + return len; +} + +int FFAudioStream::decode_frame(AVPacket *pkt, AVFrame *frame, int &got_frame) +{ + int first_frame = seeked; seeked = 0; + int ret = avcodec_decode_audio4(st->codec, frame, &got_frame, pkt); if( ret < 0 ) { + if( first_frame ) return 0; ff_err(ret, "FFAudioStream::decode_frame: Could not read audio frame\n"); return -1; } + if( got_frame ) { + int64_t pkt_ts = av_frame_get_best_effort_timestamp(frame); + if( pkt_ts != AV_NOPTS_VALUE ) + curr_pos = ffmpeg->to_secs(pkt_ts - nudge, st->time_base) * sample_rate + 0.5; + } return ret; } @@ -513,7 +583,7 @@ int FFAudioStream::nb_samples() { AVCodecContext *ctx = st->codec; return ctx->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE ? - 10000 : ctx->frame_size; + 10000 : ctx->frame_size; } int64_t FFAudioStream::load_buffer(double ** const sp, int len) @@ -527,7 +597,7 @@ int64_t FFAudioStream::load_buffer(double ** const sp, int len) int FFAudioStream::in_history(int64_t pos) { if( pos > curr_pos ) return 0; - int64_t len = curr_pos - seek_pos; + int64_t len = hpos; if( len > sz ) len = sz; if( pos < curr_pos - len ) return 0; return 1; @@ -550,13 +620,13 @@ int FFAudioStream::init_frame(AVFrame *frame) int FFAudioStream::load(int64_t pos, int len) { if( audio_seek(pos) < 0 ) return -1; - if( mbsz < len ) mbsz = len; - int ret = 0; - int64_t end_pos = pos + len; if( !frame && !(frame=av_frame_alloc()) ) { fprintf(stderr, "FFAudioStream::load: av_frame_alloc failed\n"); return -1; } + if( mbsz < len ) mbsz = len; + int64_t end_pos = pos + len; + int ret = 0; for( int i=0; ret>=0 && !flushed && curr_pos 0 ) { @@ -568,26 +638,20 @@ int FFAudioStream::load(int64_t pos, int len) zero(end_pos - curr_pos); curr_pos = end_pos; } - return curr_pos - pos; + len = curr_pos - pos; + iseek(len); + return len; } int FFAudioStream::audio_seek(int64_t pos) { if( decode_activate() < 0 ) return -1; - if( in_history(pos) ) { - iseek(curr_pos - 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; - reset(); st_eof(0); - mbsz = 0; flushed = 0; need_packet = 1; + if( in_history(pos) ) return 0; + if( pos == curr_pos ) return 0; + reset_history(); mbsz = 0; +// guarentee preload > 1sec samples + if( seek(pos-sample_rate, sample_rate) < 0 ) return -1; return 1; } @@ -621,7 +685,7 @@ int FFAudioStream::encode(double **samples, int len) return ret >= 0 ? 0 : 1; } -int FFAudioStream::encode_frame(FFPacket &pkt, AVFrame *frame, int &got_packet) +int FFAudioStream::encode_frame(AVPacket *pkt, AVFrame *frame, int &got_packet) { int ret = avcodec_encode_audio2(st->codec, pkt, frame, &got_packet); if( ret < 0 ) { @@ -631,13 +695,20 @@ int FFAudioStream::encode_frame(FFPacket &pkt, AVFrame *frame, int &got_packet) return ret; } -FFVideoStream::FFVideoStream(FFMPEG *ffmpeg, AVStream *strm, int idx) - : FFStream(ffmpeg, strm, idx) +void FFAudioStream::load_markers() +{ + IndexState *index_state = ffmpeg->file_base->asset->index_state; + if( !index_state || idx >= index_state->audio_markers.size() ) return; + FFStream::load_markers(*index_state->audio_markers[idx], sample_rate); +} + +FFVideoStream::FFVideoStream(FFMPEG *ffmpeg, AVStream *strm, int idx, int fidx) + : FFStream(ffmpeg, strm, fidx) { + this->idx = idx; width = height = 0; frame_rate = 0; aspect_ratio = 0; - seek_pos = curr_pos = 0; length = 0; convert_ctx = 0; } @@ -647,15 +718,20 @@ FFVideoStream::~FFVideoStream() if( convert_ctx ) sws_freeContext(convert_ctx); } -int FFVideoStream::decode_frame(AVFrame *frame, int &got_frame) +int FFVideoStream::decode_frame(AVPacket *pkt, AVFrame *frame, int &got_frame) { - int ret = avcodec_decode_video2(st->codec, frame, &got_frame, ipkt); + int first_frame = seeked; seeked = 0; + int ret = avcodec_decode_video2(st->codec, frame, &got_frame, pkt); if( ret < 0 ) { + if( first_frame ) return 0; ff_err(ret, "FFVideoStream::decode_frame: Could not read video frame\n"); return -1; } - if( got_frame ) - ++curr_pos; + if( got_frame ) { + int64_t pkt_ts = av_frame_get_best_effort_timestamp(frame); + if( pkt_ts != AV_NOPTS_VALUE ) + curr_pos = ffmpeg->to_secs(pkt_ts - nudge, st->time_base) * frame_rate + 0.5; + } return ret; } @@ -669,6 +745,7 @@ int FFVideoStream::load(VFrame *vframe, int64_t pos) } for( int i=0; ret>=0 && !flushed && curr_pos<=pos && i<1000; ++i ) { ret = read_frame(frame); + if( ret > 0 ) ++curr_pos; } if( ret > 0 ) { AVCodecContext *ctx = st->codec; @@ -682,24 +759,16 @@ int FFVideoStream::load(VFrame *vframe, int64_t pos) int FFVideoStream::video_seek(int64_t pos) { if( decode_activate() < 0 ) return -1; + if( !st->codec || !st->codec->codec ) return -1; + if( pos == curr_pos-1 && !seeked ) return 0; // if close enough, just read up to current -// 3*gop_size seems excessive, but less causes tears - int gop = 3*st->codec->gop_size; + int gop = st->codec->gop_size; 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; - 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; - st_eof(0); - flushed = 0; need_packet = 1; + int read_limit = curr_pos + 3*gop; + if( pos >= curr_pos && pos <= read_limit ) return 0; +// guarentee preload more than 2*gop frames + if( seek(pos - 3*gop, frame_rate) < 0 ) return -1; return 1; } @@ -737,7 +806,7 @@ int FFVideoStream::encode(VFrame *vframe) return ret >= 0 ? 0 : 1; } -int FFVideoStream::encode_frame(FFPacket &pkt, AVFrame *frame, int &got_packet) +int FFVideoStream::encode_frame(AVPacket *pkt, AVFrame *frame, int &got_packet) { int ret = avcodec_encode_video2(st->codec, pkt, frame, &got_packet); if( ret < 0 ) { @@ -911,6 +980,13 @@ int FFVideoStream::convert_pixfmt(VFrame *frame, return 0; } +void FFVideoStream::load_markers() +{ + IndexState *index_state = ffmpeg->file_base->asset->index_state; + if( idx >= index_state->video_markers.size() ) return; + FFStream::load_markers(*index_state->video_markers[idx], frame_rate); +} + FFMPEG::FFMPEG(FileBase *file_base) { @@ -938,7 +1014,7 @@ FFMPEG::~FFMPEG() close_encoder(); ffaudio.remove_all_objects(); ffvideo.remove_all_objects(); - if( encoding ) avformat_free_context(fmt_ctx); + if( fmt_ctx ) avformat_close_input(&fmt_ctx); ff_unlock(); delete flow_lock; delete mux_lock; @@ -1216,51 +1292,76 @@ double FFMPEG::to_secs(int64_t time, AVRational time_base) int FFMPEG::info(char *text, int len) { if( len <= 0 ) return 0; + decode_activate(); #define report(s...) do { int n = snprintf(cp,len,s); cp += n; len -= n; } while(0) char *cp = text; - for( int i=0; i<(int)fmt_ctx->nb_streams; ++i ) { - AVStream *st = fmt_ctx->streams[i]; + if( ffvideo.size() > 0 ) + report("\n%d video stream%s\n",ffvideo.size(), ffvideo.size()!=1 ? "s" : ""); + for( int vidx=0; vidxst; AVCodecContext *avctx = st->codec; - report(_("stream %d, id 0x%06x:\n"), i, avctx->codec_id); + report(_("vid%d (%d), id 0x%06x:\n"), vid->idx, vid->fidx, 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); - double frame_rate = !framerate.den ? 0 : (double)framerate.num / framerate.den; - report(" video %s",desc ? desc->name : " (unkn)"); - report(" %dx%d %5.2f", avctx->width, avctx->height, frame_rate); - const char *pfn = av_get_pix_fmt_name(avctx->pix_fmt); - report(" pix %s\n", pfn ? pfn : "(unkn)"); - double secs = to_secs(st->duration, st->time_base); - int64_t length = secs * frame_rate + 0.5; - report(" %jd frms %0.2f secs", length, secs); - int hrs = secs/3600; secs -= hrs*3600; - int mins = secs/60; secs -= mins*60; - report(" %d:%02d:%05.2f\n", hrs, mins, secs); - - } - else if( avctx->codec_type == AVMEDIA_TYPE_AUDIO ) { - int sample_rate = avctx->sample_rate; - const char *fmt = av_get_sample_fmt_name(avctx->sample_fmt); - report(" audio %s",desc ? desc->name : " (unkn)"); - report(" %dch %s %d",avctx->channels, fmt, sample_rate); - int sample_bits = av_get_bits_per_sample(avctx->codec_id); - report(" %dbits\n", sample_bits); - double secs = to_secs(st->duration, st->time_base); - int64_t length = secs * sample_rate + 0.5; - report(" %jd smpl %0.2f secs", length, secs); - int hrs = secs/3600; secs -= hrs*3600; - int mins = secs/60; secs -= mins*60; - report(" %d:%02d:%05.2f\n", hrs, mins, secs); - } - else - report(_(" codec_type unknown\n")); - } - report("\n"); + report(" video%d %s", vidx+1, desc ? desc->name : " (unkn)"); + report(" %dx%d %5.2f", vid->width, vid->height, vid->frame_rate); + const char *pfn = av_get_pix_fmt_name(avctx->pix_fmt); + report(" pix %s\n", pfn ? pfn : "(unkn)"); + double secs = to_secs(st->duration, st->time_base); + int64_t length = secs * vid->frame_rate + 0.5; + double ofs = to_secs((vid->nudge - st->start_time), st->time_base); + int64_t nudge = ofs * vid->frame_rate; + int ch = nudge >= 0 ? '+' : (nudge=-nudge, '-'); + report(" %jd%c%jd frms %0.2f secs", length,ch,nudge, secs); + int hrs = secs/3600; secs -= hrs*3600; + int mins = secs/60; secs -= mins*60; + report(" %d:%02d:%05.2f\n", hrs, mins, secs); + } + if( ffaudio.size() > 0 ) + report("\n%d audio stream%s\n",ffaudio.size(), ffaudio.size()!=1 ? "s" : ""); + for( int aidx=0; aidxst; + AVCodecContext *avctx = st->codec; + report(_("aud%d (%d), id 0x%06x:\n"), aud->idx, aud->fidx, avctx->codec_id); + const AVCodecDescriptor *desc = avcodec_descriptor_get(avctx->codec_id); + int nch = aud->channels, ch0 = aud->channel0+1; + report(" audio%d-%d %s", ch0, ch0+nch-1, desc ? desc->name : " (unkn)"); + const char *fmt = av_get_sample_fmt_name(avctx->sample_fmt); + report(" %s %d", fmt, aud->sample_rate); + int sample_bits = av_get_bits_per_sample(avctx->codec_id); + report(" %dbits\n", sample_bits); + double secs = to_secs(st->duration, st->time_base); + int64_t length = secs * aud->sample_rate + 0.5; + double ofs = to_secs((aud->nudge - st->start_time), st->time_base); + int64_t nudge = ofs * aud->sample_rate; + int ch = nudge >= 0 ? '+' : (nudge=-nudge, '-'); + report(" %jd%c%jd smpl %0.2f secs", length,ch,nudge, secs); + int hrs = secs/3600; secs -= hrs*3600; + int mins = secs/60; secs -= mins*60; + report(" %d:%02d:%05.2f\n", hrs, mins, secs); + } + if( fmt_ctx->nb_programs > 0 ) + report("\n%d program%s\n",fmt_ctx->nb_programs, fmt_ctx->nb_programs!=1 ? "s" : ""); for( int i=0; i<(int)fmt_ctx->nb_programs; ++i ) { report("program %d", i+1); AVProgram *pgrm = fmt_ctx->programs[i]; - for( int j=0; j<(int)pgrm->nb_stream_indexes; ++j ) - report(", %d", pgrm->stream_index[j]); + for( int j=0; j<(int)pgrm->nb_stream_indexes; ++j ) { + int idx = pgrm->stream_index[j]; + int vidx = ffvideo.size(); + while( --vidx>=0 && ffvideo[vidx]->fidx != idx ); + if( vidx >= 0 ) { + report(", vid%d", vidx); + continue; + } + int aidx = ffaudio.size(); + while( --aidx>=0 && ffaudio[aidx]->fidx != idx ); + if( aidx >= 0 ) { + report(", aud%d", aidx); + continue; + } + report(", (%d)", pgrm->stream_index[j]); + } report("\n"); } report("\n"); @@ -1347,8 +1448,8 @@ int FFMPEG::open_decoder() AVRational framerate = av_guess_frame_rate(fmt_ctx, st, 0); if( framerate.num < 1 ) continue; has_video = 1; - FFVideoStream *vid = new FFVideoStream(this, st, i); int vidx = ffvideo.size(); + FFVideoStream *vid = new FFVideoStream(this, st, vidx, i); vstrm_index.append(ffidx(vidx, 0)); ffvideo.append(vid); vid->width = avctx->width; @@ -1366,8 +1467,8 @@ int FFMPEG::open_decoder() if( avctx->channels < 1 ) continue; if( avctx->sample_rate < 1 ) continue; has_audio = 1; - FFAudioStream *aud = new FFAudioStream(this, st, i); int aidx = ffaudio.size(); + FFAudioStream *aud = new FFAudioStream(this, st, aidx, i); ffaudio.append(aud); aud->channel0 = astrm_index.size(); aud->channels = avctx->channels; @@ -1507,8 +1608,8 @@ int FFMPEG::open_encoder(const char *type, const char *spec) av_dict_set(&sopts, "b", arg, 0); } int aidx = ffaudio.size(); - int idx = aidx + ffvideo.size(); - FFAudioStream *aud = new FFAudioStream(this, st, idx); + int fidx = aidx + ffvideo.size(); + FFAudioStream *aud = new FFAudioStream(this, st, aidx, fidx); ffaudio.append(aud); fst = aud; aud->sample_rate = asset->sample_rate; ctx->channels = aud->channels = asset->channels; @@ -1566,8 +1667,8 @@ int FFMPEG::open_encoder(const char *type, const char *spec) av_dict_set(&sopts, "global_quality", arg, 0); } int vidx = ffvideo.size(); - int idx = vidx + ffaudio.size(); - FFVideoStream *vid = new FFVideoStream(this, st, idx); + int fidx = vidx + ffaudio.size(); + FFVideoStream *vid = new FFVideoStream(this, st, vidx, fidx); vstrm_index.append(ffidx(vidx, 0)); ffvideo.append(vid); fst = vid; vid->width = asset->width; @@ -1634,80 +1735,89 @@ int FFMPEG::decode_activate() { if( decoding < 0 ) { decoding = 0; + for( int vidx=0; vidxnudge = AV_NOPTS_VALUE; + for( int aidx=0; aidxnudge = AV_NOPTS_VALUE; + // set nudges for each program stream set int npgrms = fmt_ctx->nb_programs; for( int i=0; iprograms[i]; // first start time video stream - int64_t vstart_time = -1; + int64_t vstart_time = -1, astart_time = -1; for( int j=0; j<(int)pgrm->nb_stream_indexes; ++j ) { - int st_idx = pgrm->stream_index[j]; - AVStream *st = fmt_ctx->streams[st_idx]; + 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; - break; + continue; } - } - // max start time audio stream - int64_t astart_time = -1; - for( int j=0; j<(int)pgrm->nb_stream_indexes; ++j ) { - int st_idx = pgrm->stream_index[j]; - AVStream *st = fmt_ctx->streams[st_idx]; - AVCodecContext *avctx = st->codec; if( avctx->codec_type == AVMEDIA_TYPE_VIDEO ) { if( st->start_time == AV_NOPTS_VALUE ) continue; if( astart_time > st->start_time ) continue; astart_time = st->start_time; + continue; } } - if( astart_time < 0 || vstart_time < 0 ) continue; // match program streams to max start_time int64_t nudge = vstart_time > astart_time ? vstart_time : astart_time; for( int j=0; j<(int)pgrm->nb_stream_indexes; ++j ) { - int st_idx = pgrm->stream_index[j]; - AVStream *st = fmt_ctx->streams[st_idx]; + int fidx = pgrm->stream_index[j]; + AVStream *st = fmt_ctx->streams[fidx]; AVCodecContext *avctx = st->codec; - if( avctx->codec_type == AVMEDIA_TYPE_AUDIO ) { - for( int k=0; kidx == st_idx ) - ffaudio[k]->nudge = nudge; + if( avctx->codec_type == AVMEDIA_TYPE_VIDEO ) { + for( int k=0; kfidx != fidx ) continue; + ffvideo[k]->nudge = nudge; } + continue; } - else if( avctx->codec_type == AVMEDIA_TYPE_VIDEO ) { - for( int k=0; kidx == st_idx ) - ffvideo[k]->nudge = nudge; + if( avctx->codec_type == AVMEDIA_TYPE_AUDIO ) { + for( int k=0; kfidx != fidx ) continue; + ffaudio[k]->nudge = nudge; } + continue; } } } + // set nudges for any streams not yet set int64_t vstart_time = 0, astart_time = 0; int nstreams = fmt_ctx->nb_streams; for( int i=0; istreams[i]; AVCodecContext *avctx = st->codec; switch( avctx->codec_type ) { - case AVMEDIA_TYPE_VIDEO: + case AVMEDIA_TYPE_VIDEO: { 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( vstart_time >= st->start_time ) continue; vstart_time = st->start_time; - break; - case AVMEDIA_TYPE_AUDIO: + 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; + break; } default: break; } } int64_t nudge = vstart_time > astart_time ? vstart_time : astart_time; - for( int k=0; knudge != AV_NOPTS_VALUE ) continue; - ffvideo[k]->nudge = nudge; + for( int vidx=0; vidxnudge != AV_NOPTS_VALUE ) continue; + ffvideo[vidx]->nudge = nudge; } - for( int k=0; knudge != AV_NOPTS_VALUE ) continue; - ffaudio[k]->nudge = nudge; + for( int aidx=0; aidxnudge != AV_NOPTS_VALUE ) continue; + ffaudio[aidx]->nudge = nudge; } decoding = 1; } @@ -1742,12 +1852,13 @@ int FFMPEG::encode_activate() return encoding; } + int FFMPEG::audio_seek(int stream, int64_t pos) { int aidx = astrm_index[stream].st_idx; FFAudioStream *aud = ffaudio[aidx]; + pos = pos * aud->sample_rate / file_base->asset->sample_rate + 0.5; aud->audio_seek(pos); - aud->seek_pos = aud->curr_pos = pos; return 0; } @@ -1755,8 +1866,8 @@ int FFMPEG::video_seek(int stream, int64_t pos) { int vidx = vstrm_index[stream].st_idx; FFVideoStream *vid = ffvideo[vidx]; + pos = pos * vid->frame_rate / file_base->asset->frame_rate + 0.5; vid->video_seek(pos); - vid->seek_pos = vid->curr_pos = pos; return 0; } @@ -1766,9 +1877,11 @@ int FFMPEG::decode(int chn, int64_t pos, double *samples, int len) if( !has_audio || chn >= astrm_index.size() ) return -1; int aidx = astrm_index[chn].st_idx; FFAudioStream *aud = ffaudio[aidx]; + pos = pos * aud->sample_rate / file_base->asset->sample_rate + 0.5; if( aud->load(pos, len) < len ) return -1; int ch = astrm_index[chn].st_ch; - return aud->read(samples,len,ch); + int ret = aud->read(samples,len,ch); + return ret; } int FFMPEG::decode(int layer, int64_t pos, VFrame *vframe) @@ -1776,9 +1889,11 @@ int FFMPEG::decode(int layer, int64_t pos, VFrame *vframe) if( !has_video || layer >= vstrm_index.size() ) return -1; int vidx = vstrm_index[layer].st_idx; FFVideoStream *vid = ffvideo[vidx]; + pos = pos * vid->frame_rate / file_base->asset->frame_rate + 0.5; return vid->load(vframe, pos); } + int FFMPEG::encode(int stream, double **samples, int len) { FFAudioStream *aud = ffaudio[stream]; @@ -1967,7 +2082,7 @@ int FFMPEG::ff_audio_for_video(int vstream, int astream, int64_t &channel_mask) { channel_mask = 0; int pidx = -1; - int vidx = ffvideo[vstream]->idx; + int vidx = ffvideo[vstream]->fidx; // find first program with this video stream for( int i=0; pidx<0 && i<(int)fmt_ctx->nb_programs; ++i ) { AVProgram *pgrm = fmt_ctx->programs[i]; @@ -1989,7 +2104,7 @@ int FFMPEG::ff_audio_for_video(int vstream, int astream, int64_t &channel_mask) if( astream > 0 ) { --astream; continue; } int astrm = -1; for( int i=0; astrm<0 && iidx == aidx ) astrm = i; + if( ffaudio[i]->fidx == aidx ) astrm = i; if( astrm >= 0 ) { if( ret < 0 ) ret = astrm; int64_t mask = (1 << ffaudio[astrm]->channels) - 1; @@ -2229,3 +2344,148 @@ int FFStream::bs_filter(AVPacket *pkt) return ret; } +int FFMPEG::scan(IndexState *index_state, int64_t *scan_position, int *canceled) +{ + AVPacket pkt; + av_init_packet(&pkt); + AVFrame *frame = av_frame_alloc(); + if( !frame ) { + fprintf(stderr, "FFMPEG::scan: av_frame_alloc failed\n"); + return -1; + } + + index_state->add_video_markers(ffvideo.size()); + index_state->add_audio_markers(ffaudio.size()); + + for( int i=0; i<(int)fmt_ctx->nb_streams; ++i ) { + AVDictionary *copts = 0; + av_dict_copy(&copts, opts, 0); + 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"); + av_dict_free(&copts); + } + int errs = 0; + for( int64_t count=0; !*canceled; ++count ) { + av_packet_unref(&pkt); + pkt.data = 0; pkt.size = 0; + + int ret = av_read_frame(fmt_ctx, &pkt); + if( ret < 0 ) { + if( ret == AVERROR_EOF ) break; + if( ++errs > 100 ) { + ff_err(ret, "over 100 read_frame errs\n"); + break; + } + continue; + } + if( !pkt.data ) continue; + int i = pkt.stream_index; + if( i < 0 || i >= (int)fmt_ctx->nb_streams ) continue; + AVStream *st = fmt_ctx->streams[i]; + AVCodecContext *avctx = st->codec; + if( pkt.pos > *scan_position ) *scan_position = pkt.pos; + + switch( avctx->codec_type ) { + case AVMEDIA_TYPE_VIDEO: { + int vidx = ffvideo.size(); + while( --vidx>=0 && ffvideo[vidx]->fidx != i ); + if( vidx < 0 ) break; + FFVideoStream *vid = ffvideo[vidx]; + 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 ) { + if( vid->nudge != AV_NOPTS_VALUE ) tstmp -= vid->nudge; + double secs = to_secs(tstmp, st->time_base); + int64_t frm = secs * vid->frame_rate + 0.5; + if( frm < 0 ) frm = 0; + index_state->put_video_mark(vidx, frm, pkt.pos); + } +#if 0 + while( pkt.size > 0 ) { + av_frame_unref(frame); + int got_frame = 0; + int ret = vid->decode_frame(&pkt, frame, got_frame); + if( ret <= 0 ) break; +// if( got_frame ) {} + pkt.data += ret; + pkt.size -= ret; + } +#endif + break; } + case AVMEDIA_TYPE_AUDIO: { + int aidx = ffaudio.size(); + while( --aidx>=0 && ffaudio[aidx]->fidx != i ); + if( aidx < 0 ) break; + FFAudioStream *aud = ffaudio[aidx]; + 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 ) { + 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); + } + while( pkt.size > 0 ) { + int ch = aud->channel0, nch = aud->channels; + 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); + index_state->pad_data(ch, nch, aud->curr_pos); + } + av_frame_unref(frame); + int got_frame = 0; + int ret = aud->decode_frame(&pkt, frame, got_frame); + if( ret <= 0 ) break; + if( got_frame ) { + 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; + } + pkt.data += ret; + pkt.size -= ret; + } + break; } + default: break; + } + } + av_frame_free(&frame); + return 0; +} + +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; + int nb_ent = st->nb_index_entries; +// some formats already have an index + if( nb_ent > 0 ) { + AVIndexEntry *ep = &st->index_entries[nb_ent-1]; + int64_t tstmp = ep->timestamp; + if( nudge != AV_NOPTS_VALUE ) tstmp -= nudge; + double secs = ffmpeg->to_secs(tstmp, st->time_base); + int64_t no = secs * rate; + while( in < sz && marks[in].no <= no ) ++in; + } + int64_t len = sz - in; + int64_t count = max_entries - nb_ent; + if( count > len ) count = len; + for( int i=0; itime_base.den / st->time_base.num; + if( nudge != AV_NOPTS_VALUE ) tstmp += nudge; + av_add_index_entry(st, pos, tstmp, 0, 0, AVINDEX_KEYFRAME); + } +} + diff --git a/cinelerra-5.0/cinelerra/ffmpeg.h b/cinelerra-5.0/cinelerra/ffmpeg.h index 8400751b..92fabfcd 100644 --- a/cinelerra-5.0/cinelerra/ffmpeg.h +++ b/cinelerra-5.0/cinelerra/ffmpeg.h @@ -17,6 +17,7 @@ #include "ffmpeg.inc" #include "filebase.inc" #include "fileffmpeg.inc" +#include "indexstate.inc" #include "mutex.h" #include "thread.h" #include "vframe.inc" @@ -67,7 +68,7 @@ public: class FFStream { public: - FFStream(FFMPEG *ffmpeg, AVStream *st, int idx); + FFStream(FFMPEG *ffmpeg, AVStream *st, int fidx); ~FFStream(); static void ff_lock(const char *cp=0); static void ff_unlock(); @@ -77,15 +78,20 @@ public: virtual int encode_activate(); virtual int decode_activate(); int read_packet(); + int seek(int64_t no, double rate); int write_packet(FFPacket &pkt); int flush(); int decode(AVFrame *frame); + void load_markers(IndexMarks &marks, double rate); - virtual int decode_frame(AVFrame *frame, int &got_frame) = 0; - virtual int encode_frame(FFPacket &pkt, AVFrame *frame, int &got_frame) = 0; + virtual int is_audio() = 0; + virtual int is_video() = 0; + virtual int decode_frame(AVPacket *pkt, AVFrame *frame, int &got_frame) = 0; + virtual int encode_frame(AVPacket *pkt, AVFrame *frame, int &got_frame) = 0; virtual int init_frame(AVFrame *frame) = 0; virtual int create_filter(const char *filter_spec, AVCodecContext *src_ctx, AVCodecContext *sink_ctx) = 0; + virtual void load_markers() = 0; int create_filter(const char *filter_spec); int load_filter(AVFrame *frame); int read_filter(AVFrame *frame); @@ -123,11 +129,13 @@ public: int frm_count; List frms; Mutex *frm_lock; + IndexMarks *index_markers; int64_t nudge; - int idx; + int64_t seek_pos, curr_pos; + int fidx; int reading, writing; - int eof; + int seeked, eof; int st_eof() { return eof; } void st_eof(int v) { eof = v; } @@ -135,16 +143,15 @@ public: class FFAudioStream : public FFStream { float *inp, *outp, *bfr, *lmt; - long sz; + int64_t hpos, sz; int nch; int read(float *fp, long len); - void realloc(long sz, int nch, long len); - void realloc(long sz, int nch); - void reserve(long sz, int nch); + void realloc(long nsz, int nch, long len); + void realloc(long nsz, int nch); + void reserve(long nsz, int nch); long used(); long avail(); - void reset(); void iseek(int64_t ofs); float *get_outp(int len); int64_t put_inp(int len); @@ -152,18 +159,23 @@ class FFAudioStream : public FFStream { int zero(long len); int write(const double *dp, long len, int ch); public: - FFAudioStream(FFMPEG *ffmpeg, AVStream *strm, int idx); + FFAudioStream(FFMPEG *ffmpeg, AVStream *strm, int idx, int fidx); virtual ~FFAudioStream(); + int is_audio() { return 1; } + int is_video() { return 0; } + int get_samples(float *&samples, uint8_t **data, int len); int load_history(uint8_t **data, int len); - int decode_frame(AVFrame *frame, int &got_frame); - int encode_frame(FFPacket &pkt, AVFrame *frame, int &got_frame); + int decode_frame(AVPacket *pkt, AVFrame *frame, int &got_frame); + int encode_frame(AVPacket *pkt, AVFrame *frame, int &got_frame); int create_filter(const char *filter_spec, AVCodecContext *src_ctx, AVCodecContext *sink_ctx); + void load_markers(); int encode_activate(); int nb_samples(); int64_t load_buffer(double ** const sp, int len); int in_history(int64_t pos); + void reset_history(); int read(double *dp, long len, int ch); int init_frame(AVFrame *frame); @@ -171,10 +183,10 @@ public: int audio_seek(int64_t pos); int encode(double **samples, int len); + int idx; int channel0, channels; int sample_rate; int mbsz, frame_sz; - int64_t seek_pos, curr_pos; int64_t length; SwrContext *resample_context; @@ -184,27 +196,31 @@ public: class FFVideoStream : public FFStream { public: - FFVideoStream(FFMPEG *ffmpeg, AVStream *strm, int idx); + FFVideoStream(FFMPEG *ffmpeg, AVStream *strm, int idx, int fidx); virtual ~FFVideoStream(); - int decode_frame(AVFrame *frame, int &got_frame); - int encode_frame(FFPacket &pkt, AVFrame *frame, int &got_frame); + int is_audio() { return 0; } + int is_video() { return 1; } + int decode_frame(AVPacket *pkt, AVFrame *frame, int &got_frame); + int encode_frame(AVPacket *pkt, AVFrame *frame, int &got_frame); int create_filter(const char *filter_spec, AVCodecContext *src_ctx, AVCodecContext *sink_ctx); + void load_markers(); int init_frame(AVFrame *picture); int load(VFrame *vframe, int64_t pos); int video_seek(int64_t pos); int encode(VFrame *vframe); + int idx; double frame_rate; int width, height; - int64_t seek_pos, curr_pos; int64_t length; float aspect_ratio; struct SwsContext *convert_ctx; uint8_t *pkt_bfr; int pkt_bfr_sz; + int64_t start_pts; static PixelFormat color_model_to_pix_fmt(int color_model); static int pix_fmt_to_color_model(PixelFormat pix_fmt); @@ -282,7 +298,7 @@ public: uint16_t st_idx, st_ch; ffidx() { st_idx = st_ch = 0; } ffidx(const ffidx &t) { st_idx = t.st_idx; st_ch = t.st_ch; } - ffidx(uint16_t idx, uint16_t ch) { st_idx = idx; st_ch = ch; } + ffidx(uint16_t fidx, uint16_t ch) { st_idx = fidx; st_ch = ch; } }; ArrayList astrm_index; @@ -306,6 +322,7 @@ public: FFMPEG(FileBase *file_base=0); ~FFMPEG(); + int scan(IndexState *index_state, int64_t *scan_position, int *canceled); int ff_audio_stream(int channel) { return astrm_index[channel].st_idx; } int ff_video_stream(int layer) { return vstrm_index[layer].st_idx; } diff --git a/cinelerra-5.0/cinelerra/fileac3.C b/cinelerra-5.0/cinelerra/fileac3.C index 44cc2610..a072e9c0 100644 --- a/cinelerra-5.0/cinelerra/fileac3.C +++ b/cinelerra-5.0/cinelerra/fileac3.C @@ -44,7 +44,6 @@ extern "C" #include "file.h" #include "filempeg.h" #include "language.h" -#include "mwindow.inc" #include "mainerror.h" @@ -298,10 +297,7 @@ int FileAC3::write_samples(double **buffer, int64_t len) int got_packet = 0; ret = avcodec_encode_audio2(avctx, &avpkt, frame, &got_packet); if( !ret ) { - if(got_packet && avctx->coded_frame) { - avctx->coded_frame->pts = avpkt.pts; - avctx->coded_frame->key_frame = !!(avpkt.flags & AV_PKT_FLAG_KEY); - } + fprintf(stderr, "avcodec_encode_audio2 failed. \n%m\n"); } } av_packet_free_side_data(&avpkt); diff --git a/cinelerra-5.0/cinelerra/fileffmpeg.C b/cinelerra-5.0/cinelerra/fileffmpeg.C index 90c1a6ef..b195623e 100644 --- a/cinelerra-5.0/cinelerra/fileffmpeg.C +++ b/cinelerra-5.0/cinelerra/fileffmpeg.C @@ -9,13 +9,17 @@ #include "asset.h" #include "bcwindowbase.h" +#include "bcprogressbox.h" #include "bitspopup.h" #include "ffmpeg.h" #include "filebase.h" #include "file.h" #include "fileffmpeg.h" #include "filesystem.h" +#include "format.inc" +#include "indexfile.h" #include "mutex.h" +#include "preferences.h" #include "videodevice.inc" FileFFMPEG::FileFFMPEG(Asset *asset, File *file) @@ -220,6 +224,8 @@ int FileFFMPEG::open_file(int rd, int wr) if( !asset->video_length ) asset->video_length = ff->ff_video_frames(0); if( !asset->frame_rate ) asset->frame_rate = ff->ff_frame_rate(0); } + IndexState *index_state = asset->index_state; + index_state->read_markers(file->preferences->index_directory, asset->path); } } else if( wr ) { @@ -598,4 +604,87 @@ int FFMPEGConfigVideoToggle::handle_event() return 1; } +FFMPEGScanProgress::FFMPEGScanProgress(const char *title, int64_t length, int64_t *position, int *canceled) + : Thread(1, 0, 0) +{ + strcpy(this->progress_title, title); + this->length = length; + this->position = position; + this->canceled = canceled; + done = 0; + progress = 0; + start(); +} + +FFMPEGScanProgress::~FFMPEGScanProgress() +{ + done = 1; + cancel(); + join(); +} + +void FFMPEGScanProgress::run() +{ + BC_ProgressBox *progress = new BC_ProgressBox(-1, -1, progress_title, length); + progress->start(); + + struct timeval start_time, now, then; + gettimeofday(&start_time, 0); + then = start_time; + + while( !done ) { + if( progress->update(*position, 1) ) { + *canceled = done = 1; + break; + } + gettimeofday(&now, 0); + if(now.tv_sec - then.tv_sec >= 1) { + int64_t elapsed = now.tv_sec - start_time.tv_sec; + int64_t byte_rate = *position / elapsed; + int64_t eta = !byte_rate ? 0 : (length - *position) / byte_rate; + char string[BCTEXTLEN]; + sprintf(string, "%s\nETA: " _LD "m" _LD "s", + progress_title, eta / 60, eta % 60); + progress->update_title(string, 1); + then = now; + } + usleep(500000); + } + + progress->stop_progress(); + delete progress; +} + +int FileFFMPEG::get_index(char *index_path) +{ + if( !ff ) return -1; + if( !file->preferences->ffmpeg_marker_indecies ) return 1; + + IndexState *index_state = asset->index_state; + if( index_state->index_status != INDEX_NOTTESTED ) return 0; + index_state->index_status = INDEX_BUILDING; + + for( int aidx=0; aidxffaudio.size(); ++aidx ) { + FFAudioStream *aud = ff->ffaudio[aidx]; + index_state->add_audio_stream(aud->channels, aud->length); + } + + char progress_title[BCTEXTLEN]; + sprintf(progress_title, _("Creating %s\n"), index_path); + FileSystem fs; + int64_t file_bytes = fs.get_size(ff->fmt_ctx->filename); + int64_t scan_position = 0; + int canceled = 0; + FFMPEGScanProgress scan_progress(progress_title, file_bytes, &scan_position, &canceled); + + index_state->index_bytes = file_bytes; + index_state->init_scan(file->preferences->index_size); + if( ff->scan(index_state, &scan_position, &canceled) || canceled ) { + index_state->reset_index(); + index_state->reset_markers(); + return 1; + } + index_state->marker_status = MARKERS_READY; + return index_state->create_index(index_path, asset); +} diff --git a/cinelerra-5.0/cinelerra/fileffmpeg.h b/cinelerra-5.0/cinelerra/fileffmpeg.h index bde3d5d5..ed95ee94 100644 --- a/cinelerra-5.0/cinelerra/fileffmpeg.h +++ b/cinelerra-5.0/cinelerra/fileffmpeg.h @@ -3,11 +3,12 @@ #include "asset.inc" #include "bcwindowbase.inc" +#include "bcprogressbox.inc" #include "bitspopup.inc" #include "filebase.h" #include "fileffmpeg.inc" -#include "mwindow.inc" #include "mutex.h" +#include "thread.h" #include "vframe.inc" #include @@ -49,6 +50,7 @@ public: int get_audio_for_video(int vstream, int astream, int64_t &channel_mask); static void get_info(char *path,char *text,int len); int open_file(int rd,int wr); + int get_index(char *index_filename); int close_file(void); int set_video_position(int64_t pos); int set_audio_position(int64_t pos); @@ -214,4 +216,17 @@ public: FFMPEGConfigVideo *popup; }; +class FFMPEGScanProgress : public Thread +{ +public: + char progress_title[BCTEXTLEN]; + BC_ProgressBox *progress; + int64_t length, *position; + int done, *canceled; + + FFMPEGScanProgress(const char *title, int64_t length, int64_t *position, int *canceled); + ~FFMPEGScanProgress(); + void run(); +}; + #endif diff --git a/cinelerra-5.0/cinelerra/filempeg.C b/cinelerra-5.0/cinelerra/filempeg.C index dfd27181..b8e9c497 100644 --- a/cinelerra-5.0/cinelerra/filempeg.C +++ b/cinelerra-5.0/cinelerra/filempeg.C @@ -1027,56 +1027,23 @@ int FileMPEG::get_index(char *index_path) // Convert the index tables from tracks to channels. - if(mpeg3_index_tracks(fd)) - { -// Calculate size of buffer needed for all channels - int buffer_size = 0; - for(int i = 0; i < mpeg3_index_tracks(fd); i++) - { - buffer_size += mpeg3_index_size(fd, i) * - mpeg3_index_channels(fd, i) * - 2; - } - + if(mpeg3_index_tracks(fd)) { IndexState *index_state = asset->index_state; - index_state->index_buffer = new float[buffer_size]; - - int index_channels = 0; - for(int i = 0; i < mpeg3_index_tracks(fd); i++) - index_channels += mpeg3_index_channels(fd, i); -// Size of index buffer in floats - int current_offset = 0; -// Current asset channel - int current_channel = 0; - index_state->channels = index_channels; - index_state->index_zoom = mpeg3_index_zoom(fd); - index_state->index_offsets = new int64_t[index_channels]; - index_state->index_sizes = new int64_t[index_channels]; - for(int i = 0; i < mpeg3_index_tracks(fd); i++) - { - for(int j = 0; j < mpeg3_index_channels(fd, i); j++) - { - index_state->index_offsets[current_channel] = current_offset; - index_state->index_sizes[current_channel] = mpeg3_index_size(fd, i) * 2; - memcpy(index_state->index_buffer + current_offset, - mpeg3_index_data(fd, i, j), - mpeg3_index_size(fd, i) * sizeof(float) * 2); - - current_offset += mpeg3_index_size(fd, i) * 2; - current_channel++; + int index_zoom = mpeg3_index_zoom(fd); + int ntracks = mpeg3_index_tracks(fd); + int64_t offset = 0; + for(int i = 0; i < ntracks; i++) { + int nch = mpeg3_index_channels(fd, i); + for(int j = 0; j < nch; j++) { + float *bfr = (float *)mpeg3_index_data(fd, i, j); + int64_t size = 2*mpeg3_index_size(fd, i); + index_state->add_index_entry(bfr, offset, size); + offset += size; } } - FileSystem fs; - index_state->index_bytes = fs.get_size(asset->path); - - index_state->write_index(index_path, - buffer_size * sizeof(float), - asset, - asset->audio_length); - delete [] index_state->index_buffer; - - return 0; + int64_t file_bytes = fs.get_size(asset->path); + return index_state->write_index(index_path, asset, index_zoom, file_bytes); } return 1; diff --git a/cinelerra-5.0/cinelerra/filexml.C b/cinelerra-5.0/cinelerra/filexml.C index aa80ae8d..e5ce7b76 100644 --- a/cinelerra-5.0/cinelerra/filexml.C +++ b/cinelerra-5.0/cinelerra/filexml.C @@ -36,6 +36,8 @@ #define eprintf printf static const char left_delm = '<', right_delm = '>'; +const char *FileXML::xml_header = "\n"; +const int FileXML::xml_header_size = strlen(xml_header); XMLBuffer::XMLBuffer(long buf_size, long max_size, int del) { @@ -433,7 +435,12 @@ char* FileXML::get_data() } char* FileXML::string() { - return (char *)buffer->pos(); + return (char *)buffer->str(); +} + +long FileXML::length() +{ + return buffer->otell(); } char* FileXML::read_text() @@ -510,33 +517,24 @@ int FileXML::read_text_until(const char *tag_end, char *out, int len) int FileXML::write_to_file(const char *filename) { - strcpy(this->filename, filename); FILE *out = fopen(filename, "wb"); if( !out ) { eprintf("write_to_file %d \"%s\": %m\n", __LINE__, filename); return 1; } -// Position may have been rewound after storing - const char *str = string(); - long len = strlen(str); - fprintf(out, "\n"); - if( len && !fwrite(str, len, 1, out) ) { - eprintf("write_to_file %d \"%s\": %m\n", __LINE__, filename); - fclose(out); - return 1; - } + int ret = write_to_file(out, filename); fclose(out); - return 0; + return ret; } -int FileXML::write_to_file(FILE *file) +int FileXML::write_to_file(FILE *file, const char *filename) { - strcpy(filename, ""); - fprintf(file, "\n"); + strcpy(this->filename, filename); const char *str = string(); long len = strlen(str); // Position may have been rewound after storing - if( len && !fwrite(str, len, 1, file) ) { + if( !fwrite(xml_header, xml_header_size, 1, file) || + ( len > 0 && !fwrite(str, len, 1, file) ) ) { eprintf("\"%s\": %m\n", filename); return 1; } diff --git a/cinelerra-5.0/cinelerra/filexml.h b/cinelerra-5.0/cinelerra/filexml.h index d41d081c..25d0ff66 100644 --- a/cinelerra-5.0/cinelerra/filexml.h +++ b/cinelerra-5.0/cinelerra/filexml.h @@ -50,6 +50,7 @@ public: void oseek(long pos) { inp = bfr + pos; } void iseek(long pos) { outp = bfr + pos; } unsigned char *pos(long ofs=0) { return bfr+ofs; } + unsigned char *str() { if( inp < lmt ) *inp = 0; return bfr; } int read(char *bp, int n); int write(const char *bp, int n); @@ -136,7 +137,7 @@ public: int read_text_until(const char *tag_end, char *out, int len); int read_tag(); int write_to_file(const char *filename); - int write_to_file(FILE *file); + int write_to_file(FILE *file, const char *filename=""); int read_from_file(const char *filename, int ignore_error = 0); int read_from_string(char *string); char *(*decode)(char *bp, const char *sp, int n); @@ -150,6 +151,7 @@ public: int rewind(); char *get_data(); char *string(); + long length(); XMLBuffer *buffer; int coded; @@ -159,6 +161,8 @@ public: char *output; char left_delimiter, right_delimiter; char filename[MAX_TITLE]; + static const char *xml_header; + static const int xml_header_size; }; #endif diff --git a/cinelerra-5.0/cinelerra/indexable.C b/cinelerra-5.0/cinelerra/indexable.C index 865877d8..fa86e313 100644 --- a/cinelerra-5.0/cinelerra/indexable.C +++ b/cinelerra-5.0/cinelerra/indexable.C @@ -28,7 +28,6 @@ Indexable::Indexable(int is_asset) : Garbage(is_asset ? "Asset" : "EDL") { index_state = new IndexState; - index_state->reset(); this->is_asset = is_asset; strcpy(folder, MEDIA_FOLDER); } @@ -36,7 +35,7 @@ Indexable::Indexable(int is_asset) : Garbage(is_asset ? "Asset" : "EDL") Indexable::~Indexable() { - delete index_state; + index_state->remove_user(); } int Indexable::get_audio_channels() @@ -54,13 +53,26 @@ int64_t Indexable::get_audio_samples() return 0; } +void Indexable::update_path(char *new_path) +{ + strcpy(path, new_path); +} + +void Indexable::update_index(Indexable *src) +{ + index_state->remove_user(); + index_state = src->index_state; + index_state->add_user(); +} + void Indexable::copy_indexable(Indexable *src) { - strcpy(path, src->path); - index_state->copy_from(src->index_state); + if( this == src ) return; + update_path(src->path); + update_index(src); } int Indexable::have_audio() diff --git a/cinelerra-5.0/cinelerra/indexable.h b/cinelerra-5.0/cinelerra/indexable.h index 7e2631cb..766af10f 100644 --- a/cinelerra-5.0/cinelerra/indexable.h +++ b/cinelerra-5.0/cinelerra/indexable.h @@ -48,8 +48,9 @@ public: virtual int get_video_layers(); virtual int64_t get_video_frames(); - - void copy_indexable(Indexable *src); + void copy_indexable(Indexable *src); + void update_path(char *new_path); + void update_index(Indexable *src); IndexState *index_state; diff --git a/cinelerra-5.0/cinelerra/indexfile.C b/cinelerra-5.0/cinelerra/indexfile.C index 72dbb26b..8a21db0e 100644 --- a/cinelerra-5.0/cinelerra/indexfile.C +++ b/cinelerra-5.0/cinelerra/indexfile.C @@ -254,11 +254,9 @@ int IndexFile::open_file() // Get its last size without changing the real asset status. Indexable *test_indexable = new Indexable(0); if(indexable) - { test_indexable->copy_indexable(indexable); - } - read_info(test_indexable); + IndexState *index_state = test_indexable->index_state; FileSystem fs; if(fs.get_date(index_filename) < fs.get_date(test_indexable->path)) @@ -274,12 +272,12 @@ int IndexFile::open_file() fd = 0; } else - if(fs.get_size(test_indexable->path) != test_indexable->index_state->index_bytes) + if(fs.get_size(test_indexable->path) != index_state->index_bytes) { // source file is a different size than index source file if(debug) printf("IndexFile::open_file %d index_size=" _LD " source_size=" _LD "\n", __LINE__, - test_indexable->index_state->index_bytes, + index_state->index_bytes, fs.get_size(test_indexable->path)); result = 2; fclose(fd); @@ -430,11 +428,8 @@ int IndexFile::interrupt_index() int IndexFile::create_index(MainProgressBar *progress) { int result = 0; - SET_TRACE - IndexState *index_state = get_state(); - interrupt_flag = 0; // open the source file @@ -453,16 +448,13 @@ SET_TRACE // Test for index in stream table of contents if(source && !source->get_index(index_filename)) { + IndexState *index_state = get_state(); + index_state->index_status = INDEX_READY; redraw_edits(1); } else // Build index from scratch { - - - - - index_state->index_zoom = get_required_scale(); SET_TRACE // Indexes are now built for everything since it takes too long to draw @@ -610,14 +602,10 @@ int IndexFile::redraw_edits(int force) if(difference > 250 || force) { - IndexState *index_state = get_state(); redraw_timer->update(); -// Can't lock window here since the window is only redrawn when the pixel -// count changes. mwindow->gui->lock_window("IndexFile::redraw_edits"); mwindow->edl->set_index_file(indexable); mwindow->gui->draw_indexes(indexable); - index_state->old_index_end = index_state->index_end; mwindow->gui->unlock_window(); } return 0; @@ -640,7 +628,6 @@ int IndexFile::draw_index( SET_TRACE if(debug) printf("IndexFile::draw_index %d\n", __LINE__); -// check against index_end when being built if(index_state->index_zoom == 0) { printf(_("IndexFile::draw_index: index has 0 zoom\n")); @@ -675,30 +662,17 @@ SET_TRACE int64_t length = (int64_t)(w * mwindow->edl->local_session->zoom_sample * asset_over_session); - - if(index_state->index_status == INDEX_BUILDING) - { - if(startsource + length > index_state->index_end) - length = index_state->index_end - startsource; - } - -// length of index to read in samples * 2 int64_t lengthindex = length / index_state->index_zoom * 2; -// start of data in samples int64_t startindex = startsource / index_state->index_zoom * 2; +// length of index to read in floats +// length of index available in floats + int64_t endindex = index_state->index_status == INDEX_BUILDING ? + index_state->get_channel_used(edit->channel) * 2 : + index_state->get_index_size(edit->channel); // Clamp length of index to read by available data - if(startindex + lengthindex > index_state->get_index_size(edit->channel)) - lengthindex = index_state->get_index_size(edit->channel) - startindex; - - - if(debug) printf("IndexFile::draw_index %d length=" _LD "" - " index_size=" _LD " lengthindex=" _LD "\n", - __LINE__, length, index_state->get_index_size(edit->channel), - lengthindex); - if(lengthindex <= 0) return 0; - - - + if(startindex + lengthindex >= endindex ) + lengthindex = endindex - startindex; + if( lengthindex <= 0 ) return 0; // Actual length read from file in bytes int64_t length_read; @@ -707,7 +681,8 @@ SET_TRACE float *buffer = 0; int buffer_shared = 0; int center_pixel = mwindow->edl->local_session->zoom_track / 2; - if(mwindow->edl->session->show_titles) center_pixel += mwindow->theme->get_image("title_bg_data")->get_h(); + if( mwindow->edl->session->show_titles ) + center_pixel += mwindow->theme->get_image("title_bg_data")->get_h(); //int miny = center_pixel - mwindow->edl->local_session->zoom_track / 2; //int maxy = center_pixel + mwindow->edl->local_session->zoom_track / 2; int x1 = 0, y1, y2; @@ -716,18 +691,20 @@ SET_TRACE index_state->index_zoom * asset_over_session; -// get channel offset - startindex += index_state->get_index_offset(edit->channel); if(index_state->index_status == INDEX_BUILDING) { // index is in RAM, being built - buffer = &(index_state->index_buffer[startindex]); + buffer = index_state->get_channel_buffer(edit->channel); + if( !buffer ) return 0; + buffer += startindex; buffer_shared = 1; } else { +// add channel offset + startindex += index_state->get_index_offset(edit->channel); // index is stored in a file buffer = new float[lengthindex + 1]; buffer_shared = 0; @@ -753,17 +730,18 @@ SET_TRACE } } - - canvas->set_color(mwindow->theme->audio_color); - double current_frame = 0; float highsample = buffer[0]; float lowsample = buffer[1]; int prev_y1 = center_pixel; int prev_y2 = center_pixel; int first_frame = 1; + int zoom_y = mwindow->edl->local_session->zoom_y, zoom_y2 = zoom_y / 2; + int max_y = canvas->get_h(); + int zmax_y = center_pixel + zoom_y2 - 1; + if( zmax_y < max_y ) max_y = zmax_y; SET_TRACE for(int bufferposition = 0; @@ -772,22 +750,12 @@ SET_TRACE { if(current_frame >= index_frames_per_pixel) { - int next_y1 = (int)(center_pixel - highsample * mwindow->edl->local_session->zoom_y / 2); - int next_y2 = (int)(center_pixel - lowsample * mwindow->edl->local_session->zoom_y / 2); - if(next_y1 < 0) next_y1 = 0; - if(next_y1 > canvas->get_h()) next_y1 = canvas->get_h(); - if(next_y2 < 0) next_y2 = 0; - if(next_y2 > canvas->get_h()) next_y2 = canvas->get_h(); - if(next_y2 > center_pixel + mwindow->edl->local_session->zoom_y / 2 - 1) - next_y2 = center_pixel + mwindow->edl->local_session->zoom_y / 2 - 1; - if(next_y1 > center_pixel + mwindow->edl->local_session->zoom_y / 2 - 1) - next_y1 = center_pixel + mwindow->edl->local_session->zoom_y / 2 - 1; - - int y1 = next_y1; - int y2 = next_y2; - - - + + int y1 = (int)(center_pixel - highsample * zoom_y2); + int y2 = (int)(center_pixel - lowsample * zoom_y2); + CLAMP(y1, 0, max_y); int next_y1 = y1; + CLAMP(y2, 0, max_y); int next_y2 = y2; +//printf("draw_line (%f,%f) = %d,%d, %d,%d\n", lowsample, highsample, x1 + x, y1, x1 + x, y2); //SET_TRACE // A different algorithm has to be used if it's 1 sample per pixel and the @@ -830,8 +798,8 @@ SET_TRACE // Get last column if(current_frame) { - y1 = (int)(center_pixel - highsample * mwindow->edl->local_session->zoom_y / 2); - y2 = (int)(center_pixel - lowsample * mwindow->edl->local_session->zoom_y / 2); + y1 = (int)(center_pixel - highsample * zoom_y2); + y2 = (int)(center_pixel - lowsample * zoom_y2); canvas->draw_line(x1 + x, y1, x1 + x, y2, pixmap); } @@ -874,15 +842,9 @@ int IndexFile::read_info(Indexable *test_indexable) // Store format in actual asset. // If it's a nested EDL, we never want the format, just the index info. if(!test_indexable) test_indexable = indexable; - - - IndexState *index_state = 0; - if(test_indexable) - index_state = test_indexable->index_state; - else - return 1; + if(!test_indexable) return 1; - + IndexState * index_state = test_indexable->index_state; if(index_state->index_status == INDEX_NOTTESTED) { // read start of index data diff --git a/cinelerra-5.0/cinelerra/indexfile.h b/cinelerra-5.0/cinelerra/indexfile.h index ee414498..55865f08 100644 --- a/cinelerra-5.0/cinelerra/indexfile.h +++ b/cinelerra-5.0/cinelerra/indexfile.h @@ -27,6 +27,7 @@ #include "file.inc" #include "guicast.h" #include "indexable.inc" +#include "indexfile.inc" #include "indexstate.inc" #include "indexthread.inc" #include "mainprogress.inc" diff --git a/cinelerra-5.0/cinelerra/indexfile.inc b/cinelerra-5.0/cinelerra/indexfile.inc index 5ef34587..361739a9 100644 --- a/cinelerra-5.0/cinelerra/indexfile.inc +++ b/cinelerra-5.0/cinelerra/indexfile.inc @@ -23,12 +23,5 @@ #define INDEXFILE_INC class IndexFile; -class IndexState; - -// Index statuses -#define INDEX_READY 0 -#define INDEX_NOTTESTED 1 -#define INDEX_BUILDING 2 -#define INDEX_TOOSMALL 3 #endif diff --git a/cinelerra-5.0/cinelerra/indexstate.C b/cinelerra-5.0/cinelerra/indexstate.C index 52eb0c8a..6de43198 100644 --- a/cinelerra-5.0/cinelerra/indexstate.C +++ b/cinelerra-5.0/cinelerra/indexstate.C @@ -20,90 +20,131 @@ */ +#include "arraylist.h" #include "asset.h" +#include "clip.h" #include "filexml.h" #include "format.inc" +#include "indexfile.h" #include "indexstate.h" -#include "indexstate.inc" #include "language.h" +#include "mainerror.h" +#include "mutex.h" #include +#include +int IndexMarks::find(int64_t no) +{ + int l = -1, r = size(); + while( (r - l) > 1 ) { + int i = (l+r) >> 1; + if( no > values[i].no ) l = i; else r = i; + } + return l; +} -IndexState::IndexState() +IndexChannel::IndexChannel(IndexState *state, int64_t length) { - reset(); + this->state = state; + this->length = length; + min = max = 0; + bfr = inp = 0; + size = 0; + zidx = 0; } -IndexState::~IndexState() +IndexChannel::~IndexChannel() { - delete [] index_offsets; - delete [] index_sizes; -// Don't delete index buffer since it is shared with the index thread. + delete [] bfr; } -void IndexState::reset() +void IndexChannel::put_entry() +{ + inp->min = min; + inp->max = max; + ++inp; + max = -1; min = 1; + zidx = 0; +} + +int64_t IndexChannel::pos() +{ + return used() * state->index_zoom + zidx; +} + +void IndexChannel::pad_data(int64_t pos) +{ + CLAMP(pos, 0, length); + while( this->pos() < pos ) put_entry(); + inp = bfr + pos / state->index_zoom; + zidx = pos % state->index_zoom; +} + +void IndexState::reset_index() { index_status = INDEX_NOTTESTED; - index_start = old_index_end = index_end = 0; - index_offsets = 0; - index_sizes = 0; + index_start = 0; index_zoom = 0; index_bytes = 0; - index_buffer = 0; - channels = 0; + index_entries.remove_all_objects(); + index_channels.remove_all_objects(); } -void IndexState::dump() +void IndexState::reset_markers() { - printf("IndexState::dump this=%p\n", this); - printf(" channels=%d index_status=%d index_zoom=" _LD - " index_bytes=" _LD " index_offsets=%p\n", - channels, index_status, index_zoom, - index_bytes, index_offsets); - if(index_sizes) - { - printf(" index_sizes="); - for(int i = 0; i < channels; i++) - printf(_LD " ", index_sizes[i]); - printf("\n"); - } + marker_status = MARKERS_NOTTESTED; + video_markers.remove_all_objects(); + audio_markers.remove_all_objects(); } -void IndexState::copy_from(IndexState *src) +IndexState::IndexState() + : Garbage("IndexState") { - if(this == src) return; - - delete [] index_offsets; - delete [] index_sizes; - index_offsets = 0; - index_sizes = 0; - -//printf("Asset::update_index 1 %d\n", index_status); - index_status = src->index_status; - index_zoom = src->index_zoom; // zoom factor of index data - index_start = src->index_start; // byte start of index data in the index file - index_bytes = src->index_bytes; // Total bytes in source file for comparison before rebuilding the index - index_end = src->index_end; - old_index_end = src->old_index_end; // values for index build - channels = src->channels; - - if(src->index_offsets) - { - index_offsets = new int64_t[channels]; - index_sizes = new int64_t[channels]; - - int i; - for(i = 0; i < channels; i++) - { -// offsets of channels in index file in floats - index_offsets[i] = src->index_offsets[i]; - index_sizes[i] = src->index_sizes[i]; - } + marker_lock = new Mutex("IndexState::marker_lock"); + reset_index(); + reset_markers(); +} + +IndexState::~IndexState() +{ + reset_index(); + reset_markers(); + delete marker_lock; +} + +void IndexState::init_scan(int64_t index_length) +{ + index_zoom = 1; + int channels = index_channels.size(); + if( !channels ) return; + int64_t max_samples = 0; + for( int ch=0; chlength; + if( max_samples < len ) max_samples = len; + } + int64_t items = index_length / sizeof(IndexItem); + int64_t count = (items - channels) / channels + 1; + while( count*index_zoom < max_samples ) index_zoom *= 2; + for( int ch=0; chlength / index_zoom + 1; + chn->alloc(len); + chn->put_entry(); } + reset_markers(); +} -// pointer - index_buffer = src->index_buffer; +void IndexState::dump() +{ + printf("IndexState::dump this=%p\n", this); + printf(" index_status=%d index_zoom=" _LD " index_bytes=" _LD "\n", + index_status, index_zoom, index_bytes); + printf(" index entries=%d\n", index_entries.size()); + for( int i=0; ioffset, index_entries[i]->size); + printf("\n"); } void IndexState::write_xml(FileXML *file) @@ -114,17 +155,13 @@ void IndexState::write_xml(FileXML *file) file->append_tag(); file->append_newline(); - if(index_offsets) - { - for(int i = 0; i < channels; i++) - { - file->tag.set_title("OFFSET"); - file->tag.set_property("FLOAT", index_offsets[i]); - file->append_tag(); - file->tag.set_title("SIZE"); - file->tag.set_property("FLOAT", index_sizes[i]); - file->append_tag(); - } + for( int i=0; itag.set_title("OFFSET"); + file->tag.set_property("FLOAT", index_entries[i]->offset); + file->append_tag(); + file->tag.set_title("SIZE"); + file->tag.set_property("FLOAT", index_entries[i]->size); + file->append_tag(); } file->append_newline(); @@ -135,18 +172,8 @@ void IndexState::write_xml(FileXML *file) void IndexState::read_xml(FileXML *file, int channels) { - this->channels = channels; - - delete [] index_offsets; - delete [] index_sizes; - index_offsets = new int64_t[channels]; - index_sizes = new int64_t[channels]; - - for(int i = 0; i < channels; i++) - { - index_offsets[i] = 0; - index_sizes[i] = 0; - } + index_entries.remove_all_objects(); + for( int i=0; itag.get_property("ZOOM", 1); index_bytes = file->tag.get_property("BYTES", (int64_t)0); - while(!result) - { + while(!result) { result = file->read_tag(); - if(!result) - { - if(file->tag.title_is("/INDEX")) - { + if(!result) { + if(file->tag.title_is("/INDEX")) { result = 1; } - else - if(file->tag.title_is("OFFSET")) - { - if(current_offset < channels) - { - index_offsets[current_offset++] = file->tag.get_property("FLOAT", 0); + else if(file->tag.title_is("OFFSET")) { + if(current_offset < channels) { + int64_t offset = file->tag.get_property("FLOAT", (int64_t)0); + index_entries[current_offset++]->offset = offset; //printf("Asset::read_index %d %d\n", current_offset - 1, index_offsets[current_offset - 1]); } } - else - if(file->tag.title_is("SIZE")) - { - if(current_size < channels) - { - index_sizes[current_size++] = file->tag.get_property("FLOAT", 0); + else if(file->tag.title_is("SIZE")) { + if(current_size < channels) { + int64_t size = file->tag.get_property("FLOAT", (int64_t)0); + index_entries[current_size++]->size = size; } } } } } -int IndexState::write_index(const char *path, - int data_bytes, - Asset *asset, - int64_t length_source) +int IndexState::write_index(const char *index_path, Asset *asset, int64_t zoom, int64_t file_bytes) { - - FILE *file; - if(!(file = fopen(path, "wb"))) - { -// failed to create it - printf(_("IndexState::write_index Couldn't write index file %s to disk.\n"), - path); + FILE *fp = fopen(index_path, "wb"); + if( !fp ) { + eprintf(_("IndexState::write_index Couldn't write index file %s to disk.\n"), + index_path); + return 1; } + index_zoom = zoom; + index_bytes = file_bytes; + index_status = INDEX_READY; + + FileXML xml; +// write index_state as asset or directly. + if( asset ) + asset->write(&xml, 1, ""); else - { - FileXML xml; -// Pad index start position - fwrite((char*)&(index_start), sizeof(int64_t), 1, file); - - index_status = INDEX_READY; - -// Write asset encoding information in index file. -// This also calls back into index_state to write it. - if(asset) - { - asset->write(&xml, - 1, - ""); - } - else - { -// Must write index_state directly. - write_xml(&xml); - } + write_xml(&xml); + int64_t len = xml.length() + FileXML::xml_header_size; + index_start = sizeof(index_start) + len; + fwrite(&index_start, sizeof(index_start), 1, fp); + xml.write_to_file(fp); + + int channels = index_entries.size(); + int64_t max_size = 0; + for( int ch=0; chbfr; + int64_t size = ent->size; + if( max_size < size ) max_size = size; + fwrite(bfr, sizeof(float), size, fp); + } + + fclose(fp); + reset_index(); + return 0; +} - xml.write_to_file(file); - index_start = ftell(file); - fseek(file, 0, SEEK_SET); -// Write index start - fwrite((char*)&(index_start), sizeof(int64_t), 1, file); - fseek(file, index_start, SEEK_SET); - -// Write index data - fwrite(index_buffer, - data_bytes, - 1, - file); - fclose(file); +int IndexState::write_markers(const char *index_path) +{ + int vid_size = video_markers.size(); + int aud_size = audio_markers.size(); + if( !vid_size && !aud_size ) return 0; + + FILE *fp = 0; + char marker_path[BCTEXTLEN]; + strcpy(marker_path, index_path); + char *basename = strrchr(marker_path,'/'); + if( !basename ) basename = marker_path; + char *ext = strrchr(basename, '.'); + if( ext ) { + strcpy(ext, ".mkr"); + fp = fopen(marker_path, "wb"); } -// Force reread of header - index_status = INDEX_NOTTESTED; - index_end = length_source; - old_index_end = 0; - index_start = 0; + char version[] = MARKER_MAGIC_VERSION; + if( !fp || !fwrite(version, strlen(version), 1, fp) ) { + eprintf(_("IndexState::write_markers Couldn't write marker file %s to disk.\n"), + marker_path); + return 1; + } + + fwrite(&vid_size, sizeof(vid_size), 1, fp); + for( int vidx=0; vidxlock("IndexState::read_markers"); + if( marker_status == MARKERS_NOTTESTED ) { + char src_path[BCTEXTLEN], marker_path[BCTEXTLEN]; + IndexFile::get_index_filename(src_path, index_dir, marker_path, file_path, ".mkr"); + FILE *fp = fopen(marker_path, "rb"); + int vsz = strlen(MARKER_MAGIC_VERSION); + char version[vsz]; + if( fp && fread(version, vsz, 1, fp) ) { + if( memcmp(version, MARKER_MAGIC_VERSION, vsz) ) { + eprintf(_("IndexState::read_markers marker file version mismatched\n: %s\n"), + marker_path); + return 1; + } + ret = read_marks(fp); + if( !ret ) marker_status = MARKERS_READY; + fclose(fp); + } + } + marker_lock->unlock(); + return ret; +} + +int IndexState::read_marks(FILE *fp) +{ + reset_markers(); + int vid_size = 0; + if( !fread(&vid_size, sizeof(vid_size), 1, fp) ) return 1; + add_video_markers(vid_size); + for( int vidx=0; vidxbfr; + int64_t size = 2 * chn->used(); + add_index_entry(bfr, offset, size); + offset += size; + } + + write_markers(index_path); + return write_index(index_path, asset, index_zoom, index_bytes); +} + int64_t IndexState::get_index_offset(int channel) { - return channel < channels && index_offsets ? index_offsets[channel] : 0; + return channel >= index_entries.size() ? 0 : + index_entries[channel]->offset; } int64_t IndexState::get_index_size(int channel) { - return channel < channels && index_sizes ? index_sizes[channel] : 0; + return channel >= index_entries.size() ? 0 : + index_entries[channel]->size; } +float *IndexState::get_channel_buffer(int channel) +{ + return channel >= index_channels.size() ? 0 : + (float *)index_channels[channel]->bfr; +} + +int64_t IndexState::get_channel_used(int channel) +{ + return channel >= index_channels.size() ? 0 : + index_channels[channel]->used(); +} diff --git a/cinelerra-5.0/cinelerra/indexstate.h b/cinelerra-5.0/cinelerra/indexstate.h index fd095875..3280579d 100644 --- a/cinelerra-5.0/cinelerra/indexstate.h +++ b/cinelerra-5.0/cinelerra/indexstate.h @@ -23,52 +23,178 @@ #ifndef INDEXSTATE_H #define INDEXSTATE_H +#include "arraylist.h" #include "asset.inc" #include "filexml.inc" +#include "garbage.h" +#include "indexstate.inc" +#include "mutex.inc" +#include #include +class IndexItem { +public: + float max, min; +}; + +class IndexMark { +public: + int64_t no, pos; + IndexMark() {} + IndexMark(int64_t n, int64_t p) { no = n; pos = p; } + + bool operator==(IndexMark &v) { return v.no == no; } + bool operator >(IndexMark &v) { return v.no > no; } +}; + +class IndexChannel : public IndexItem { +public: + IndexChannel(IndexState *state, int64_t length); + ~IndexChannel(); + + IndexState *state; + IndexItem *bfr, *inp; + int64_t length, size; + int zidx; + + void alloc(int64_t sz) { bfr = inp = new IndexItem[(size=sz)+1]; } + int64_t used() { return inp - bfr; } + int64_t avail() { return size - used(); } + int64_t pos(); + void put_sample(float v, int zoom) { + if( v > max ) max = v; + if( v < min ) min = v; + if( ++zidx >= zoom ) + put_entry(); + } + void put_entry(); + void pad_data(int64_t pos); +}; + +class IndexChannels : public ArrayList { +public: + IndexChannels() {} + ~IndexChannels() { remove_all_objects(); } +}; + + +class IndexEntry { +public: + float *bfr; +// offsets / sizes of channels in index buffer in floats + int64_t offset, size; + IndexEntry(float *bp, int64_t ofs, int64_t sz) { + bfr = bp; offset = ofs; size = sz; + } +}; + +class IndexEntries : public ArrayList { +public: + IndexEntries() {} + ~IndexEntries() { remove_all_objects(); } +}; + +class IndexMarks : public ArrayList { +public: + IndexMarks() {} + ~IndexMarks() {} + void add_mark(int64_t no, int64_t pos) { append(IndexMark(no, pos)); } + int find(int64_t no); +}; + +class IndexMarkers : public ArrayList { +public: + IndexMarkers() {} + ~IndexMarkers() { remove_all_objects(); } +}; + // Assets & nested EDLs store this information to have their indexes built -class IndexState +class IndexState : public Garbage { public: IndexState(); ~IndexState(); - void reset(); - void copy_from(IndexState *src); + void reset_index(); + void reset_markers(); + void init_scan(int64_t index_length); + // Supply asset to include encoding information in index file. void write_xml(FileXML *file); void read_xml(FileXML *file, int channels); - int write_index(const char *path, - int data_bytes, - Asset *asset, - int64_t length_source); + int write_index(const char *index_path, Asset *asset, int64_t zoom, int64_t file_bytes); + int create_index(const char *index_path, Asset *asset); + int read_markers(char *index_dir, char *file_path); + int read_marks(FILE *fp); + int write_markers(const char *index_path); int64_t get_index_offset(int channel); int64_t get_index_size(int channel); + float *get_channel_buffer(int channel); + int64_t get_channel_used(int channel); void dump(); // index info - int index_status; // Macro from assets.inc - int64_t index_zoom; // zoom factor of index data - int64_t index_start; // byte start of index data in the index file + int index_status; // Macro from assets.inc + int marker_status; + int64_t index_zoom; // zoom factor of index data + int64_t index_start; // byte start of index data in the index file // Total bytes in the source file when the index was buillt int64_t index_bytes; - int64_t index_end, old_index_end; // values for index build -// offsets of channels in index buffer in floats - int64_t *index_offsets; -// Sizes of channels in index buffer in floats. This allows -// variable channel size. - int64_t *index_sizes; -// [ index channel ][ index channel ] -// [high][low][high][low][high][low][high][low] - float *index_buffer; -// Number of channels our buffers were allocated for - int channels; -}; + IndexChannels index_channels; + IndexEntries index_entries; + Mutex *marker_lock; + IndexMarkers video_markers; + IndexMarkers audio_markers; + void add_audio_channel(int64_t length) { + index_channels.append(new IndexChannel(this, length)); + } + void add_audio_stream(int nch, int64_t length) { + for( int ch=0; ch= 0 ) video_markers.append(new IndexMarks()); + } + void add_audio_markers(int n) { + while( --n >= 0 ) audio_markers.append(new IndexMarks()); + } + void add_index_entry(float *bfr, int64_t offset, int64_t size) { + index_entries.append(new IndexEntry(bfr, offset, size)); + } + void put_data(int ch, int nch, float *data, int64_t len) { + IndexChannel *chn = index_channels[ch]; + int64_t avl = chn->avail() * index_zoom - chn->zidx; + if( len > avl ) len = avl; + for( int64_t i=len; --i>=0; data+=nch ) + chn->put_sample(*data, index_zoom); + } + void put_data(int ch, double *data, int64_t len) { + IndexChannel *chn = index_channels[ch]; + int64_t avl = chn->avail() * index_zoom - chn->zidx; + if( len > avl ) len = avl; + for( int64_t i=len; --i>=0; ) + chn->put_sample(*data++, index_zoom); + } + int64_t pos(int ch) { return index_channels[ch]->pos(); } + void pad_data(int ch, int nch, int64_t pos) { + while( --nch >= 0 ) index_channels[ch++]->pad_data(pos); + } + void put_video_mark(int vidx, int64_t frm, int64_t pos) { + if( vidx >= video_markers.size() ) return; + IndexMarks &marks = *video_markers[vidx]; + if( marks.size()>0 && marks.last().no >= frm ) return; + marks.add_mark(frm, pos); + } + void put_audio_mark(int aidx, int64_t smpl, int64_t pos) { + if( aidx >= audio_markers.size() ) return; + IndexMarks &marks = *audio_markers[aidx]; + if( marks.size()>0 && marks.last().no >= smpl ) return; + marks.add_mark(smpl, pos); + } +}; #endif diff --git a/cinelerra-5.0/cinelerra/indexstate.inc b/cinelerra-5.0/cinelerra/indexstate.inc index adcffda5..5bbb56d5 100644 --- a/cinelerra-5.0/cinelerra/indexstate.inc +++ b/cinelerra-5.0/cinelerra/indexstate.inc @@ -1,7 +1,17 @@ #ifndef INDEXSTATE_INC #define INDEXSTATE_INC +// magic number "mkr" version = "1" +#define MARKER_MAGIC_VERSION "mkr1" +class IndexItem; +class IndexMark; +class IndexChannel; +class IndexChannels; +class IndexEntry; +class IndexEntries; +class IndexMarks; +class IndexMarkers; class IndexState; // Index statuses @@ -10,7 +20,7 @@ class IndexState; #define INDEX_BUILDING 2 #define INDEX_TOOSMALL 3 +#define MARKERS_NOTTESTED 0 +#define MARKERS_READY 1 #endif - - diff --git a/cinelerra-5.0/cinelerra/indexthread.C b/cinelerra-5.0/cinelerra/indexthread.C index c990e5fb..efe938e3 100644 --- a/cinelerra-5.0/cinelerra/indexthread.C +++ b/cinelerra-5.0/cinelerra/indexthread.C @@ -54,24 +54,6 @@ IndexThread::IndexThread(MWindow *mwindow, this->mwindow = mwindow; this->index_filename = index_filename; this->index_file = index_file; - IndexState *index_state = index_file->get_state(); - -// initialize output data -// size of output file in floats - int64_t index_size = mwindow->preferences->index_size / - sizeof(float) + 1; - - delete [] index_state->index_buffer; - delete [] index_state->index_offsets; - delete [] index_state->index_sizes; - - index_state->channels = index_file->source_channels; -// buffer used for drawing during the build. This is not deleted in the index_state - index_state->index_buffer = new float[index_size]; -// This is deleted in the index_state's destructor - index_state->index_offsets = new int64_t[index_state->channels]; - index_state->index_sizes = new int64_t[index_state->channels]; - bzero(index_state->index_buffer, index_size * sizeof(float)); // initialization is completed in run for(int i = 0; i < TOTAL_INDEX_BUFFERS; i++) @@ -84,7 +66,6 @@ IndexThread::IndexThread(MWindow *mwindow, output_lock[i] = new Condition(0, "IndexThread::output_lock"); input_lock[i] = new Condition(1, "IndexThread::input_lock"); - for(int j = 0; j < index_file->source_channels; j++) { buffer_in[i][j] = new Samples(buffer_size); @@ -99,8 +80,6 @@ IndexThread::IndexThread(MWindow *mwindow, IndexThread::~IndexThread() { - IndexState *index_state = index_file->get_state(); - for(int i = 0; i < TOTAL_INDEX_BUFFERS; i++) { for(int j = 0; j < index_file->source_channels; j++) @@ -111,9 +90,6 @@ IndexThread::~IndexThread() delete output_lock[i]; delete input_lock[i]; } - - delete [] index_state->index_buffer; - index_state->index_buffer = 0; } int IndexThread::start_build() @@ -134,113 +110,26 @@ int IndexThread::stop_build() void IndexThread::run() { int done = 0; - -// current high samples in index - int64_t *highpoint; -// current low samples in the index - int64_t *lowpoint; -// position in current indexframe - int64_t *frame_position; - int first_point = 1; IndexState *index_state = index_file->get_state(); - - highpoint = new int64_t[index_file->source_channels]; - lowpoint = new int64_t[index_file->source_channels]; - frame_position = new int64_t[index_file->source_channels]; - -// predict first highpoint for each channel plus padding and initialize it - for(int64_t channel = 0; channel < index_file->source_channels; channel++) - { - highpoint[channel] = - index_state->index_offsets[channel] = - (length_source / index_state->index_zoom * 2 + 1) * channel; - lowpoint[channel] = highpoint[channel] + 1; - - index_state->index_sizes[channel] = 0; - frame_position[channel] = 0; - } - - //int64_t index_start = 0; // end of index during last edit update - index_state->index_end = 0; // samples in source completed - index_state->old_index_end = 0; + int index_channels = index_file->source_channels; + index_state->add_audio_stream(index_channels, index_file->source_length); + index_state->init_scan(mwindow->preferences->index_size); index_state->index_status = INDEX_BUILDING; - int64_t zoomx = index_state->index_zoom; - float *index_buffer = index_state->index_buffer; // output of index build - int64_t *index_sizes = index_state->index_sizes; - int64_t *index_offsets = index_state->index_offsets; -//printf("IndexThread::run %d\n", __LINE__); -//index_state->dump(); -//printf("IndexThread::run %d\n", __LINE__); - while(!interrupt_flag && !done) - { + while(!interrupt_flag && !done) { output_lock[current_buffer]->lock("IndexThread::run"); if(last_buffer[current_buffer]) done = 1; - if(!interrupt_flag && !done) - { + if(!interrupt_flag && !done) { // process buffer - int64_t fragment_size = input_len[current_buffer]; - -// printf("IndexThread::run %d index_state->channels=%d index_file->source_channels=%d index_state->index_buffer=%p buffer_in=%p\n", -// __LINE__, -// index_state->channels, -// index_file->source_channels, -// index_state->index_buffer, -// buffer_in); - for(int channel = 0; channel < index_file->source_channels; channel++) - { -SET_TRACE - int64_t *highpoint_channel = &highpoint[channel]; - int64_t *lowpoint_channel = &lowpoint[channel]; - int64_t *frame_position_channel = &frame_position[channel]; - double *buffer_source = buffer_in[current_buffer][channel]->get_data(); - -SET_TRACE - for(int64_t i = 0; i < fragment_size; i++) - { - if(*frame_position_channel == zoomx) - { - *highpoint_channel += 2; - *lowpoint_channel += 2; - *frame_position_channel = 0; -// store and reset output values - index_buffer[*highpoint_channel] = - index_buffer[*lowpoint_channel] = - buffer_source[i]; - index_sizes[channel] = - *lowpoint_channel - - index_offsets[channel] + - 1; - } - else - { -// get high and low points - if(first_point) - { - index_buffer[*highpoint_channel] = - index_buffer[*lowpoint_channel] = buffer_source[i]; - first_point = 0; - } - else - { - if(buffer_source[i] > index_buffer[*highpoint_channel]) - index_buffer[*highpoint_channel] = buffer_source[i]; - else - if(buffer_source[i] < index_buffer[*lowpoint_channel]) - index_buffer[*lowpoint_channel] = buffer_source[i]; - } - } - (*frame_position_channel)++; - } // end index one buffer -SET_TRACE + int64_t len = input_len[current_buffer]; + for( int ch=0; chget_data(); + index_state->put_data(ch,samples,len); } - index_state->index_end += fragment_size; - // draw simultaneously with build index_file->redraw_edits(0); - //index_start = index_state->index_end; } input_lock[current_buffer]->unlock(); @@ -251,15 +140,8 @@ SET_TRACE index_file->redraw_edits(1); // write the index file to disk - index_state->write_index(index_filename, - (lowpoint[index_file->source_channels - 1] + 1) * sizeof(float), - index_file->indexable->is_asset ? (Asset*)index_file->indexable : 0, - index_file->source_length); - - - delete [] highpoint; - delete [] lowpoint; - delete [] frame_position; + Asset *asset = index_file->indexable->is_asset ? (Asset*)index_file->indexable : 0; + index_state->create_index(index_filename, asset); } diff --git a/cinelerra-5.0/cinelerra/mbuttons.C b/cinelerra-5.0/cinelerra/mbuttons.C index d2836607..fff91679 100644 --- a/cinelerra-5.0/cinelerra/mbuttons.C +++ b/cinelerra-5.0/cinelerra/mbuttons.C @@ -71,7 +71,7 @@ void MButtons::create_objects() edit_panel->create_objects(); x += edit_panel->get_w(); - ffmpeg_toggle = new MainFFMpegToggle(mwindow, this, get_w()-30, 0); + ffmpeg_toggle = new MainFFMpegToggle(mwindow, this, get_w(), 0); add_subwindow(ffmpeg_toggle); flash(0); } @@ -83,6 +83,7 @@ int MButtons::resize_event() mwindow->theme->mbuttons_w, mwindow->theme->mbuttons_h); draw_top_background(get_parent(), 0, 0, get_w(), get_h()); + ffmpeg_toggle->reposition_window(get_w()-30, 0); flash(0); return 0; } diff --git a/cinelerra-5.0/cinelerra/mediadb.C b/cinelerra-5.0/cinelerra/mediadb.C index 35783daa..2c057c09 100644 --- a/cinelerra-5.0/cinelerra/mediadb.C +++ b/cinelerra-5.0/cinelerra/mediadb.C @@ -15,7 +15,7 @@ -void write_pbm(uint8_t *tp, int w, int h, const char *fmt, ...) +void write_pgm(uint8_t *tp, int w, int h, const char *fmt, ...) { va_list ap; va_start(ap, fmt); char fn[256]; vsnprintf(fn, sizeof(fn), fmt, ap); @@ -28,7 +28,7 @@ void write_pbm(uint8_t *tp, int w, int h, const char *fmt, ...) } } -void write_pgm(uint8_t *tp, int w, int h, const char *fmt, ...) +void write_ppm(uint8_t *tp, int w, int h, const char *fmt, ...) { va_list ap; va_start(ap, fmt); char fn[256]; vsnprintf(fn, sizeof(fn), fmt, ap); diff --git a/cinelerra-5.0/cinelerra/mwindow.C b/cinelerra-5.0/cinelerra/mwindow.C index a53bd4c8..d9bbe2ac 100644 --- a/cinelerra-5.0/cinelerra/mwindow.C +++ b/cinelerra-5.0/cinelerra/mwindow.C @@ -645,6 +645,8 @@ void MWindow::clean_indexes() //printf("MWindow::clean_indexes 2 %s\n", string2); sprintf(ptr, ".toc"); remove(string2); + sprintf(ptr, ".mkr"); + remove(string2); } } @@ -2605,6 +2607,7 @@ void MWindow::remove_indexfile(Indexable *indexable) // Erase file IndexFile::delete_index(preferences, asset, ".toc"); IndexFile::delete_index(preferences, asset, ".idx"); + IndexFile::delete_index(preferences, asset, ".mkr"); } void MWindow::rebuild_indices() diff --git a/cinelerra-5.0/cinelerra/mwindowgui.C b/cinelerra-5.0/cinelerra/mwindowgui.C index 86a41fcc..b0c0f53f 100644 --- a/cinelerra-5.0/cinelerra/mwindowgui.C +++ b/cinelerra-5.0/cinelerra/mwindowgui.C @@ -389,7 +389,6 @@ int MWindowGUI::resize_event(int w, int h) mwindow->session->mwindow_h = h; mwindow->theme->get_mwindow_sizes(this, w, h); mwindow->theme->draw_mwindow_bg(this); -// mainmenu->reposition_window(0, 0, w, mainmenu->get_h()); mbuttons->resize_event(); statusbar->resize_event(); @@ -469,6 +468,9 @@ int MWindowGUI::resize_event(int w, int h) zoombar->resize_event(); pane_button->reposition_window(w - mwindow->theme->get_image_set("pane")[0]->get_w(), mwindow->theme->mzoom_y + 1 - mwindow->theme->get_image_set("pane")[0]->get_h()); + int x = get_w() - MainShBtns::calculate_w(0); + mainmenu->resize_event(x, mainmenu->get_h()); + mainshbtns->reposition_window(x, 0); // get_scrollbars(0); // canvas->resize_event(); //printf("MWindowGUI::resize_event %d\n", __LINE__); diff --git a/cinelerra-5.0/cinelerra/nestededls.C b/cinelerra-5.0/cinelerra/nestededls.C index 3d1731c9..4c7c64a0 100644 --- a/cinelerra-5.0/cinelerra/nestededls.C +++ b/cinelerra-5.0/cinelerra/nestededls.C @@ -84,7 +84,7 @@ void NestedEDLs::update_index(EDL *nested_edl) // __LINE__, // current->index_state, // nested_edl->index_state->index_status); - current->index_state->copy_from(nested_edl->index_state); + current->update_index(nested_edl); } } } diff --git a/cinelerra-5.0/cinelerra/performanceprefs.C b/cinelerra-5.0/cinelerra/performanceprefs.C index edc2f69e..3f77dce5 100644 --- a/cinelerra-5.0/cinelerra/performanceprefs.C +++ b/cinelerra-5.0/cinelerra/performanceprefs.C @@ -112,6 +112,9 @@ void PerformancePrefs::create_objects() ffmpeg_early_probe = new PrefsFFMPEGEarlyProbe(this, x, y); add_subwindow(ffmpeg_early_probe); + x1 = x + ffmpeg_early_probe->get_w() + 24; + ffmpeg_marker_indecies = new PrefsFFMPEGMarkerIndecies(this, x1, y); + add_subwindow(ffmpeg_marker_indecies); y += 30; @@ -581,6 +584,7 @@ PrefsFFMPEGEarlyProbe::PrefsFFMPEGEarlyProbe(PerformancePrefs *perf_prefs, int x PrefsFFMPEGEarlyProbe::~PrefsFFMPEGEarlyProbe() { } + int PrefsFFMPEGEarlyProbe::handle_event() { perf_prefs->pwindow->thread->preferences->ffmpeg_early_probe = get_value(); @@ -588,6 +592,24 @@ int PrefsFFMPEGEarlyProbe::handle_event() } +PrefsFFMPEGMarkerIndecies::PrefsFFMPEGMarkerIndecies(PerformancePrefs *perf_prefs, int x, int y) + : BC_CheckBox(x, y, + perf_prefs->pwindow->thread->preferences->ffmpeg_marker_indecies, + _("build ffmpeg marker indecies")) +{ + this->perf_prefs = perf_prefs; +} +PrefsFFMPEGMarkerIndecies::~PrefsFFMPEGMarkerIndecies() +{ +} + +int PrefsFFMPEGMarkerIndecies::handle_event() +{ + perf_prefs->pwindow->thread->preferences->ffmpeg_marker_indecies = get_value(); + return 1; +} + + diff --git a/cinelerra-5.0/cinelerra/performanceprefs.h b/cinelerra-5.0/cinelerra/performanceprefs.h index 0841ab82..7ac789cf 100644 --- a/cinelerra-5.0/cinelerra/performanceprefs.h +++ b/cinelerra-5.0/cinelerra/performanceprefs.h @@ -62,6 +62,7 @@ public: BC_Title *master_rate; PrefsFileForking *file_forking; PrefsFFMPEGEarlyProbe *ffmpeg_early_probe; + PrefsFFMPEGMarkerIndecies *ffmpeg_marker_indecies; }; @@ -167,6 +168,17 @@ public: PerformancePrefs *perf_prefs; }; +class PrefsFFMPEGMarkerIndecies : public BC_CheckBox +{ +public: + PrefsFFMPEGMarkerIndecies(PerformancePrefs *perf_prefs, int x, int y); + ~PrefsFFMPEGMarkerIndecies(); + + int handle_event(); + + PerformancePrefs *perf_prefs; +}; + diff --git a/cinelerra-5.0/cinelerra/performanceprefs.inc b/cinelerra-5.0/cinelerra/performanceprefs.inc index 806cfea8..954b546c 100644 --- a/cinelerra-5.0/cinelerra/performanceprefs.inc +++ b/cinelerra-5.0/cinelerra/performanceprefs.inc @@ -31,6 +31,7 @@ class PrefsForceUniprocessor; class PrefsTrapSigSEGV; class PrefsTrapSigINTR; class PrefsFFMPEGEarlyProbe; +class PrefsFFMPEGMarkerIndecies; class PrefsFileForking; class PrefsRenderFarm; class PrefsRenderFarmConsolidate; diff --git a/cinelerra-5.0/cinelerra/preferences.C b/cinelerra-5.0/cinelerra/preferences.C index 0a8d3374..204acf32 100644 --- a/cinelerra-5.0/cinelerra/preferences.C +++ b/cinelerra-5.0/cinelerra/preferences.C @@ -70,12 +70,12 @@ Preferences::Preferences() sprintf(index_directory, BCASTDIR); if(strlen(index_directory)) fs.complete_path(index_directory); - cache_size = 0xa00000; - index_size = 0x300000; + cache_size = 0x1000000; + index_size = 0x400000; index_count = 100; use_thumbnails = 1; - trap_sigsegv = 0; - trap_sigintr = 0; + trap_sigsegv = 1; + trap_sigintr = 1; theme[0] = 0; use_renderfarm = 0; force_uniprocessor = 0; @@ -87,8 +87,9 @@ Preferences::Preferences() renderfarm_job_count = 20; processors = calculate_processors(0); real_processors = calculate_processors(1); - file_forking = 1; + file_forking = 0; ffmpeg_early_probe = 0; + ffmpeg_marker_indecies = 1; warn_indecies = 1; // Default brender asset @@ -124,6 +125,8 @@ Preferences::Preferences() Preferences::~Preferences() { brender_asset->Garbage::remove_user(); + shbtn_prefs.remove_all_objects(); + renderfarm_nodes.remove_all_objects(); delete preferences_lock; } @@ -190,6 +193,7 @@ void Preferences::copy_from(Preferences *that) real_processors = that->real_processors; file_forking = that->file_forking; ffmpeg_early_probe = that->ffmpeg_early_probe; + ffmpeg_marker_indecies = that->ffmpeg_marker_indecies; warn_indecies = that->warn_indecies; renderfarm_nodes.remove_all_objects(); renderfarm_ports.remove_all(); @@ -339,10 +343,11 @@ int Preferences::load_defaults(BC_Hash *defaults) - force_uniprocessor = defaults->get("FORCE_UNIPROCESSOR", 0); - file_forking = defaults->get("FILE_FORKING", 1); - ffmpeg_early_probe = defaults->get("FFMPEG_EARLY_PROBE", 0); - warn_indecies = defaults->get("WARN_INDECIES", 1); + force_uniprocessor = defaults->get("FORCE_UNIPROCESSOR", force_uniprocessor); + file_forking = defaults->get("FILE_FORKING", file_forking); + ffmpeg_early_probe = defaults->get("FFMPEG_EARLY_PROBE", ffmpeg_early_probe); + ffmpeg_marker_indecies = defaults->get("FFMPEG_MARKER_INDECIES", ffmpeg_marker_indecies); + warn_indecies = defaults->get("WARN_INDECIES", warn_indecies); use_brender = defaults->get("USE_BRENDER", use_brender); brender_fragment = defaults->get("BRENDER_FRAGMENT", brender_fragment); cache_size = defaults->get("CACHE_SIZE", cache_size); @@ -440,6 +445,7 @@ int Preferences::save_defaults(BC_Hash *defaults) defaults->update("FORCE_UNIPROCESSOR", force_uniprocessor); defaults->update("FILE_FORKING", file_forking); defaults->update("FFMPEG_EARLY_PROBE", ffmpeg_early_probe); + defaults->update("FFMPEG_MARKER_INDECIES", ffmpeg_marker_indecies); defaults->update("WARN_INDECIES", warn_indecies); brender_asset->save_defaults(defaults, "BRENDER_", diff --git a/cinelerra-5.0/cinelerra/preferences.h b/cinelerra-5.0/cinelerra/preferences.h index 30ff9242..a2829e7e 100644 --- a/cinelerra-5.0/cinelerra/preferences.h +++ b/cinelerra-5.0/cinelerra/preferences.h @@ -101,6 +101,8 @@ public: int file_forking; // ffmpeg probes early/late during File::open_file read int ffmpeg_early_probe; +// ffmpeg builds marker indecies as it builds idx files + int ffmpeg_marker_indecies; int warn_indecies; // Default positions for channels diff --git a/cinelerra-5.0/cinelerra/record.C b/cinelerra-5.0/cinelerra/record.C index ffa2b282..c4c6f4b5 100644 --- a/cinelerra-5.0/cinelerra/record.C +++ b/cinelerra-5.0/cinelerra/record.C @@ -465,6 +465,7 @@ void Record::delete_index_file(Asset *asset) { IndexFile::delete_index(mwindow->preferences, asset, ".toc"); IndexFile::delete_index(mwindow->preferences, asset, ".idx"); + IndexFile::delete_index(mwindow->preferences, asset, ".mkr"); } void Record::delete_batch() diff --git a/cinelerra-5.0/cinelerra/removefile.C b/cinelerra-5.0/cinelerra/removefile.C index e2cdb52c..6833917e 100644 --- a/cinelerra-5.0/cinelerra/removefile.C +++ b/cinelerra-5.0/cinelerra/removefile.C @@ -46,7 +46,7 @@ RemoveFile::RemoveFile(const char *path,int autodelete) void RemoveFile::run() { run_lock.lock("RemoveFile::run_lock"); -printf("RemoveFile::run: deleting %s\n", file_path); +//printf("RemoveFile::run: deleting %s\n", file_path); remove(file_path); run_lock.unlock(); } @@ -66,7 +66,7 @@ RemoveAll::RemoveAll(const char *path) else RemoveFile::removeFileWait(fp); } -printf("RemoveAll::deleting directory %s\n", path); +//printf("RemoveAll::deleting directory %s\n", path); rmdir(path); } diff --git a/cinelerra-5.0/guicast/arraylist.h b/cinelerra-5.0/guicast/arraylist.h index 3c439438..5f66b341 100644 --- a/cinelerra-5.0/guicast/arraylist.h +++ b/cinelerra-5.0/guicast/arraylist.h @@ -69,6 +69,7 @@ public: total = 0; } TYPE &last() { return values[total - 1]; } + void set_array_delete() { dtype = d_array; } void set_free() { dtype = d_free; } int size() { return total; } diff --git a/cinelerra-5.0/guicast/bcwindowbase.C b/cinelerra-5.0/guicast/bcwindowbase.C index 32547118..21a91f71 100644 --- a/cinelerra-5.0/guicast/bcwindowbase.C +++ b/cinelerra-5.0/guicast/bcwindowbase.C @@ -139,10 +139,12 @@ BC_WindowBase::~BC_WindowBase() if( get_resources()->get_synchronous() && top_level->options & GLX_WINDOW ) { if( !glx_win ) { // NVIDIA library threading problem, XCloseDisplay SEGVs without this + lock_window("BC_WindowBase::~BC_WindowBase:XDestroyWindow"); sync_lock("BC_WindowBase::~BC_WindowBase:XDestroyWindow"); glXMakeContextCurrent(top_level->display, 0, 0, 0); XDestroyWindow(top_level->display, win); sync_unlock(); + unlock_window(); } else get_resources()->get_synchronous()->delete_window(this); diff --git a/cinelerra-5.0/guicast/test4.C b/cinelerra-5.0/guicast/test4.C index e28ec10b..6e9d27f7 100644 --- a/cinelerra-5.0/guicast/test4.C +++ b/cinelerra-5.0/guicast/test4.C @@ -84,7 +84,7 @@ const char *cmdl[] = { "rgb_floatp", "rgba_floatp", }; -void write_pbm(uint8_t *tp, int w, int h, const char *fmt, ...) +void write_pgm(uint8_t *tp, int w, int h, const char *fmt, ...) { va_list ap; va_start(ap, fmt); char fn[256]; vsnprintf(fn, sizeof(fn), fmt, ap); @@ -97,7 +97,7 @@ void write_pbm(uint8_t *tp, int w, int h, const char *fmt, ...) } } -void write_pgm(uint8_t *tp, int w, int h, const char *fmt, ...) +void write_ppm(uint8_t *tp, int w, int h, const char *fmt, ...) { va_list ap; va_start(ap, fmt); char fn[256]; vsnprintf(fn, sizeof(fn), fmt, ap); diff --git a/cinelerra-5.0/guicast/thread.C b/cinelerra-5.0/guicast/thread.C index 552255a5..c3d068e0 100644 --- a/cinelerra-5.0/guicast/thread.C +++ b/cinelerra-5.0/guicast/thread.C @@ -266,16 +266,6 @@ int Thread::continue_thread() return 0; } -bool Thread::exists() -{ - return tid != ((pthread_t)-1); -} - -bool Thread::running() -{ - return exists() & ~finished; -} - int Thread::set_synchronous(int value) { this->synchronous = value != 0; diff --git a/cinelerra-5.0/guicast/thread.h b/cinelerra-5.0/guicast/thread.h index ae607a38..1eb86959 100644 --- a/cinelerra-5.0/guicast/thread.h +++ b/cinelerra-5.0/guicast/thread.h @@ -37,9 +37,9 @@ static inline int gettid() { return syscall(SYS_gettid, 0, 0, 0); } class Thread { -private: static void* entrypoint(void *parameters); - bool exists(); // tid exists + bool exists() { return tid != ((pthread_t)-1); } + protected: virtual void run() = 0; public: @@ -54,7 +54,8 @@ public: int enable_cancel(); int disable_cancel(); int get_cancel_enabled(); - bool running(); // exists and not finished + bool running() { return exists() && !finished; } + int get_synchronous(); int set_synchronous(int value); int get_realtime(); -- 2.26.2