ardour
video_image_frame.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 <sigc++/bind.h>
21 #include "ardour/tempo.h"
22 
23 #include "ardour_ui.h"
24 #include "video_image_frame.h"
25 #include "public_editor.h"
26 #include "canvas/container.h"
27 #include "utils_videotl.h"
28 
29 #include <gtkmm2ext/utils.h>
30 #include <pthread.h>
31 
32 #include "i18n.h"
33 
34 using namespace std;
35 using namespace ARDOUR;
36 using namespace VideoUtils;
37 
38 static void freedata_cb (uint8_t *d, void* /*arg*/) {
39  /* later this can be used with libharvid
40  * the buffer/videocacheline instead of freeing it
41  */
42  free (d);
43 }
44 
45 VideoImageFrame::VideoImageFrame (PublicEditor& ed, ArdourCanvas::Container& parent, int w, int h, std::string vsurl, std::string vfn)
46  : editor (ed)
47  , _parent(&parent)
48  , clip_width(w)
49  , clip_height(h)
50  , video_server_url(vsurl)
51  , video_filename(vfn)
52 {
53  pthread_mutex_init(&request_lock, NULL);
54  pthread_mutex_init(&queue_lock, NULL);
55  queued_request=false;
56  video_frame_number = -1;
57  rightend = -1;
58  sample_position = 0;
59  thread_active=false;
60 
62  image = new ArdourCanvas::Image (_parent, Cairo::FORMAT_ARGB32, clip_width, clip_height);
63 
64  img = image->get_image();
65  fill_frame(0, 0, 0);
66  draw_line();
67  draw_x();
68  image->put_image(img);
69 
70  image->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_videotl_bar_event), _parent));
71 }
72 
74 {
75  if (thread_active) pthread_join(thread_id_tt, NULL);
76  delete image;
77  pthread_mutex_destroy(&request_lock);
78  pthread_mutex_destroy(&queue_lock);
79 }
80 
81 void
83 {
84  double new_unit_position = editor.sample_to_pixel (sample);
85  image->move (ArdourCanvas::Duple (new_unit_position - unit_position, 0.0));
86  sample_position = sample;
87  unit_position = new_unit_position;
88 }
89 
90 void
92 {
94 }
95 
96 void
98  ImgChanged(); /* EMIT SIGNAL */
99 }
100 
101 void
102 VideoImageFrame::set_videoframe (framepos_t videoframenumber, int re)
103 {
104  if (video_frame_number == videoframenumber && rightend == re) return;
105 
106  video_frame_number = videoframenumber;
107  rightend = re;
108 
109  img = image->get_image();
110  fill_frame(0, 0, 0);
111  draw_x();
112  draw_line();
113  cut_rightend();
114  image->put_image(img);
115  exposeimg();
116 
117  /* request video-frame from decoder in background thread */
119 }
120 
121 void
123 {
124  const int rowstride = img->stride;
125  const int clip_height = img->height;
126  uint8_t *pixels, *p;
127  pixels = img->data;
128 
129  int y;
130  for (y = 0;y < clip_height; y++) {
131  p = pixels + y * rowstride;
132  p[0] = 255; p[1] = 255; p[2] = 255; p[3] = 255;
133  }
134 }
135 
136 void
137 VideoImageFrame::fill_frame (const uint8_t r, const uint8_t g, const uint8_t b)
138 {
139  const int rowstride = img->stride;
140  const int clip_height = img->height;
141  const int clip_width = img->width;
142  uint8_t *pixels, *p;
143  pixels = img->data;
144 
145  int x,y;
146  for (y = 0; y < clip_height; ++y) {
147  for (x = 0; x < clip_width; ++x) {
148  p = pixels + y * rowstride + x * 4;
149  p[0] = b; p[1] = g; p[2] = r; p[3] = 255;
150  }
151  }
152 }
153 
154 void
156 {
157  int x,y;
158  const int rowstride = img->stride;
159  const int clip_width = img->width;
160  const int clip_height = img->height;
161  uint8_t *pixels, *p;
162  pixels = img->data;
163 
164  for (x = 0;x < clip_width; x++) {
165  y = clip_height * x / clip_width;
166  p = pixels + y * rowstride + x * 4;
167  p[0] = 192; p[1] = 192; p[2] = 192; p[3] = 255;
168  p = pixels + y * rowstride + (clip_width-x-1) * 4;
169  p[0] = 192; p[1] = 192; p[2] = 192; p[3] = 255;
170  }
171 }
172 
173 void
175 {
176 
177  if (rightend < 0 ) { return; }
178 
179  const int rowstride = img->stride;
180  const int clip_height = img->height;
181  const int clip_width = img->width;
182  uint8_t *pixels, *p;
183  pixels = img->data;
184  if (rightend > clip_width) { return; }
185 
186  int x,y;
187  for (y = 0;y < clip_height; ++y) {
188  p = pixels + y * rowstride + rightend * 4;
189  p[0] = 192; p[1] = 192; p[2] = 192; p[3] = 255;
190  for (x=rightend+1; x < clip_width; ++x) {
191  p = pixels + y * rowstride + x * 4;
192  p[0] = 0; p[1] = 0; p[2] = 0; p[3] = 0;
193  }
194  }
195 }
196 
197 void *
198 http_get_thread (void *arg) {
199  VideoImageFrame *vif = static_cast<VideoImageFrame *>(arg);
200  char url[2048];
201  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
202  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
203  snprintf(url, sizeof(url), "%s?frame=%li&w=%d&h=%d&file=%s&format=bgra",
204  vif->get_video_server_url().c_str(),
205  (long int) vif->get_req_frame(), vif->get_width(), vif->get_height(),
206  vif->get_video_filename().c_str()
207  );
208  int status = 0;
209  int timeout = 1000; // * 5ms -> 5sec
210  char *res = NULL;
211  do {
212  res=a3_curl_http_get(url, &status);
213  if (status == 503) Glib::usleep(5000); // try-again
214  } while (status == 503 && --timeout > 0);
215 
216  if (status != 200 || !res) {
217  printf("no-video frame: video-server returned http-status: %d\n", status);
218  }
219 
220  pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
221  vif->http_download_done(res);
222  pthread_exit(0);
223  return 0;
224 }
225 
226 void
228  if (queued_request) {
230  return;
231  }
232 
233  if (!data) {
234  /* Image request failed (HTTP error or timeout) */
235  img = image->get_image();
236  fill_frame(128, 0, 0);
237  draw_x();
238  cut_rightend();
239  draw_line();
240  cut_rightend();
241  image->put_image(img);
242  } else {
243  img = image->get_image(false);
244  img->data = (uint8_t*) data;
245  img->destroy_callback = &freedata_cb;
246  draw_line();
247  cut_rightend();
248  image->put_image(img);
249  }
250 
251  exposeimg();
252  /* don't request frames too quickly, wait after user has zoomed */
253  Glib::usleep(40000);
254 
255  if (queued_request) {
257  }
258  pthread_mutex_unlock(&request_lock);
259 }
260 
261 
262 void
264  if (pthread_mutex_trylock(&request_lock)) {
265  pthread_mutex_lock(&queue_lock);
266  queued_request=true;
268  pthread_mutex_unlock(&queue_lock);
269  return;
270  }
271  if (thread_active) pthread_join(thread_id_tt, NULL);
272  pthread_mutex_lock(&queue_lock);
273  queued_request=false;
275  pthread_mutex_unlock(&queue_lock);
276  int rv = pthread_create(&thread_id_tt, NULL, http_get_thread, this);
277  thread_active=true;
278  if (rv) {
279  thread_active=false;
280  printf("thread creation failed. %i\n",rv);
281  http_download_done(NULL);
282  }
283 }
284 
285 void
287  pthread_mutex_lock(&queue_lock);
288  queued_request=false;
290  pthread_mutex_unlock(&queue_lock);
291 
292  http_get_thread(this);
293 }
294 
virtual ~VideoImageFrame()
std::string get_video_server_url()
ArdourCanvas::Image * image
char * a3_curl_http_get(const char *u, int *status)
void set_videoframe(framepos_t, int rightend=-1)
Representation of the interface of the Editor class.
Definition: Beats.hpp:239
virtual double sample_to_pixel(framepos_t frame) const =0
std::string get_video_filename()
a single video-frame to be displayed in the video timeline
pthread_mutex_t queue_lock
boost::shared_ptr< ArdourCanvas::Image::Data > img
pthread_mutex_t request_lock
void http_download_done(char *)
void http_get_again(framepos_t fn)
Definition: amp.h:29
PBD::Signal0< void > ImgChanged
common functions used for video-file im/export
int64_t framepos_t
Definition: types.h:66
framepos_t want_video_frame_number
PublicEditor & editor
void http_get(framepos_t fn)
framepos_t video_frame_number
static void freedata_cb(uint8_t *d, void *)
void * http_get_thread(void *arg)
virtual bool canvas_videotl_bar_event(GdkEvent *event, ArdourCanvas::Item *)=0
framepos_t get_req_frame()
VideoImageFrame(PublicEditor &, ArdourCanvas::Container &, int, int, std::string, std::string)
void set_position(framepos_t)
framepos_t req_video_frame_number
void fill_frame(const uint8_t r, const uint8_t g, const uint8_t b)
int pthread_create(pthread_t *__restrict thread, __const pthread_attr_t *__restrict attr, void *(*start_routine)(void *), void *__restrict arg)
Definition: gprofhelper.c:83
framepos_t sample_position
ArdourCanvas::Container * _parent