Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / file.C
index 13e1b457463a2876c411725d4c6ad2ff700c9336..90ad489f76a3307314789cab03035426f4d6e374 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * CINELERRA
  * Copyright (C) 2010 Adam Williams <broadcast at earthling dot net>
+ * Copyright (C) 2003-2016 Cinelerra CV contributors
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,6 +27,7 @@
 #include <stdarg.h>
 #include <ctype.h>
 #include <limits.h>
+#include <time.h>
 // work arounds (centos)
 #include <lzma.h>
 #ifndef INT64_MAX
@@ -44,6 +46,7 @@
 #include "filecr2.h"
 #include "filedb.h"
 #include "filedv.h"
+#include "filedpx.h"
 #include "fileexr.h"
 #include "fileffmpeg.h"
 #include "fileflac.h"
@@ -55,6 +58,7 @@
 #include "fileogg.h"
 #include "filepng.h"
 #include "fileppm.h"
+#include "fileref.h"
 #include "filescene.h"
 #include "filesndfile.h"
 #include "filetga.h"
 #include "samples.h"
 #include "vframe.h"
 
-//static int temp_debug = 0;
+#ifdef HAVE_OGG
 //suppress noref warning
 void *vorbis0_ov_callbacks[] = {
  &OV_CALLBACKS_DEFAULT, &OV_CALLBACKS_NOCLOSE,
  &OV_CALLBACKS_STREAMONLY, &OV_CALLBACKS_STREAMONLY_NOCLOSE,
 };
+#endif
 
 File::File()
 {
@@ -102,7 +107,6 @@ File::~File()
        }
 
        if( temp_frame ) {
-//printf("File::~File %d temp_debug=%d\n", __LINE__, --temp_debug);
                delete temp_frame;
        }
 
