thumbnailer.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*****************************************************************************
  2. * thumbnailer.c
  3. *****************************************************************************
  4. * Copyright © 2011-2012 VLC authors and VideoLAN
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  19. *****************************************************************************/
  20. #include <assert.h>
  21. #include <jni.h>
  22. #include <vlc/vlc.h>
  23. #include <pthread.h>
  24. #include <stdbool.h>
  25. #include <time.h>
  26. #include <errno.h>
  27. #include <unistd.h>
  28. #define LOG_TAG "VLC/JNI/thumbnailer"
  29. #include "log.h"
  30. #include "utils.h"
  31. #define THUMBNAIL_POSITION 0.5
  32. #define PIXEL_SIZE 4 /* RGBA */
  33. /*
  34. Frame is: thumbnail + black borders
  35. frameData = frameWidth * frameHeight (values given by Java UI)
  36. ┌————————————————————————————————————————————————————┐
  37. │ │
  38. │ Black Borders │
  39. │ │
  40. ├————————————————————————————————————————————————————┤
  41. │ │
  42. │ Thumbnail Data │
  43. │ │
  44. │ thumbHeight x thumbWidth │
  45. │ thumbPitch = thumbWidth * 4 │
  46. │ │
  47. ├————————————————————————————————————————————————————┤
  48. │ │
  49. │ │
  50. │ │
  51. └————————————————————————————————————————————————————┘
  52. */
  53. enum {
  54. THUMB_SEEKING,
  55. THUMB_SEEKED,
  56. THUMB_DROP_FIRST_FRAME,
  57. THUMB_DONE,
  58. };
  59. typedef struct
  60. {
  61. int state;
  62. char *thumbData;
  63. char *frameData;
  64. unsigned blackBorders;
  65. unsigned frameWidth;
  66. unsigned thumbHeight;
  67. unsigned thumbPitch;
  68. pthread_mutex_t doneMutex;
  69. pthread_cond_t doneCondVar;
  70. } thumbnailer_sys_t;
  71. /**
  72. * Thumbnailer vout lock
  73. **/
  74. static void *thumbnailer_lock(void *opaque, void **pixels)
  75. {
  76. thumbnailer_sys_t *sys = opaque;
  77. *pixels = sys->thumbData;
  78. return NULL;
  79. }
  80. /**
  81. * Thumbnailer vout unlock
  82. **/
  83. static void thumbnailer_unlock(void *opaque, void *picture, void *const *pixels)
  84. {
  85. thumbnailer_sys_t *sys = opaque;
  86. /* If we have already received a thumbnail, or we are still seeking,
  87. * we skip this frame. */
  88. pthread_mutex_lock(&sys->doneMutex);
  89. int state = sys->state;
  90. if (state == THUMB_SEEKED)
  91. sys->state = THUMB_DROP_FIRST_FRAME;
  92. pthread_mutex_unlock(&sys->doneMutex);
  93. if (state != THUMB_DROP_FIRST_FRAME)
  94. return;
  95. /* we have received our first thumbnail and we can exit. */
  96. const char *dataSrc = sys->thumbData;
  97. char *dataDest = sys->frameData + sys->blackBorders * PIXEL_SIZE;
  98. /* Copy the thumbnail. */
  99. for (unsigned i = 0; i < sys->thumbHeight; ++i)
  100. {
  101. memcpy(dataDest, dataSrc, sys->thumbPitch);
  102. dataDest += sys->frameWidth * PIXEL_SIZE;
  103. dataSrc += sys->thumbPitch;
  104. }
  105. /* Signal that the thumbnail was created. */
  106. pthread_mutex_lock(&sys->doneMutex);
  107. sys->state = THUMB_DONE;
  108. pthread_cond_signal(&sys->doneCondVar);
  109. pthread_mutex_unlock(&sys->doneMutex);
  110. }
  111. /**
  112. * Thumbnailer main function.
  113. * return null if the thumbail generation failed.
  114. **/
  115. jbyteArray Java_org_videolan_vlc_LibVLC_getThumbnail(JNIEnv *env, jobject thiz,
  116. jlong instance, jstring filePath,
  117. const jint frameWidth, const jint frameHeight)
  118. {
  119. libvlc_instance_t *libvlc = (libvlc_instance_t *)(intptr_t)instance;
  120. jbyteArray byteArray = NULL;
  121. /* Create the thumbnailer data structure */
  122. thumbnailer_sys_t *sys = calloc(1, sizeof(thumbnailer_sys_t));
  123. if (sys == NULL)
  124. {
  125. LOGE("Could not create the thumbnailer data structure!");
  126. return NULL;
  127. }
  128. /* Initialize the barrier. */
  129. pthread_mutex_init(&sys->doneMutex, NULL);
  130. pthread_cond_init(&sys->doneCondVar, NULL);
  131. /* Create a media player playing environment */
  132. libvlc_media_player_t *mp = libvlc_media_player_new(libvlc);
  133. libvlc_media_t *m = new_media(instance, env, thiz, filePath, true, false);
  134. if (m == NULL)
  135. {
  136. LOGE("Could not create the media to play!");
  137. goto end;
  138. }
  139. /* Fast and no options */
  140. libvlc_media_add_option( m, ":no-audio" );
  141. libvlc_media_add_option( m, ":no-spu" );
  142. libvlc_media_add_option( m, ":no-osd" );
  143. libvlc_media_player_set_media(mp, m);
  144. /* Get the size of the video with the tracks information of the media. */
  145. libvlc_media_track_info_t *tracks;
  146. libvlc_media_parse(m);
  147. int nbTracks = libvlc_media_get_tracks_info(m, &tracks);
  148. libvlc_media_release(m);
  149. /* Parse the results */
  150. unsigned videoWidth = 0, videoHeight = 0;
  151. bool hasVideoTrack = false;
  152. for (unsigned i = 0; i < nbTracks; ++i)
  153. if (tracks[i].i_type == libvlc_track_video)
  154. {
  155. videoWidth = tracks[i].u.video.i_width;
  156. videoHeight = tracks[i].u.video.i_height;
  157. hasVideoTrack = true;
  158. break;
  159. }
  160. free(tracks);
  161. /* Abort if we have not found a video track. */
  162. if (!hasVideoTrack)
  163. {
  164. LOGE("Could not find any video track in this file.\n");
  165. goto end;
  166. }
  167. LOGD("Video dimensions: %ix%i.\n", videoWidth, videoHeight );
  168. /* VLC could not tell us the size */
  169. if( videoWidth == 0 || videoHeight == 0 )
  170. {
  171. LOGE("Could not find the video dimensions.\n");
  172. goto end;
  173. }
  174. if( videoWidth < 32 || videoHeight < 32 || videoWidth > 2048 || videoWidth > 2048 )
  175. {
  176. LOGE("Wrong video dimensions.\n");
  177. goto end;
  178. }
  179. /* Compute the size parameters of the frame to generate. */
  180. unsigned thumbWidth = frameWidth;
  181. unsigned thumbHeight = frameHeight;
  182. const float inputAR = (float)videoWidth / videoHeight;
  183. const float screenAR = (float)frameWidth / frameHeight;
  184. /* Most of the cases, video is wider than tall */
  185. if (screenAR < inputAR)
  186. {
  187. thumbHeight = (float)frameWidth / inputAR + 1;
  188. sys->blackBorders = ( (frameHeight - thumbHeight) / 2 ) * frameWidth;
  189. }
  190. else
  191. {
  192. LOGD("Weird aspect Ratio.\n");
  193. thumbWidth = (float)frameHeight * inputAR;
  194. sys->blackBorders = (frameWidth - thumbWidth) / 2;
  195. }
  196. sys->thumbPitch = thumbWidth * PIXEL_SIZE;
  197. sys->thumbHeight = thumbHeight;
  198. sys->frameWidth = frameWidth;
  199. /* Allocate the memory to store the frames. */
  200. size_t thumbSize = sys->thumbPitch * (sys->thumbHeight+1);
  201. sys->thumbData = malloc(thumbSize);
  202. if (sys->thumbData == NULL)
  203. {
  204. LOGE("Could not allocate the memory to store the frame!");
  205. goto end;
  206. }
  207. /* Allocate the memory to store the thumbnail. */
  208. unsigned frameSize = frameWidth * frameHeight * PIXEL_SIZE;
  209. sys->frameData = calloc(frameSize, 1);
  210. if (sys->frameData == NULL)
  211. {
  212. LOGE("Could not allocate the memory to store the thumbnail!");
  213. goto end;
  214. }
  215. /* Set the video format and the callbacks. */
  216. libvlc_video_set_format(mp, "RGBA", thumbWidth, thumbHeight, sys->thumbPitch);
  217. libvlc_video_set_callbacks(mp, thumbnailer_lock, thumbnailer_unlock,
  218. NULL, (void*)sys);
  219. sys->state = THUMB_SEEKING;
  220. /* Play the media. */
  221. libvlc_media_player_play(mp);
  222. libvlc_media_player_set_position(mp, THUMBNAIL_POSITION);
  223. int loops = 100;
  224. for (;;) {
  225. float pos = libvlc_media_player_get_position(mp);
  226. if (pos > THUMBNAIL_POSITION || !loops--)
  227. break;
  228. usleep(50000);
  229. }
  230. /* Wait for the thumbnail to be generated. */
  231. pthread_mutex_lock(&sys->doneMutex);
  232. sys->state = THUMB_SEEKED;
  233. struct timespec deadline;
  234. clock_gettime(CLOCK_REALTIME, &deadline);
  235. deadline.tv_sec += 10; /* amount of seconds before we abort thumbnailer */
  236. do {
  237. int ret = pthread_cond_timedwait(&sys->doneCondVar, &sys->doneMutex, &deadline);
  238. if (ret == ETIMEDOUT)
  239. break;
  240. } while (sys->state != THUMB_DONE);
  241. pthread_mutex_unlock(&sys->doneMutex);
  242. /* Stop and release the media player. */
  243. libvlc_media_player_stop(mp);
  244. libvlc_media_player_release(mp);
  245. if (sys->state == THUMB_DONE) {
  246. /* Create the Java byte array to return the create thumbnail. */
  247. byteArray = (*env)->NewByteArray(env, frameSize);
  248. if (byteArray == NULL)
  249. {
  250. LOGE("Could not allocate the Java byte array to store the frame!");
  251. goto end;
  252. }
  253. (*env)->SetByteArrayRegion(env, byteArray, 0, frameSize,
  254. (jbyte *)sys->frameData);
  255. }
  256. end:
  257. pthread_mutex_destroy(&sys->doneMutex);
  258. pthread_cond_destroy(&sys->doneCondVar);
  259. free(sys->frameData);
  260. free(sys->thumbData);
  261. free(sys);
  262. return byteArray;
  263. }