aboutsummaryrefslogtreecommitdiff
path: root/src/source.c
blob: 6203dfe1b0848f85d608d90901382322f5fdf24f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include "source.h"
#include "source_private.h"

#include <pthread.h>
#include <stdlib.h>

struct imv_source {
  /* pointers to implementation's functions */
  const struct imv_source_vtable *vtable;

  /* pointer to implementation data */
  void *private;

  /* Attempted to be locked by load_first_frame or load_next_frame.
   * If the mutex can't be locked, the call is aborted.
   * Used to prevent the source from having multiple worker threads at once.
   * Released by the source before calling the message callback with a result.
   */
  pthread_mutex_t busy;

  /* callback function */
  imv_source_callback callback;
  /* callback data */
  void *callback_data;
};

struct imv_source *imv_source_create(const struct imv_source_vtable *vtable, void *private)
{
  struct imv_source *source = calloc(1, sizeof *source);
  source->vtable = vtable;
  source->private = private;
  pthread_mutex_init(&source->busy, NULL);
  return source;
}

static void *free_thread(void *src)
{
  imv_source_free(src);
  return NULL;
}

void imv_source_async_free(struct imv_source *src)
{
  pthread_t thread;
  pthread_create(&thread, NULL, free_thread, src);
  pthread_detach(thread);
}

static void *first_frame_thread(void *src)
{
  imv_source_load_first_frame(src);
  return NULL;
}

void imv_source_async_load_first_frame(struct imv_source *src)
{
  pthread_t thread;
  pthread_create(&thread, NULL, first_frame_thread, src);
  pthread_detach(thread);
}

static void *next_frame_thread(void *src)
{
  imv_source_load_next_frame(src);
  return NULL;
}
void imv_source_async_load_next_frame(struct imv_source *src)
{
  pthread_t thread;
  pthread_create(&thread, NULL, next_frame_thread, src);
  pthread_detach(thread);
}

void imv_source_free(struct imv_source *src)
{
  pthread_mutex_lock(&src->busy);
  src->vtable->free(src->private);
  pthread_mutex_unlock(&src->busy);
  pthread_mutex_destroy(&src->busy);
  free(src);
}

void imv_source_load_first_frame(struct imv_source *src)
{
  if (!src->vtable->load_first_frame) {
    return;
  }

  if (pthread_mutex_trylock(&src->busy)) {
    return;
  }

  struct imv_source_message msg = {
    .source = src,
    .user_data = src->callback_data
  };

  src->vtable->load_first_frame(src->private, &msg.image, &msg.frametime);

  pthread_mutex_unlock(&src->busy);

  src->callback(&msg);
}

void imv_source_load_next_frame(struct imv_source *src)
{
  if (!src->vtable->load_next_frame) {
    return;
  }

  if (pthread_mutex_trylock(&src->busy)) {
    return;
  }

  struct imv_source_message msg = {
    .source = src,
    .user_data = src->callback_data
  };

  src->vtable->load_next_frame(src->private, &msg.image, &msg.frametime);

  pthread_mutex_unlock(&src->busy);

  src->callback(&msg);
}

void imv_source_set_callback(struct imv_source *src, imv_source_callback callback,
    void *data)
{
  src->callback = callback;
  src->callback_data = data;
}