2 Copyright (C) 2010, 2013 Paul Davis
3 Author: Robin Gareus <robin@gareus.org>
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.
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.
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.
20 #include <sigc++/bind.h>
21 #include "ardour/tempo.h"
23 #include "ardour_ui.h"
24 #include "video_image_frame.h"
25 #include "public_editor.h"
27 #include "canvas/group.h"
28 #include "utils_videotl.h"
30 #include <gtkmm2ext/utils.h>
36 using namespace ARDOUR;
38 VideoImageFrame::VideoImageFrame (PublicEditor& ed, ArdourCanvas::Group& parent, int w, int h, std::string vsurl, std::string vfn)
43 , video_server_url(vsurl)
46 pthread_mutex_init(&request_lock, NULL);
47 pthread_mutex_init(&queue_lock, NULL);
49 video_frame_number = -1;
54 unit_position = editor.sample_to_pixel (frame_position);
55 image = new ArdourCanvas::Image (_parent, Cairo::FORMAT_ARGB32, clip_width, clip_height);
57 img = image->get_image();
61 image->put_image(img);
63 image->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_videotl_bar_event), _parent));
66 VideoImageFrame::~VideoImageFrame ()
68 if (thread_active) pthread_join(thread_id_tt, NULL);
70 pthread_mutex_destroy(&request_lock);
71 pthread_mutex_destroy(&queue_lock);
75 VideoImageFrame::set_position (framepos_t frame)
77 double new_unit_position = editor.sample_to_pixel (frame);
78 image->move (ArdourCanvas::Duple (new_unit_position - unit_position, 0.0));
79 frame_position = frame;
80 unit_position = new_unit_position;
84 VideoImageFrame::reposition ()
86 set_position (frame_position);
90 VideoImageFrame::exposeimg () {
91 //ImgChanged(); /* EMIT SIGNAL */
95 VideoImageFrame::set_videoframe (framepos_t videoframenumber, int re)
97 if (video_frame_number == videoframenumber && rightend == re) return;
99 video_frame_number = videoframenumber;
102 img = image->get_image();
107 image->put_image(img);
110 /* request video-frame from decoder in background thread */
111 http_get(video_frame_number);
115 VideoImageFrame::draw_line ()
117 const int rowstride = img->stride;
118 const int clip_height = img->height;
120 pixels = img->data.get();
123 for (y = 0;y < clip_height; y++) {
124 p = pixels + y * rowstride;
125 p[0] = 255; p[1] = 255; p[2] = 255; p[3] = 255;
130 VideoImageFrame::fill_frame (const uint8_t r, const uint8_t g, const uint8_t b)
132 const int rowstride = img->stride;
133 const int clip_height = img->height;
134 const int clip_width = img->width;
136 pixels = img->data.get();
139 for (y = 0; y < clip_height; ++y) {
140 for (x = 0; x < clip_width; ++x) {
141 p = pixels + y * rowstride + x * 4;
142 p[0] = b; p[1] = g; p[2] = r; p[3] = 255;
148 VideoImageFrame::draw_x ()
151 const int rowstride = img->stride;
152 const int clip_width = img->width;
153 const int clip_height = img->height;
155 pixels = img->data.get();
157 for (x = 0;x < clip_width; x++) {
158 y = clip_height * x / clip_width;
159 p = pixels + y * rowstride + x * 4;
160 p[0] = 192; p[1] = 192; p[2] = 192; p[3] = 255;
161 p = pixels + y * rowstride + (clip_width-x-1) * 4;
162 p[0] = 192; p[1] = 192; p[2] = 192; p[3] = 255;
167 VideoImageFrame::cut_rightend ()
170 if (rightend < 0 ) { return; }
172 const int rowstride = img->stride;
173 const int clip_height = img->height;
174 const int clip_width = img->width;
176 pixels = img->data.get();
177 if (rightend > clip_width) { return; }
180 for (y = 0;y < clip_height; ++y) {
181 p = pixels + y * rowstride + rightend * 4;
182 p[0] = 192; p[1] = 192; p[2] = 192; p[3] = 255;
183 for (x=rightend+1; x < clip_width; ++x) {
184 p = pixels + y * rowstride + x * 4;
185 p[0] = 0; p[1] = 0; p[2] = 0; p[3] = 0;
191 http_get_thread (void *arg) {
192 VideoImageFrame *vif = static_cast<VideoImageFrame *>(arg);
194 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
195 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
196 snprintf(url, sizeof(url), "%s?frame=%li&w=%d&h=%di&file=%s&format=bgra",
197 vif->get_video_server_url().c_str(),
198 (long int) vif->get_req_frame(), vif->get_width(), vif->get_height(),
199 vif->get_video_filename().c_str()
202 int timeout = 1000; // * 5ms -> 5sec
205 res=curl_http_get(url, &status);
206 if (status == 503) usleep(5000); // try-again
207 } while (status == 503 && --timeout > 0);
209 if (status != 200 || !res) {
210 printf("no-video frame: video-server returned http-status: %d\n", status);
213 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
214 vif->http_download_done(res);
220 VideoImageFrame::http_download_done (char *data){
221 if (queued_request) {
222 http_maybe_get_again();
227 /* Image request failed (HTTP error or timeout) */
228 img = image->get_image();
229 fill_frame(128, 0, 0);
234 image->put_image(img);
236 img = image->get_image();
237 /* TODO - have curl write directly to the shared memory region */
238 memcpy((void*) img->data.get(), data, img->stride * img->height);
242 image->put_image(img);
246 /* don't request frames rapidly, wait after user has zoomed */
249 if (queued_request) {
250 http_maybe_get_again();
252 pthread_mutex_unlock(&request_lock);
257 VideoImageFrame::http_get(framepos_t fn) {
258 if (pthread_mutex_trylock(&request_lock)) {
259 pthread_mutex_lock(&queue_lock);
261 want_video_frame_number=fn;
262 pthread_mutex_unlock(&queue_lock);
265 if (thread_active) pthread_join(thread_id_tt, NULL);
266 pthread_mutex_lock(&queue_lock);
267 queued_request=false;
268 req_video_frame_number=fn;
269 pthread_mutex_unlock(&queue_lock);
270 int rv = pthread_create(&thread_id_tt, NULL, http_get_thread, this);
274 printf("thread creation failed. %i\n",rv);
275 http_download_done(NULL);
280 VideoImageFrame::http_maybe_get_again() {
281 pthread_mutex_lock(&queue_lock);
282 queued_request=false;
283 req_video_frame_number=want_video_frame_number;
284 pthread_mutex_unlock(&queue_lock);
286 http_get_thread(this);
291 #include <curl/curl.h>
293 struct MemoryStruct {
299 WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) {
300 size_t realsize = size * nmemb;
301 struct MemoryStruct *mem = (struct MemoryStruct *)data;
303 mem->data = (char *)realloc(mem->data, mem->size + realsize + 1);
305 memcpy(&(mem->data[mem->size]), ptr, realsize);
306 mem->size += realsize;
307 mem->data[mem->size] = 0;
312 char *curl_http_get (const char *u, int *status) {
315 struct MemoryStruct chunk;
317 if (status) *status = 0;
318 if (strncmp("http://", u, 7)) return NULL;
323 curl = curl_easy_init();
324 if(!curl) return NULL;
325 curl_easy_setopt(curl, CURLOPT_URL, u);
327 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
328 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
329 curl_easy_setopt(curl, CURLOPT_USERAGENT, ARDOUR_USER_AGENT);
330 curl_easy_setopt(curl, CURLOPT_TIMEOUT, ARDOUR_CURL_TIMEOUT);
331 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
332 #ifdef CURLERRORDEBUG
333 char curlerror[CURL_ERROR_SIZE] = "";
334 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curlerror);
337 res = curl_easy_perform(curl);
338 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpstatus);
339 curl_easy_cleanup(curl);
340 if (status) *status = httpstatus;
342 #ifdef CURLERRORDEBUG
343 printf("curl_http_get() failed: %s\n", curlerror);
347 if (httpstatus != 200) {
354 } /* end extern "C" */