ardour
utils_videotl.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010-2013 Paul Davis
3  Author: Robin Gareus <robin@gareus.org>
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 #include <cstdio>
21 #include <string>
22 #include <cerrno>
23 #include <gtkmm.h>
24 #include <curl/curl.h>
25 
26 #include "pbd/error.h"
27 #include "ardour/ardour.h"
29 #include "video_image_frame.h"
30 #include "utils_videotl.h"
31 
32 #ifdef WAF_BUILD
33 #include "gtk2ardour-version.h"
34 #endif
35 
36 #ifndef ARDOUR_CURL_TIMEOUT
37 #define ARDOUR_CURL_TIMEOUT (60)
38 #endif
39 #include "i18n.h"
40 
41 using namespace Gtk;
42 using namespace std;
43 using namespace PBD;
44 using namespace ARDOUR;
45 using namespace VideoUtils;
46 
47 bool
48 VideoUtils::confirm_video_outfn (std::string outfn, std::string docroot)
49 {
50  /* replace docroot's '/' to G_DIR_SEPARATOR for the comparison */
51  size_t look_here = 0;
52  size_t found_here;
53  const char ds = G_DIR_SEPARATOR;
54  while((found_here = docroot.find('/', look_here)) != string::npos) {
55  docroot.replace(found_here, 1, std::string(&ds, 1));
56  look_here = found_here + 1;
57  }
58 
59  if (!docroot.empty() && docroot.compare(0, docroot.length(), outfn, 0, docroot.length())) {
60  ArdourDialog confirm (_("Destination is outside Video Server's docroot. "), true);
61  Label m (_("The destination file path is outside of the Video Server's docroot. The file will not be readable by the Video Server. Do you still want to continue?"));
62  confirm.get_vbox()->pack_start (m, true, true);
63  confirm.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
64  confirm.add_button (_("Continue"), Gtk::RESPONSE_ACCEPT);
65  confirm.show_all ();
66  if (confirm.run() == RESPONSE_CANCEL) { return false; }
67  }
68 
69  if (Glib::file_test(outfn, Glib::FILE_TEST_EXISTS)) {
70  ArdourDialog confirm (_("Confirm Overwrite"), true);
71  Label m (_("A file with the same name already exists. Do you want to overwrite it?"));
72  confirm.get_vbox()->pack_start (m, true, true);
73  confirm.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
74  confirm.add_button (_("Overwrite"), Gtk::RESPONSE_ACCEPT);
75  confirm.show_all ();
76  if (confirm.run() == RESPONSE_CANCEL) { return false; }
77  }
78 
79  std::string dir = Glib::path_get_dirname (outfn);
80  if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
81  error << string_compose(_("Cannot create video folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
82  return false;
83  }
84  return true;
85 }
86 
87 std::string
88 VideoUtils::video_dest_dir (const std::string sessiondir, const std::string docroot)
89 {
90  std::string dir = docroot;
91  if (dir.empty() || !dir.compare(0, dir.length(), sessiondir, 0, dir.length())) {
92  dir=sessiondir;
93  }
94  if ((dir.empty() || dir.at(dir.length()-1) != G_DIR_SEPARATOR)) { dir += G_DIR_SEPARATOR; }
95 
96  if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
97  error << string_compose(_("Cannot create video folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
98  }
99  return dir;
100 }
101 
102 std::string
104 {
105  if (config->get_video_advanced_setup()) {
106  return config->get_video_server_docroot();
107  }
108 #ifndef PLATFORM_WINDOWS
109  return X_("/");
110 #else
111  return X_("C:\\");
112 #endif
113 }
114 
115 std::string
117 {
118  if (config->get_video_advanced_setup()) {
119  return config->get_video_server_url();
120  }
121  return X_("http://localhost:1554");
122 }
123 
124 
125 std::string
126 VideoUtils::strip_file_extension (const std::string infile)
127 {
128  std::string rv;
129  char *ext, *bn = strdup(infile.c_str());
130  if ((ext=strrchr(bn, '.'))) {
131  if (!strchr(ext, G_DIR_SEPARATOR)) {
132  *ext = 0;
133  }
134  }
135  rv = std::string(bn);
136  free(bn);
137  return rv;
138 }
139 
140 std::string
141 VideoUtils::get_file_extension (const std::string infile)
142 {
143  std::string rv = "";
144  char *ext, *bn = strdup(infile.c_str());
145  if ((ext=strrchr(bn, '.'))) {
146  if (!strchr(ext, G_DIR_SEPARATOR)) {
147  rv=std::string(ext+1);
148  }
149  }
150  free(bn);
151  return rv;
152 }
153 
154 std::string
155 VideoUtils::video_dest_file (const std::string dir, const std::string infile)
156 {
157  return Glib::build_filename(dir, strip_file_extension(Glib::path_get_basename(infile)) + ".avi");
158 }
159 
160 std::string
161 VideoUtils::video_map_path (std::string server_docroot, std::string filepath)
162 {
163  std::string rv = filepath;
164 
165  /* strip docroot */
166  if (server_docroot.length() > 0) {
167  if (rv.compare(0, server_docroot.length(), server_docroot) == 0 ) {
168  rv = rv.substr(server_docroot.length());
169  }
170  }
171 
172  /* replace all G_DIR_SEPARATOR with '/' */
173  size_t look_here = 0;
174  size_t found_here;
175  while((found_here = rv.find(G_DIR_SEPARATOR, look_here)) != string::npos) {
176  rv.replace(found_here, 1, "/");
177  look_here = found_here + 1;
178  }
179 
180  CURL *curl;
181  char *ue;
182  curl = curl_easy_init();
183  ue = curl_easy_escape(curl, rv.c_str(),rv.length());
184  if (ue) {
185  rv = std::string(ue);
186  curl_free(ue);
187  }
188  curl_easy_cleanup(curl);
189 
190  return rv;
191 }
192 
193 void
194 VideoUtils::ParseCSV (const std::string &csv, std::vector<std::vector<std::string> > &lines)
195 {
196  bool inQuote(false);
197  bool newLine(false);
198  std::string field;
199  lines.clear();
200  std::vector<std::string> line;
201 
202  std::string::const_iterator aChar = csv.begin();
203  while (aChar != csv.end()) {
204  switch (*aChar) {
205  case '"':
206  newLine = false;
207  inQuote = !inQuote;
208  break;
209 
210  case ',':
211  newLine = false;
212  if (inQuote == true) {
213  field += *aChar;
214  } else {
215  line.push_back(field);
216  field.clear();
217  }
218  break;
219 
220  case '\n':
221  case '\r':
222  if (inQuote == true) {
223  field += *aChar;
224  } else {
225  if (newLine == false) {
226  line.push_back(field);
227  lines.push_back(line);
228  field.clear();
229  line.clear();
230  newLine = true;
231  }
232  }
233  break;
234 
235  default:
236  newLine = false;
237  field.push_back(*aChar);
238  break;
239  }
240  aChar++;
241  }
242 
243  if (field.size())
244  line.push_back(field);
245 
246  if (line.size())
247  lines.push_back(line);
248 }
249 
250 bool
252  std::string video_server_url,
253  std::string filepath,
254  double &video_file_fps,
255  long long int &video_duration,
256  double &video_start_offset,
257  double &video_aspect_ratio
258  )
259 {
260  char url[2048];
261 
262  snprintf(url, sizeof(url), "%s%sinfo/?file=%s&format=csv"
263  , video_server_url.c_str()
264  , (video_server_url.length()>0 && video_server_url.at(video_server_url.length()-1) == '/')?"":"/"
265  , filepath.c_str());
266  char *res = a3_curl_http_get(url, NULL);
267  if (!res) {
268  return false;
269  }
270 
271  std::vector<std::vector<std::string> > lines;
272  ParseCSV(std::string(res), lines);
273  free(res);
274 
275  if (lines.empty() || lines.at(0).empty() || lines.at(0).size() != 6) {
276  return false;
277  }
278  if (atoi(lines.at(0).at(0)) != 1) return false; // version
279  video_start_offset = 0.0;
280  video_aspect_ratio = atof (lines.at(0).at(3));
281  video_file_fps = atof (lines.at(0).at(4));
282  video_duration = atoll(lines.at(0).at(5));
283  return true;
284 }
285 
286 void
287 VideoUtils::video_draw_cross (Glib::RefPtr<Gdk::Pixbuf> img)
288 {
289 
290  int rowstride = img->get_rowstride();
291  int n_channels = img->get_n_channels();
292  guchar *pixels, *p;
293  pixels = img->get_pixels();
294 
295  int x,y;
296  int clip_width = img->get_width();
297  int clip_height = img->get_height();
298 
299  for (x=0;x<clip_width;x++) {
300  y = clip_height * x / clip_width;
301  p = pixels + y * rowstride + x * n_channels;
302  p[0] = 192; p[1] = 192; p[2] = 192;
303  if (n_channels>3) p[3] = 255;
304  p = pixels + y * rowstride + (clip_width-x-1) * n_channels;
305  p[0] = 192; p[1] = 192; p[2] = 192;
306  if (n_channels>3) p[3] = 255;
307  }
308 }
309 
310 
311 extern "C" {
312 #include <curl/curl.h>
313 
314  struct A3MemoryStruct {
315  char *data;
316  size_t size;
317  };
318 
319  static size_t
320  WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) {
321  size_t realsize = size * nmemb;
322  struct A3MemoryStruct *mem = (struct A3MemoryStruct *)data;
323 
324  mem->data = (char *)realloc(mem->data, mem->size + realsize + 1);
325  if (mem->data) {
326  memcpy(&(mem->data[mem->size]), ptr, realsize);
327  mem->size += realsize;
328  mem->data[mem->size] = 0;
329  }
330  return realsize;
331  }
332 
333  char *a3_curl_http_get (const char *u, int *status) {
334  CURL *curl;
335  CURLcode res;
336  struct A3MemoryStruct chunk;
337  long int httpstatus;
338  if (status) *status = 0;
339  //Glib::usleep(500000); return NULL; // TEST & DEBUG
340  if (strncmp("http://", u, 7)) return NULL;
341 
342  chunk.data=NULL;
343  chunk.size=0;
344 
345  curl = curl_easy_init();
346  if(!curl) return NULL;
347  curl_easy_setopt(curl, CURLOPT_URL, u);
348 
349  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
350  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
351  curl_easy_setopt(curl, CURLOPT_USERAGENT, PROGRAM_NAME VERSIONSTRING);
352  curl_easy_setopt(curl, CURLOPT_TIMEOUT, ARDOUR_CURL_TIMEOUT);
353  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
354 #ifdef CURLERRORDEBUG
355  char curlerror[CURL_ERROR_SIZE] = "";
356  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curlerror);
357 #endif
358 
359  res = curl_easy_perform(curl);
360  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpstatus);
361  curl_easy_cleanup(curl);
362  if (status) *status = httpstatus;
363  if (res) {
364 #ifdef CURLERRORDEBUG
365  printf("a3_curl_http_get() failed: %s\n", curlerror);
366 #endif
367  return NULL;
368  }
369  if (httpstatus != 200) {
370  free (chunk.data);
371  chunk.data = NULL;
372  }
373  return (chunk.data);
374  }
375 
376 } /* end extern "C" */
std::string video_get_docroot(ARDOUR::RCConfiguration *config)
int atoi(const string &s)
Definition: convert.cc:140
bool video_query_info(std::string video_server_url, std::string filepath, double &video_file_fps, long long int &video_duration, double &video_start_offset, double &video_aspect_ratio)
void video_draw_cross(Glib::RefPtr< Gdk::Pixbuf > img)
std::string video_dest_file(const std::string, const std::string)
int64_t atoll(const string &s)
Definition: convert.cc:152
char * a3_curl_http_get(const char *u, int *status)
Definition: ardour_ui.h:130
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
std::string strip_file_extension(const std::string infile)
void ParseCSV(const std::string &csv, std::vector< std::vector< std::string > > &lines)
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
#define ARDOUR_CURL_TIMEOUT
std::string video_map_path(std::string server_docroot, std::string filepath)
#define _(Text)
Definition: i18n.h:11
#define X_(Text)
Definition: i18n.h:13
bool confirm_video_outfn(std::string, std::string docroot="")
Definition: amp.h:29
common functions used for video-file im/export
std::string video_dest_dir(const std::string, const std::string)
std::string video_get_server_url(ARDOUR::RCConfiguration *config)
Definition: debug.h:30
std::string get_file_extension(const std::string infile)
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
double atof(const string &s)
Definition: convert.cc:158