Remove old log file.
[dcpomatic.git] / src / lib / util.cc
1 /*
2     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
3     Copyright (C) 2000-2007 Paul Davis
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 /** @file src/lib/util.cc
22  *  @brief Some utility functions and classes.
23  */
24
25 #include <sstream>
26 #include <iomanip>
27 #include <iostream>
28 #include <fstream>
29 #ifdef DVDOMATIC_POSIX
30 #include <execinfo.h>
31 #include <cxxabi.h>
32 #endif
33 #include <libssh/libssh.h>
34 #include <signal.h>
35 #include <boost/algorithm/string.hpp>
36 #include <boost/bind.hpp>
37 #include <boost/lambda/lambda.hpp>
38 #include <boost/lexical_cast.hpp>
39 #include <boost/thread.hpp>
40 #include <boost/filesystem.hpp>
41 #include <openjpeg.h>
42 #include <openssl/md5.h>
43 #include <magick/MagickCore.h>
44 #include <magick/version.h>
45 #include <libdcp/version.h>
46 extern "C" {
47 #include <libavcodec/avcodec.h>
48 #include <libavformat/avformat.h>
49 #include <libswscale/swscale.h>
50 #include <libavfilter/avfiltergraph.h>
51 #include <libpostproc/postprocess.h>
52 #include <libavutil/pixfmt.h>
53 }
54 #include "util.h"
55 #include "exceptions.h"
56 #include "scaler.h"
57 #include "format.h"
58 #include "dcp_content_type.h"
59 #include "filter.h"
60 #include "screen.h"
61 #include "sound_processor.h"
62 #ifndef DVDOMATIC_DISABLE_PLAYER
63 #include "player_manager.h"
64 #endif
65
66 using namespace std;
67 using namespace boost;
68
69 thread::id ui_thread;
70
71 /** Convert some number of seconds to a string representation
72  *  in hours, minutes and seconds.
73  *
74  *  @param s Seconds.
75  *  @return String of the form H:M:S (where H is hours, M
76  *  is minutes and S is seconds).
77  */
78 string
79 seconds_to_hms (int s)
80 {
81         int m = s / 60;
82         s -= (m * 60);
83         int h = m / 60;
84         m -= (h * 60);
85
86         stringstream hms;
87         hms << h << ":";
88         hms.width (2);
89         hms << setfill ('0') << m << ":";
90         hms.width (2);
91         hms << setfill ('0') << s;
92
93         return hms.str ();
94 }
95
96 /** @param s Number of seconds.
97  *  @return String containing an approximate description of s (e.g. "about 2 hours")
98  */
99 string
100 seconds_to_approximate_hms (int s)
101 {
102         int m = s / 60;
103         s -= (m * 60);
104         int h = m / 60;
105         m -= (h * 60);
106
107         stringstream ap;
108         
109         if (h > 0) {
110                 if (m > 30) {
111                         ap << (h + 1) << " hours";
112                 } else {
113                         if (h == 1) {
114                                 ap << "1 hour";
115                         } else {
116                                 ap << h << " hours";
117                         }
118                 }
119         } else if (m > 0) {
120                 if (m == 1) {
121                         ap << "1 minute";
122                 } else {
123                         ap << m << " minutes";
124                 }
125         } else {
126                 ap << s << " seconds";
127         }
128
129         return ap.str ();
130 }
131
132 #ifdef DVDOMATIC_POSIX
133 /** @param l Mangled C++ identifier.
134  *  @return Demangled version.
135  */
136 static string
137 demangle (string l)
138 {
139         string::size_type const b = l.find_first_of ("(");
140         if (b == string::npos) {
141                 return l;
142         }
143
144         string::size_type const p = l.find_last_of ("+");
145         if (p == string::npos) {
146                 return l;
147         }
148
149         if ((p - b) <= 1) {
150                 return l;
151         }
152         
153         string const fn = l.substr (b + 1, p - b - 1);
154
155         int status;
156         try {
157                 
158                 char* realname = abi::__cxa_demangle (fn.c_str(), 0, 0, &status);
159                 string d (realname);
160                 free (realname);
161                 return d;
162                 
163         } catch (std::exception) {
164                 
165         }
166         
167         return l;
168 }
169
170 /** Write a stacktrace to an ostream.
171  *  @param out Stream to write to.
172  *  @param levels Number of levels to go up the call stack.
173  */
174 void
175 stacktrace (ostream& out, int levels)
176 {
177         void *array[200];
178         size_t size;
179         char **strings;
180         size_t i;
181      
182         size = backtrace (array, 200);
183         strings = backtrace_symbols (array, size);
184      
185         if (strings) {
186                 for (i = 0; i < size && (levels == 0 || i < size_t(levels)); i++) {
187                         out << "  " << demangle (strings[i]) << endl;
188                 }
189                 
190                 free (strings);
191         }
192 }
193 #endif
194
195 /** @return Version of vobcopy that is on the path (and hence that we will use) */
196 static string
197 vobcopy_version ()
198 {
199         FILE* f = popen ("vobcopy -V 2>&1", "r");
200         if (f == 0) {
201                 throw EncodeError ("could not run vobcopy to check version");
202         }
203
204         string version = "unknown";
205         
206         while (!feof (f)) {
207                 char buf[256];
208                 if (fgets (buf, sizeof (buf), f)) {
209                         string s (buf);
210                         vector<string> b;
211                         split (b, s, is_any_of (" "));
212                         if (b.size() >= 2 && b[0] == "Vobcopy") {
213                                 version = b[1];
214                         }
215                 }
216         }
217
218         pclose (f);
219
220         return version;
221 }
222
223 /** @param v Version as used by FFmpeg.
224  *  @return A string representation of v.
225  */
226 static string
227 ffmpeg_version_to_string (int v)
228 {
229         stringstream s;
230         s << ((v & 0xff0000) >> 16) << "." << ((v & 0xff00) >> 8) << "." << (v & 0xff);
231         return s.str ();
232 }
233
234 /** Return a user-readable string summarising the versions of our dependencies */
235 string
236 dependency_version_summary ()
237 {
238         stringstream s;
239         s << "libopenjpeg " << opj_version () << ", "
240           << "vobcopy " << vobcopy_version() << ", "
241           << "libavcodec " << ffmpeg_version_to_string (avcodec_version()) << ", "
242           << "libavfilter " << ffmpeg_version_to_string (avfilter_version()) << ", "
243           << "libavformat " << ffmpeg_version_to_string (avformat_version()) << ", "
244           << "libavutil " << ffmpeg_version_to_string (avutil_version()) << ", "
245           << "libpostproc " << ffmpeg_version_to_string (postproc_version()) << ", "
246           << "libswscale " << ffmpeg_version_to_string (swscale_version()) << ", "
247           << MagickVersion << ", "
248           << "libssh " << ssh_version (0) << ", "
249           << "libdcp " << libdcp::version << " git " << libdcp::git_commit;
250
251         return s.str ();
252 }
253
254 double
255 seconds (struct timeval t)
256 {
257         return t.tv_sec + (double (t.tv_usec) / 1e6);
258 }
259
260
261 #ifdef DVDOMATIC_POSIX
262 void
263 sigchld_handler (int, siginfo_t* info, void *)
264 {
265 #ifndef DVDOMATIC_DISABLE_PLAYER        
266         PlayerManager::instance()->child_exited (info->si_pid);
267 #endif  
268 }
269 #endif
270
271 /** Call the required functions to set up DVD-o-matic's static arrays, etc.
272  *  Must be called from the UI thread, if there is one.
273  */
274 void
275 dvdomatic_setup ()
276 {
277         Format::setup_formats ();
278         DCPContentType::setup_dcp_content_types ();
279         Scaler::setup_scalers ();
280         Filter::setup_filters ();
281         SoundProcessor::setup_sound_processors ();
282
283         ui_thread = this_thread::get_id ();
284
285 #ifdef DVDOMATIC_POSIX  
286         struct sigaction sa;
287         sa.sa_flags = SA_SIGINFO;
288         sigemptyset (&sa.sa_mask);
289         sa.sa_sigaction = sigchld_handler;
290         sigaction (SIGCHLD, &sa, 0);
291 #endif  
292 }
293
294 /** @param start Start position for the crop within the image.
295  *  @param size Size of the cropped area.
296  *  @return FFmpeg crop filter string.
297  */
298 string
299 crop_string (Position start, Size size)
300 {
301         stringstream s;
302         s << "crop=" << size.width << ":" << size.height << ":" << start.x << ":" << start.y;
303         return s.str ();
304 }
305
306 /** @param s A string.
307  *  @return Parts of the string split at spaces, except when a space is within quotation marks.
308  */
309 vector<string>
310 split_at_spaces_considering_quotes (string s)
311 {
312         vector<string> out;
313         bool in_quotes = false;
314         string c;
315         for (string::size_type i = 0; i < s.length(); ++i) {
316                 if (s[i] == ' ' && !in_quotes) {
317                         out.push_back (c);
318                         c = "";
319                 } else if (s[i] == '"') {
320                         in_quotes = !in_quotes;
321                 } else {
322                         c += s[i];
323                 }
324         }
325
326         out.push_back (c);
327         return out;
328 }
329
330 string
331 md5_digest (void const * data, int size)
332 {
333         MD5_CTX md5_context;
334         MD5_Init (&md5_context);
335         MD5_Update (&md5_context, data, size);
336         unsigned char digest[MD5_DIGEST_LENGTH];
337         MD5_Final (digest, &md5_context);
338         
339         stringstream s;
340         for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
341                 s << hex << setfill('0') << setw(2) << ((int) digest[i]);
342         }
343
344         return s.str ();
345 }
346
347 /** @param file File name.
348  *  @return MD5 digest of file's contents.
349  */
350 string
351 md5_digest (string file)
352 {
353         ifstream f (file.c_str(), ios::binary);
354         if (!f.good ()) {
355                 throw OpenFileError (file);
356         }
357         
358         f.seekg (0, ios::end);
359         int bytes = f.tellg ();
360         f.seekg (0, ios::beg);
361
362         int const buffer_size = 64 * 1024;
363         char buffer[buffer_size];
364
365         MD5_CTX md5_context;
366         MD5_Init (&md5_context);
367         while (bytes > 0) {
368                 int const t = min (bytes, buffer_size);
369                 f.read (buffer, t);
370                 MD5_Update (&md5_context, buffer, t);
371                 bytes -= t;
372         }
373
374         unsigned char digest[MD5_DIGEST_LENGTH];
375         MD5_Final (digest, &md5_context);
376
377         stringstream s;
378         for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
379                 s << hex << setfill('0') << setw(2) << ((int) digest[i]);
380         }
381
382         return s.str ();
383 }
384
385 /** @param fps Arbitrary frames-per-second value.
386  *  @return DCPFrameRate for this frames-per-second.
387  */
388 DCPFrameRate
389 dcp_frame_rate (float fps)
390 {
391         DCPFrameRate dfr;
392
393         dfr.run_fast = (fps != rint (fps));
394         dfr.frames_per_second = rint (fps);
395         dfr.skip = 1;
396
397         /* XXX: somewhat arbitrary */
398         if (fps == 50) {
399                 dfr.frames_per_second = 25;
400                 dfr.skip = 2;
401         }
402
403         return dfr;
404 }
405
406 /** @param An arbitrary sampling rate.
407  *  @return The appropriate DCP-approved sampling rate (48kHz or 96kHz).
408  */
409 int
410 dcp_audio_sample_rate (int fs)
411 {
412         if (fs <= 48000) {
413                 return 48000;
414         }
415
416         return 96000;
417 }
418
419 bool operator== (Size const & a, Size const & b)
420 {
421         return (a.width == b.width && a.height == b.height);
422 }
423
424 bool operator== (Crop const & a, Crop const & b)
425 {
426         return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom);
427 }
428
429 bool operator!= (Crop const & a, Crop const & b)
430 {
431         return !(a == b);
432 }
433
434 /** @param index Colour LUT index.
435  *  @return Human-readable name.
436  */
437 string
438 colour_lut_index_to_name (int index)
439 {
440         switch (index) {
441         case 0:
442                 return "sRGB";
443         case 1:
444                 return "Rec 709";
445         }
446
447         assert (false);
448         return "";
449 }
450
451 Socket::Socket ()
452         : _deadline (_io_service)
453         , _socket (_io_service)
454         , _buffer_data (0)
455 {
456         _deadline.expires_at (posix_time::pos_infin);
457         check ();
458 }
459
460 void
461 Socket::check ()
462 {
463         if (_deadline.expires_at() <= asio::deadline_timer::traits_type::now ()) {
464                 _socket.close ();
465                 _deadline.expires_at (posix_time::pos_infin);
466         }
467
468         _deadline.async_wait (boost::bind (&Socket::check, this));
469 }
470
471 /** Blocking connect with timeout.
472  *  @param endpoint End-point to connect to.
473  *  @param timeout Time-out in seconds.
474  */
475 void
476 Socket::connect (asio::ip::basic_resolver_entry<asio::ip::tcp> const & endpoint, int timeout)
477 {
478         system::error_code ec = asio::error::would_block;
479         _socket.async_connect (endpoint, lambda::var(ec) = lambda::_1);
480         do {
481                 _io_service.run_one();
482         } while (ec == asio::error::would_block);
483
484         if (ec || !_socket.is_open ()) {
485                 throw NetworkError ("connect timed out");
486         }
487 }
488
489 /** Blocking write with timeout.
490  *  @param data Buffer to write.
491  *  @param size Number of bytes to write.
492  *  @param timeout Time-out, in seconds.
493  */
494 void
495 Socket::write (uint8_t const * data, int size, int timeout)
496 {
497         _deadline.expires_from_now (posix_time::seconds (timeout));
498         system::error_code ec = asio::error::would_block;
499
500         asio::async_write (_socket, asio::buffer (data, size), lambda::var(ec) = lambda::_1);
501         do {
502                 _io_service.run_one ();
503         } while (ec == asio::error::would_block);
504
505         if (ec) {
506                 throw NetworkError ("write timed out");
507         }
508 }
509
510 /** Blocking read with timeout.
511  *  @param data Buffer to read to.
512  *  @param size Number of bytes to read.
513  *  @param timeout Time-out, in seconds.
514  */
515 int
516 Socket::read (uint8_t* data, int size, int timeout)
517 {
518         _deadline.expires_from_now (posix_time::seconds (timeout));
519         system::error_code ec = asio::error::would_block;
520
521         int amount_read = 0;
522
523         _socket.async_read_some (
524                 asio::buffer (data, size),
525                 (lambda::var(ec) = lambda::_1, lambda::var(amount_read) = lambda::_2)
526                 );
527
528         do {
529                 _io_service.run_one ();
530         } while (ec == asio::error::would_block);
531         
532         if (ec) {
533                 amount_read = 0;
534         }
535
536         return amount_read;
537 }
538
539 /** Mark some data as being `consumed', so that it will not be returned
540  *  as data again.
541  *  @param size Amount of data to consume, in bytes.
542  */
543 void
544 Socket::consume (int size)
545 {
546         assert (_buffer_data >= size);
547         
548         _buffer_data -= size;
549         if (_buffer_data > 0) {
550                 /* Shift still-valid data to the start of the buffer */
551                 memmove (_buffer, _buffer + size, _buffer_data);
552         }
553 }
554
555 /** Read a definite amount of data from our socket, and mark
556  *  it as consumed.
557  *  @param data Where to put the data.
558  *  @param size Number of bytes to read.
559  */
560 void
561 Socket::read_definite_and_consume (uint8_t* data, int size, int timeout)
562 {
563         int const from_buffer = min (_buffer_data, size);
564         if (from_buffer > 0) {
565                 /* Get data from our buffer */
566                 memcpy (data, _buffer, from_buffer);
567                 consume (from_buffer);
568                 /* Update our output state */
569                 data += from_buffer;
570                 size -= from_buffer;
571         }
572
573         /* read() the rest */
574         while (size > 0) {
575                 int const n = read (data, size, timeout);
576                 if (n <= 0) {
577                         throw NetworkError ("could not read");
578                 }
579
580                 data += n;
581                 size -= n;
582         }
583 }
584
585 /** Read as much data as is available, up to some limit.
586  *  @param data Where to put the data.
587  *  @param size Maximum amount of data to read.
588  */
589 void
590 Socket::read_indefinite (uint8_t* data, int size, int timeout)
591 {
592         assert (size < int (sizeof (_buffer)));
593
594         /* Amount of extra data we need to read () */
595         int to_read = size - _buffer_data;
596         while (to_read > 0) {
597                 /* read as much of it as we can (into our buffer) */
598                 int const n = read (_buffer + _buffer_data, to_read, timeout);
599                 if (n <= 0) {
600                         throw NetworkError ("could not read");
601                 }
602
603                 to_read -= n;
604                 _buffer_data += n;
605         }
606
607         assert (_buffer_data >= size);
608
609         /* copy data into the output buffer */
610         assert (size >= _buffer_data);
611         memcpy (data, _buffer, size);
612 }
613
614 /** @param other A Rect.
615  *  @return The intersection of this with `other'.
616  */
617 Rect
618 Rect::intersection (Rect const & other) const
619 {
620         int const tx = max (x, other.x);
621         int const ty = max (y, other.y);
622         
623         return Rect (
624                 tx, ty,
625                 min (x + width, other.x + other.width) - tx,
626                 min (y + height, other.y + other.height) - ty
627                 );
628 }
629
630 /** Round a number up to the nearest multiple of another number.
631  *  @param a Number to round.
632  *  @param t Multiple to round to.
633  *  @return Rounded number.
634  */
635 int
636 stride_round_up (int c, int const * stride, int t)
637 {
638         int const a = stride[c] + (t - 1);
639         return a - (a % t);
640 }
641
642 int
643 stride_lookup (int c, int const * stride)
644 {
645         return stride[c];
646 }
647
648 /** Read a sequence of key / value pairs from a text stream;
649  *  the keys are the first words on the line, and the values are
650  *  the remainder of the line following the key.  Lines beginning
651  *  with # are ignored.
652  *  @param s Stream to read.
653  *  @return key/value pairs.
654  */
655 multimap<string, string>
656 read_key_value (istream &s) 
657 {
658         multimap<string, string> kv;
659         
660         string line;
661         while (getline (s, line)) {
662                 if (line.empty ()) {
663                         continue;
664                 }
665
666                 if (line[0] == '#') {
667                         continue;
668                 }
669
670                 if (line[line.size() - 1] == '\r') {
671                         line = line.substr (0, line.size() - 1);
672                 }
673
674                 size_t const s = line.find (' ');
675                 if (s == string::npos) {
676                         continue;
677                 }
678
679                 kv.insert (make_pair (line.substr (0, s), line.substr (s + 1)));
680         }
681
682         return kv;
683 }
684
685 string
686 get_required_string (multimap<string, string> const & kv, string k)
687 {
688         if (kv.count (k) > 1) {
689                 throw StringError ("unexpected multiple keys in key-value set");
690         }
691
692         multimap<string, string>::const_iterator i = kv.find (k);
693         
694         if (i == kv.end ()) {
695                 throw StringError (String::compose ("missing key %1 in key-value set", k));
696         }
697
698         return i->second;
699 }
700
701 int
702 get_required_int (multimap<string, string> const & kv, string k)
703 {
704         string const v = get_required_string (kv, k);
705         return lexical_cast<int> (v);
706 }
707
708 float
709 get_required_float (multimap<string, string> const & kv, string k)
710 {
711         string const v = get_required_string (kv, k);
712         return lexical_cast<float> (v);
713 }
714
715 string
716 get_optional_string (multimap<string, string> const & kv, string k)
717 {
718         if (kv.count (k) > 1) {
719                 throw StringError ("unexpected multiple keys in key-value set");
720         }
721
722         multimap<string, string>::const_iterator i = kv.find (k);
723         if (i == kv.end ()) {
724                 return "";
725         }
726
727         return i->second;
728 }
729
730 int
731 get_optional_int (multimap<string, string> const & kv, string k)
732 {
733         if (kv.count (k) > 1) {
734                 throw StringError ("unexpected multiple keys in key-value set");
735         }
736
737         multimap<string, string>::const_iterator i = kv.find (k);
738         if (i == kv.end ()) {
739                 return 0;
740         }
741
742         return lexical_cast<int> (i->second);
743 }
744
745 /** Construct an AudioBuffers.  Audio data is undefined after this constructor.
746  *  @param channels Number of channels.
747  *  @param frames Number of frames to reserve space for.
748  */
749 AudioBuffers::AudioBuffers (int channels, int frames)
750         : _channels (channels)
751         , _frames (frames)
752         , _allocated_frames (frames)
753 {
754         _data = new float*[_channels];
755         for (int i = 0; i < _channels; ++i) {
756                 _data[i] = new float[frames];
757         }
758 }
759
760 /** Copy constructor.
761  *  @param other Other AudioBuffers; data is copied.
762  */
763 AudioBuffers::AudioBuffers (AudioBuffers const & other)
764         : _channels (other._channels)
765         , _frames (other._frames)
766         , _allocated_frames (other._frames)
767 {
768         _data = new float*[_channels];
769         for (int i = 0; i < _channels; ++i) {
770                 _data[i] = new float[_frames];
771                 memcpy (_data[i], other._data[i], _frames * sizeof (float));
772         }
773 }
774
775 /** AudioBuffers destructor */
776 AudioBuffers::~AudioBuffers ()
777 {
778         for (int i = 0; i < _channels; ++i) {
779                 delete[] _data[i];
780         }
781
782         delete[] _data;
783 }
784
785 /** @param c Channel index.
786  *  @return Buffer for this channel.
787  */
788 float*
789 AudioBuffers::data (int c) const
790 {
791         assert (c >= 0 && c < _channels);
792         return _data[c];
793 }
794
795 /** Set the number of frames that these AudioBuffers will report themselves
796  *  as having.
797  *  @param f Frames; must be less than or equal to the number of allocated frames.
798  */
799 void
800 AudioBuffers::set_frames (int f)
801 {
802         assert (f <= _allocated_frames);
803         _frames = f;
804 }
805
806 /** Make all samples on all channels silent */
807 void
808 AudioBuffers::make_silent ()
809 {
810         for (int i = 0; i < _channels; ++i) {
811                 make_silent (i);
812         }
813 }
814
815 /** Make all samples on a given channel silent.
816  *  @param c Channel.
817  */
818 void
819 AudioBuffers::make_silent (int c)
820 {
821         assert (c >= 0 && c < _channels);
822         
823         for (int i = 0; i < _frames; ++i) {
824                 _data[c][i] = 0;
825         }
826 }
827
828 /** Copy data from another AudioBuffers to this one.  All channels are copied.
829  *  @param from AudioBuffers to copy from; must have the same number of channels as this.
830  *  @param frames_to_copy Number of frames to copy.
831  *  @param read_offset Offset to read from in `from'.
832  *  @param write_offset Offset to write to in `to'.
833  */
834 void
835 AudioBuffers::copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset)
836 {
837         assert (from->channels() == channels());
838
839         assert (from);
840         assert (read_offset >= 0 && (read_offset + frames_to_copy) <= from->_allocated_frames);
841         assert (write_offset >= 0 && (write_offset + frames_to_copy) <= _allocated_frames);
842
843         for (int i = 0; i < _channels; ++i) {
844                 memcpy (_data[i] + write_offset, from->_data[i] + read_offset, frames_to_copy * sizeof(float));
845         }
846 }
847
848 /** Move audio data around.
849  *  @param from Offset to move from.
850  *  @param to Offset to move to.
851  *  @param frames Number of frames to move.
852  */
853     
854 void
855 AudioBuffers::move (int from, int to, int frames)
856 {
857         if (frames == 0) {
858                 return;
859         }
860         
861         assert (from >= 0);
862         assert (from < _frames);
863         assert (to >= 0);
864         assert (to < _frames);
865         assert (frames > 0);
866         assert (frames <= _frames);
867         assert ((from + frames) <= _frames);
868         assert ((to + frames) <= _frames);
869         
870         for (int i = 0; i < _channels; ++i) {
871                 memmove (_data[i] + to, _data[i] + from, frames * sizeof(float));
872         }
873 }
874
875 /** Trip an assert if the caller is not in the UI thread */
876 void
877 ensure_ui_thread ()
878 {
879         assert (this_thread::get_id() == ui_thread);
880 }
881
882 /** @param v Source video frame.
883  *  @param audio_sample_rate Source audio sample rate.
884  *  @param frames_per_second Number of video frames per second.
885  *  @return Equivalent number of audio frames for `v'.
886  */
887 int64_t
888 video_frames_to_audio_frames (SourceFrame v, float audio_sample_rate, float frames_per_second)
889 {
890         return ((int64_t) v * audio_sample_rate / frames_per_second);
891 }
892
893 /** @param f Filename.
894  *  @return true if this file is a still image, false if it is something else.
895  */
896 bool
897 still_image_file (string f)
898 {
899 #if BOOST_FILESYSTEM_VERSION == 3
900         string ext = boost::filesystem::path(f).extension().string();
901 #else
902         string ext = boost::filesystem::path(f).extension();
903 #endif
904
905         transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
906         
907         return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png");
908 }