@@ -172,10 +176,12 @@ int File::get_options(FormatTools *format,
        format_completion->lock("File::get_options");
        switch( asset->format ) {
 #ifdef HAVE_CIN_3RDPARTY
+#ifdef HAVE_LIBZMPEG
        case FILE_AC3: FileAC3::get_parameters(parent_window, asset, format_window,
                        audio_options, video_options, edl);
                break;
 #endif
+#endif
 #ifdef HAVE_DV
        case FILE_RAWDV:
                FileDV::get_parameters(parent_window, asset, format_window,
@@ -206,6 +212,13 @@ int File::get_options(FormatTools *format,
                FileJPEG::get_parameters(parent_window, asset, format_window,
                        audio_options, video_options, edl);
                break;
+#ifdef HAVE_LIBDPX
+       case FILE_DPX:
+       case FILE_DPX_LIST:
+               FileDPX::get_parameters(parent_window, asset, format_window,
+                       audio_options, video_options, edl);
+               break;
+#endif
 #ifdef HAVE_OPENEXR
        case FILE_EXR:
        case FILE_EXR_LIST:
@@ -237,10 +250,16 @@ int File::get_options(FormatTools *format,
                FileTIFF::get_parameters(parent_window, asset, format_window,
                        audio_options, video_options, edl);
                break;
+#ifdef HAVE_OGG
        case FILE_OGG:
                FileOGG::get_parameters(parent_window, asset, format_window,
                        audio_options, video_options, edl);
                break;
+       case FILE_VORBIS:
+               FileVorbis::get_parameters(parent_window, asset, format_window,
+                       audio_options, video_options, edl);
+               break;
+#endif
        default:
                break;
        }
@@ -272,6 +291,7 @@ int File::can_scale_input(Asset *asset)
        case FILE_MPEG:
        case FILE_FFMPEG:
                return 1;
+       case FILE_DPX:
        case FILE_EXR:
        case FILE_JPEG:
        case FILE_PNG:
@@ -341,6 +361,24 @@ int File::delete_oldest()
        return frame_cache->delete_oldest();
 }
 
+int File::get_cache_frame(VFrame *frame, int64_t position)
+{
+       return frame_cache->get_cache_frame(frame, position,
+               current_layer, asset->frame_rate);
+}
+
+void File::put_cache_frame(VFrame *frame, int64_t position, int use_copy)
+{
+       frame_cache->put_cache_frame(frame,
+               position, current_layer, asset->frame_rate, use_copy);
+}
+
+int File::get_use_cache()
+{
+       return use_cache;
+}
+
+
 // file driver in order of probe precidence
 //  can be reordered in preferences->interface
 const char *File::default_probes[] = { 
@@ -354,7 +392,12 @@ const char *File::default_probes[] = {
        "PNG",
        "PPM",
        "JPEG",
+#ifdef HAVE_GIFLIB
        "GIF",
+#endif
+#ifdef HAVE_LIBDPX
+       "DPX",
+#endif
 #ifdef HAVE_OPENEXR
        "EXR",
 #endif
@@ -362,11 +405,15 @@ const char *File::default_probes[] = {
        "CR2",
        "TGA",
        "TIFF",
+#ifdef HAVE_OGG
        "OGG",
        "Vorbis",
+#endif
+#ifdef HAVE_LIBZMPEG
        "MPEG",
+#endif
        "EDL",
-               "FFMPEG_Late", 
+       "FFMPEG_Late",
 }; 
 const int File::nb_probes =
        sizeof(File::default_probes)/sizeof(File::default_probes[0]); 
@@ -429,12 +476,24 @@ int File::probe()
                        file = new FileJPEG(this->asset, this);
                        return FILE_OK;
                }
+#ifdef HAVE_GIFLIB
                if( !strcmp(pref->name,"GIF") ) { // GIF file
-                       if( !FileGIF::check_sig(this->asset)) continue;
-                       file = new FileGIF(this->asset, this);
+                       if( FileGIFList::check_sig(this->asset) )
+                               file = new FileGIFList(this->asset, this);
+                       else if( FileGIF::check_sig(this->asset) )
+                               file = new FileGIF(this->asset, this);
+                       else continue;
+                       return FILE_OK;
+               }
+#endif
+#ifdef HAVE_LIBDPX
+               if( !strcmp(pref->name,"DPX") ) { // DPX file
+                       if( !FileDPX::check_sig(this->asset, data) ) continue;
+                       file = new FileDPX(this->asset, this);
                        return FILE_OK;
                }
-#ifdef HAVE_EXR
+#endif
+#ifdef HAVE_OPENEXR
                if( !strcmp(pref->name,"EXR") ) { // EXR file
                        if( !FileEXR::check_sig(this->asset, data)) continue;
                        file = new FileEXR(this->asset, this);
@@ -461,6 +520,7 @@ int File::probe()
                        file = new FileTIFF(this->asset, this);
                        return FILE_OK;
                }
+#ifdef HAVE_OGG
                if( !strcmp(pref->name,"OGG") ) { // OGG file
                        if( !FileOGG::check_sig(this->asset) ) continue;
                        file = new FileOGG(this->asset, this);
@@ -471,6 +531,7 @@ int File::probe()
                        file = new FileVorbis(this->asset, this);
                        return FILE_OK;
                }
+#endif
 #ifdef HAVE_LIBZMPEG
                if( !strcmp(pref->name,"MPEG") ) { // MPEG file
                        if( !FileMPEG::check_sig(this->asset) ) continue;
@@ -514,9 +575,11 @@ int File::open_file(Preferences *preferences,
                break; }
 // format already determined
 #ifdef HAVE_CIN_3RDPARTY
+#ifdef HAVE_LIBZMPEG
        case FILE_AC3:
                file = new FileAC3(this->asset, this);
                break;
+#endif
 #endif
        case FILE_SCENE:
                file = new FileScene(this->asset, this);
@@ -549,17 +612,27 @@ int File::open_file(Preferences *preferences,
        case FILE_JPEG_LIST:
                file = new FileJPEG(this->asset, this);
                break;
-
+#ifdef HAVE_GIFLIB
        case FILE_GIF:
-       case FILE_GIF_LIST:
                file = new FileGIF(this->asset, this);
                break;
+       case FILE_GIF_LIST:
+               file = new FileGIFList(this->asset, this);
+               break;
+#endif
+#ifdef HAVE_LIBDPX
+       case FILE_DPX:
+       case FILE_DPX_LIST:
+               file = new FileDPX(this->asset, this);
+               break;
+#endif
 #ifdef HAVE_OPENEXR
        case FILE_EXR:
        case FILE_EXR_LIST:
                file = new FileEXR(this->asset, this);
                break;
 #endif
+
        case FILE_FLAC:
                file = new FileFLAC(this->asset, this);
                break;
@@ -591,6 +664,7 @@ int File::open_file(Preferences *preferences,
                file = new FileMPEG(this->asset, this);
                break;
 #endif
+#ifdef HAVE_OGG
        case FILE_OGG:
                file = new FileOGG(this->asset, this);
                break;
@@ -598,11 +672,15 @@ int File::open_file(Preferences *preferences,
        case FILE_VORBIS:
                file = new FileVorbis(this->asset, this);
                break;
+#endif
 #ifdef HAVE_DV
        case FILE_RAWDV:
                file = new FileDV(this->asset, this);
                break;
 #endif
+       case FILE_REF:
+               file = new FileREF(this->asset, this);
+               break;
 // try plugins
        default:
                return 1;
@@ -617,6 +695,17 @@ int File::open_file(Preferences *preferences,
        }
 
 
+// If file type is a list verify that all files match in dimensions.
+// Should be done only after the file open function has been performed
+// Reason: although this function checks if file exists or not but
+// it has no way of relaying this information back and if this function
+// is called before open_file the program may accidently interpret file
+// not found as file size don't match
+ if( !file->verify_file_list() ) {
+  delete file;  file = 0;
+  return FILE_SIZE_DONT_MATCH;
+ }
+
 
 // Set extra writing parameters to mandatory settings.
        if( wr ) {
@@ -683,17 +772,10 @@ void File::delete_temp_frame_buffer()
 
 int File::close_file(int ignore_thread)
 {
-       const int debug = 0;
-
-       if( debug ) printf("File::close_file file=%p %d\n", file, __LINE__);
-
        if( !ignore_thread ) {
                stop_audio_thread();
                stop_video_thread();
        }
-
-
-       if( debug ) printf("File::close_file file=%p %d\n", file, __LINE__);
        if( file ) {
 // The file's asset is a copy of the argument passed to open_file so the
 // user must copy lengths from the file's asset.
@@ -705,28 +787,18 @@ int File::close_file(int ignore_thread)
                file->close_file();
                delete file;
        }
-       if( debug ) printf("File::close_file file=%p %d\n", file, __LINE__);
 
        delete_temp_samples_buffer();
        delete_temp_frame_buffer();
-       if( debug ) printf("File::close_file file=%p %d\n", file, __LINE__);
-
-       if( debug ) printf("File::close_file file=%p %d\n", file, __LINE__);
-
        reset_parameters();
-       if( debug ) printf("File::close_file file=%p %d\n", file, __LINE__);
        return 0;
 }
 
-
-
 int File::get_index(IndexFile *index_file, MainProgressBar *progress_bar)
 {
        return !file ? -1 : file->get_index(index_file, progress_bar);
 }
 
-
-
 int File::start_audio_thread(int buffer_size, int ring_buffers)
 {
        this->audio_ring_buffers = ring_buffers;
@@ -739,20 +811,16 @@ int File::start_audio_thread(int buffer_size, int ring_buffers)
        return 0;
 }
 
-int File::start_video_thread(int buffer_size,
-       int color_model,
-       int ring_buffers,
-       int compressed)
+int File::start_video_thread(int buffer_size, int color_model,
+               int ring_buffers, int compressed)
 {
        this->video_ring_buffers = ring_buffers;
        this->video_buffer_size = buffer_size;
 
        if( !video_thread ) {
                video_thread = new FileThread(this, 0, 1);
-               video_thread->start_writing(buffer_size,
-                       color_model,
-                       ring_buffers,
-                       compressed);
+               video_thread->start_writing(buffer_size, color_model,
+                               ring_buffers, compressed);
        }
        return 0;
 }
@@ -1152,51 +1220,52 @@ int File::read_frame(VFrame *frame, int is_thread)
        if( debug ) PRINT_TRACE
        int result = 0;
        int supported_colormodel = colormodel_supported(frame->get_color_model());
-       int advance_position = 1;
-       int cache_active = use_cache || asset->single_frame ? 1 : 0;
+       int do_read = 1;
+// if reverse playback reading, use_cache is -1
+       int cache_active = asset->single_frame ? 1 : use_cache;
        int64_t cache_position = !asset->single_frame ? current_frame : -1;
 
 // Test cache
-       if( cache_active && frame_cache->get_frame(frame, cache_position,
-                       current_layer, asset->frame_rate) ) {
-// Can't advance position if cache used.
-//printf("File::read_frame %d\n", __LINE__);
-               advance_position = 0;
+       if( cache_active ) {
+               if( get_cache_frame(frame, cache_position) ) {
+                       do_read = 0;
+               }
+               else if( cache_active < 0 && frame_cache->cache_items() > 0 ) {
+// reverse reading and cache miss, clear cache for new readahead
+                       purge_cache();
+               }
        }
-// Need temp
-       else if( frame->get_color_model() != BC_COMPRESSED &&
-               (supported_colormodel != frame->get_color_model() ||
-               (!file->can_scale_input() &&
-                       (frame->get_w() != asset->width ||
-                        frame->get_h() != asset->height))) ) {
-
-//                     printf("File::read_frame %d\n", __LINE__);
-// Can't advance position here because it needs to be added to cache
-               if( temp_frame ) {
-                       if( !temp_frame->params_match(asset->width, asset->height, supported_colormodel) ) {
-                               delete temp_frame;
-                               temp_frame = 0;
+// Need to read
+       if( do_read ) {
+               VFrame *vframe = frame;
+               if( frame->get_color_model() != BC_COMPRESSED &&
+                   (supported_colormodel != frame->get_color_model() ||
+                       (!file->can_scale_input() &&
+                               (frame->get_w() != asset->width ||
+                                frame->get_h() != asset->height))) ) {
+                       if( temp_frame ) {
+                               if( !temp_frame->params_match(asset->width, asset->height,
+                                               supported_colormodel) ) {
+                                       delete temp_frame;  temp_frame = 0;
+                               }
+                       }
+                       if( !temp_frame ) {
+                               temp_frame = new VFrame(asset->width, asset->height,
+                                               supported_colormodel, 0);
+                               temp_frame->clear_frame();
                        }
-               }
 
-               if( !temp_frame ) {
-                       temp_frame = new VFrame(asset->width, asset->height, supported_colormodel, 0);
+                       temp_frame->copy_stacks(frame);
+                       vframe = temp_frame;
                }
-
-//                     printf("File::read_frame %d\n", __LINE__);
-               temp_frame->copy_stacks(frame);
-               result = file->read_frame(temp_frame);
-               if( !result )
-                       frame->transfer_from(temp_frame);
-               else if( result && frame->get_status() > 0 )
-                       frame->set_status(-1);
-//printf("File::read_frame %d\n", __LINE__);
-       }
-       else {
-// Can't advance position here because it needs to be added to cache
-//printf("File::read_frame %d\n", __LINE__);
-               result = file->read_frame(frame);
-               if( result && frame->get_status() > 0 )
+               result = file->read_frame(vframe);
+               if( !result ) {
+                       if( frame != vframe )
+                               frame->transfer_from(vframe);
+                       if( cache_active > 0 )
+                               put_cache_frame(frame, cache_position, 1);
+               }
+               else if( frame->get_status() > 0 )
                        frame->set_status(-1);
 //for( int i = 0; i < 100 * 1000; i++ ) ((float*)frame->get_rows()[0])[i] = 1.0;
        }
@@ -1204,12 +1273,9 @@ int File::read_frame(VFrame *frame, int is_thread)
        if( result && !current_frame )
                frame->clear_frame();
 
-       if( cache_active && advance_position && frame->get_status() > 0 )
-               frame_cache->put_frame(frame, cache_position,
-                       current_layer, asset->frame_rate, 1, 0);
 //printf("File::read_frame %d\n", __LINE__);
 
-       if( advance_position ) current_frame++;
+       if( do_read ) current_frame++;
        if( debug ) PRINT_TRACE
        return 0;
 }
@@ -1244,9 +1310,13 @@ int File::strtoformat(const char *format)
        if( !strcasecmp(format, _(TIFF_LIST_NAME)) ) return FILE_TIFF_LIST;
        if( !strcasecmp(format, _(JPEG_NAME)) ) return FILE_JPEG;
        if( !strcasecmp(format, _(JPEG_LIST_NAME)) ) return FILE_JPEG_LIST;
+       if( !strcasecmp(format, _(DPX_NAME)) ) return FILE_DPX;
+       if( !strcasecmp(format, _(DPX_LIST_NAME)) ) return FILE_DPX_LIST;
        if( !strcasecmp(format, _(EXR_NAME)) ) return FILE_EXR;
        if( !strcasecmp(format, _(EXR_LIST_NAME)) ) return FILE_EXR_LIST;
        if( !strcasecmp(format, _(FLAC_NAME)) ) return FILE_FLAC;
+       if( !strcasecmp(format, _(GIF_NAME)) ) return FILE_GIF;
+       if( !strcasecmp(format, _(GIF_LIST_NAME)) ) return FILE_GIF_LIST;
        if( !strcasecmp(format, _(CR2_NAME)) ) return FILE_CR2;
        if( !strcasecmp(format, _(CR2_LIST_NAME)) ) return FILE_CR2_LIST;
        if( !strcasecmp(format, _(MPEG_NAME)) ) return FILE_MPEG;
@@ -1259,6 +1329,7 @@ int File::strtoformat(const char *format)
        if( !strcasecmp(format, _(RAWDV_NAME)) ) return FILE_RAWDV;
        if( !strcasecmp(format, _(FFMPEG_NAME)) ) return FILE_FFMPEG;
        if( !strcasecmp(format, _(DBASE_NAME)) ) return FILE_DB;
+       if( !strcasecmp(format, _(REF_NAME)) ) return FILE_REF;
 
        return 0;
 }
@@ -1283,6 +1354,10 @@ const char* File::formattostr(int format)
        case FILE_CR2:          return _(CR2_NAME);
        case FILE_CR2_LIST:     return _(CR2_LIST_NAME);
        case FILE_FLAC:         return _(FLAC_NAME);
+       case FILE_GIF:          return _(GIF_NAME);
+       case FILE_GIF_LIST:     return _(GIF_LIST_NAME);
+       case FILE_DPX:          return _(DPX_NAME);
+       case FILE_DPX_LIST:     return _(DPX_LIST_NAME);
        case FILE_EXR:          return _(EXR_NAME);
        case FILE_EXR_LIST:     return _(EXR_LIST_NAME);
 #ifdef HAVE_LIBZMPEG
@@ -1294,11 +1369,14 @@ const char* File::formattostr(int format)
        case FILE_TGA_LIST:     return _(TGA_LIST_NAME);
        case FILE_TIFF:         return _(TIFF_NAME);
        case FILE_TIFF_LIST:    return _(TIFF_LIST_NAME);
+#ifdef HAVE_OGG
        case FILE_OGG:          return _(OGG_NAME);
        case FILE_VORBIS:       return _(VORBIS_NAME);
+#endif
        case FILE_RAWDV:        return _(RAWDV_NAME);
        case FILE_FFMPEG:       return _(FFMPEG_NAME);
        case FILE_DB:           return _(DBASE_NAME);
+       case FILE_REF:          return _(REF_NAME);
        }
        return _("Unknown");
 }
@@ -1374,6 +1452,10 @@ int File::get_best_colormodel(Asset *asset, int driver)
 #endif
        case FILE_JPEG:
        case FILE_JPEG_LIST:    return FileJPEG::get_best_colormodel(asset, driver);
+#ifdef HAVE_LIBDPX
+       case FILE_DPX:
+       case FILE_DPX_LIST:     return FileDPX::get_best_colormodel(asset, driver);     
+#endif
 #ifdef HAVE_OPENEXR
        case FILE_EXR:
        case FILE_EXR_LIST:     return FileEXR::get_best_colormodel(asset, driver);
@@ -1390,6 +1472,7 @@ int File::get_best_colormodel(Asset *asset, int driver)
        case FILE_DB:           return FileDB::get_best_colormodel(asset, driver);
 #endif
        case FILE_FFMPEG:       return FileFFMPEG::get_best_colormodel(asset, driver);
+       case FILE_REF:          return FileREF::get_best_colormodel(asset, driver);
        }
 
        return BC_RGB888;
@@ -1434,6 +1517,8 @@ int File::renders_video(int format)
        case FILE_CR2_LIST:
        case FILE_EXR:
        case FILE_EXR_LIST:
+       case FILE_GIF:
+       case FILE_GIF_LIST:
        case FILE_PNG:
        case FILE_PNG_LIST:
        case FILE_PPM:
@@ -1506,25 +1591,30 @@ const char* File::get_tag(int format)
        case FILE_AU:           return "au";
        case FILE_RAWDV:        return "dv";
        case FILE_DB:           return "db";
+       case FILE_DPX:          return "dpx";
        case FILE_EXR:          return "exr";
-       case FILE_EXR_LIST:     return "exr";
+       case FILE_DPX_LIST:     return "dpxs";
+       case FILE_EXR_LIST:     return "exrs";
        case FILE_FLAC:         return "flac";
        case FILE_JPEG:         return "jpg";
-       case FILE_JPEG_LIST:    return "jpg";
+       case FILE_JPEG_LIST:    return "jpgs";
        case FILE_OGG:          return "ogg";
+       case FILE_GIF:          return "gif";
+       case FILE_GIF_LIST:     return "gifs";
        case FILE_PCM:          return "pcm";
        case FILE_PNG:          return "png";
-       case FILE_PNG_LIST:     return "png";
+       case FILE_PNG_LIST:     return "pngs";
        case FILE_PPM:          return "ppm";
-       case FILE_PPM_LIST:     return "ppm";
+       case FILE_PPM_LIST:     return "ppms";
        case FILE_TGA:          return "tga";
-       case FILE_TGA_LIST:     return "tga";
+       case FILE_TGA_LIST:     return "tgas";
        case FILE_TIFF:         return "tif";
-       case FILE_TIFF_LIST:    return "tif";
+       case FILE_TIFF_LIST:    return "tifs";
        case FILE_VMPEG:        return "m2v";
        case FILE_VORBIS:       return "ogg";
        case FILE_WAV:          return "wav";
        case FILE_FFMPEG:       return "ffmpg";
+       case FILE_REF:          return "ref";
        }
        return 0;
 }
@@ -1553,6 +1643,8 @@ const char* File::get_prefix(int format)
        case FILE_PNG_LIST:     return "PNG_LIST";
        case FILE_PPM_LIST:     return "PPM_LIST";
        case FILE_AC3:          return "AC3";
+       case FILE_DPX:          return "DPX";
+       case FILE_DPX_LIST:     return "DPX_LIST";
        case FILE_EXR:          return "EXR";
        case FILE_EXR_LIST:     return "EXR_LIST";
        case FILE_CR2:          return "CR2";
@@ -1564,27 +1656,12 @@ const char* File::get_prefix(int format)
        case FILE_CR2_LIST:     return "CR2_LIST";
        case FILE_GIF_LIST:     return "GIF_LIST";
        case FILE_DB:           return "DB";
+       case FILE_REF:          return "REF";
        }
        return _("UNKNOWN");
 }
 
 
-PackagingEngine *File::new_packaging_engine(Asset *asset)
-{
-       PackagingEngine *result;
-       switch( asset->format ) {
-       case FILE_OGG:
-               result = (PackagingEngine*)new PackagingEngineOGG();
-               break;
-       default:
-               result = (PackagingEngine*) new PackagingEngineDefault();
-               break;
-       }
-
-       return result;
-}
-
-
 int File::record_fd()
 {
        return file ? file->record_fd() : -1;
@@ -1594,7 +1671,14 @@ int File::record_fd()
 void File::get_exe_path(char *result, char *bnp)
 {
 // Get executable path, basename
+#if !defined(__FreeBSD__)
        int len = readlink("/proc/self/exe", result, BCTEXTLEN-1);
+#else
+       char exe_path[BCTEXTLEN];
+       sprintf(exe_path,"/proc/%d/file",getpid());
+       int len = readlink(exe_path, result, BCTEXTLEN-1);
+#endif
+
        if( len >= 0 ) {
                result[len] = 0;
                char *ptr = strrchr(result, '/');
@@ -1637,11 +1721,15 @@ void File::setenv_path(const char *var, const char *path, int overwrite)
        getenv_path(env_path, path);
        setenv(var, env_path, overwrite);
 }
-
-void File::init_cin_path()
+/**
+* @brief Set various environment variables to pass constant values to
+*        forks, shell scripts, and other parts of CinGG.
+*/
+void File::init_cin_env_vars()
 {
-       char env_path[BCTEXTLEN], env_pkg[BCTEXTLEN];
-// these values are advertised for forks/shell scripts
+       char env_path[BCTEXTLEN], env_pkg[BCTEXTLEN], build_name[32], dateTimeText[32];
+       struct stat st;
+       struct tm *dateTime;
        get_exe_path(env_path, env_pkg);
        setenv_path("CIN_PATH", env_path, 1);
        setenv_path("CIN_PKG", env_pkg, 1);
@@ -1652,5 +1740,12 @@ void File::init_cin_path()
        setenv_path("CIN_LADSPA", LADSPA_DIR, 0);
        setenv_path("CIN_LOCALE", LOCALE_DIR, 0);
        setenv_path("CIN_BROWSER", CIN_BROWSER, 0);
+// Create env_var CINGG_BUILD for use when running as AppImage
+       stat(env_path, &st);
+       dateTime = gmtime(&st.st_mtime);
+       strftime(dateTimeText, sizeof(dateTimeText), "%Y%m%d_%H%M%S", dateTime);
+    snprintf(build_name, sizeof(build_name), "CINGG_%s", dateTimeText);
+       build_name[sizeof(build_name) - 1] = 0;
+       setenv_path("CINGG_BUILD", build_name, 1);
 }