1/* poppler-media.cc: glib interface to MediaRendition
2 *
3 * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.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, or (at your option)
8 * 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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21
22#include <cerrno>
23
24#include <goo/gfile.h>
25
26#include "poppler-media.h"
27#include "poppler-private.h"
28
29/**
30 * SECTION: poppler-media
31 * @short_description: Media
32 * @title: PopplerMedia
33 */
34
35typedef struct _PopplerMediaClass PopplerMediaClass;
36
37struct _PopplerMedia
38{
39 GObject parent_instance;
40
41 gchar *filename;
42 gboolean auto_play;
43 gboolean show_controls;
44 gfloat repeat_count;
45
46 gchar *mime_type;
47 Object stream;
48};
49
50struct _PopplerMediaClass
51{
52 GObjectClass parent_class;
53};
54
55G_DEFINE_TYPE(PopplerMedia, poppler_media, G_TYPE_OBJECT)
56
57static void poppler_media_finalize(GObject *object)
58{
59 PopplerMedia *media = POPPLER_MEDIA(object);
60
61 if (media->filename) {
62 g_free(mem: media->filename);
63 media->filename = nullptr;
64 }
65
66 if (media->mime_type) {
67 g_free(mem: media->mime_type);
68 media->mime_type = nullptr;
69 }
70
71 media->stream = Object();
72
73 G_OBJECT_CLASS(poppler_media_parent_class)->finalize(object);
74}
75
76static void poppler_media_class_init(PopplerMediaClass *klass)
77{
78 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
79
80 gobject_class->finalize = poppler_media_finalize;
81}
82
83static void poppler_media_init(PopplerMedia *media) { }
84
85PopplerMedia *_poppler_media_new(const MediaRendition *poppler_media)
86{
87 PopplerMedia *media;
88
89 g_assert(poppler_media != nullptr);
90
91 media = POPPLER_MEDIA(g_object_new(POPPLER_TYPE_MEDIA, nullptr));
92
93 if (poppler_media->getIsEmbedded()) {
94 const GooString *mime_type;
95
96 media->stream = poppler_media->getEmbbededStreamObject()->copy();
97 mime_type = poppler_media->getContentType();
98 if (mime_type) {
99 media->mime_type = g_strdup(str: mime_type->c_str());
100 }
101 } else {
102 media->filename = g_strdup(str: poppler_media->getFileName()->c_str());
103 }
104
105 const MediaParameters *mp = poppler_media->getBEParameters();
106 mp = mp ? mp : poppler_media->getMHParameters();
107
108 media->auto_play = mp ? mp->autoPlay : false;
109 media->show_controls = mp ? mp->showControls : false;
110 media->repeat_count = mp ? mp->repeatCount : 1.f;
111
112 return media;
113}
114
115/**
116 * poppler_media_get_filename:
117 * @poppler_media: a #PopplerMedia
118 *
119 * Returns the media clip filename, in case of non-embedded media. filename might be
120 * a local relative or absolute path or a URI
121 *
122 * Return value: a filename, return value is owned by #PopplerMedia and should not be freed
123 *
124 * Since: 0.14
125 */
126const gchar *poppler_media_get_filename(PopplerMedia *poppler_media)
127{
128 g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), NULL);
129 g_return_val_if_fail(!poppler_media->stream.isStream(), NULL);
130
131 return poppler_media->filename;
132}
133
134/**
135 * poppler_media_is_embedded:
136 * @poppler_media: a #PopplerMedia
137 *
138 * Whether the media clip is embedded in the PDF. If the result is %TRUE, the embedded stream
139 * can be saved with poppler_media_save() or poppler_media_save_to_callback() function.
140 * If the result is %FALSE, the media clip filename can be retrieved with
141 * poppler_media_get_filename() function.
142 *
143 * Return value: %TRUE if media clip is embedded, %FALSE otherwise
144 *
145 * Since: 0.14
146 */
147gboolean poppler_media_is_embedded(PopplerMedia *poppler_media)
148{
149 g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
150
151 return poppler_media->stream.isStream();
152}
153
154/**
155 * poppler_media_get_auto_play:
156 * @poppler_media: a #PopplerMedia
157 *
158 * Returns the auto-play parameter.
159 *
160 * Return value: %TRUE if media should auto-play, %FALSE otherwise
161 *
162 * Since: 20.04.0
163 */
164gboolean poppler_media_get_auto_play(PopplerMedia *poppler_media)
165{
166 g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
167
168 return poppler_media->auto_play;
169}
170
171/**
172 * poppler_media_get_show_controls:
173 * @poppler_media: a #PopplerMedia
174 *
175 * Returns the show controls parameter.
176 *
177 * Return value: %TRUE if media should show controls, %FALSE otherwise
178 *
179 * Since: 20.04.0
180 */
181gboolean poppler_media_get_show_controls(PopplerMedia *poppler_media)
182{
183 g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
184
185 return poppler_media->show_controls;
186}
187
188/**
189 * poppler_media_get_repeat_count:
190 * @poppler_media: a #PopplerMedia
191 *
192 * Returns the repeat count parameter.
193 *
194 * Return value: Repeat count parameter (float)
195 *
196 * Since: 20.04.0
197 */
198gfloat poppler_media_get_repeat_count(PopplerMedia *poppler_media)
199{
200 g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
201
202 return poppler_media->repeat_count;
203}
204
205/**
206 * poppler_media_get_mime_type:
207 * @poppler_media: a #PopplerMedia
208 *
209 * Returns the media clip mime-type
210 *
211 * Return value: the mime-type, return value is owned by #PopplerMedia and should not be freed
212 *
213 * Since: 0.14
214 */
215const gchar *poppler_media_get_mime_type(PopplerMedia *poppler_media)
216{
217 g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), NULL);
218
219 return poppler_media->mime_type;
220}
221
222static gboolean save_helper(const gchar *buf, gsize count, gpointer data, GError **error)
223{
224 FILE *f = (FILE *)data;
225 gsize n;
226
227 n = fwrite(ptr: buf, size: 1, n: count, s: f);
228 if (n != count) {
229 int errsv = errno;
230 g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(err_no: errsv), format: "Error writing to media file: %s", g_strerror(errnum: errsv));
231 return FALSE;
232 }
233
234 return TRUE;
235}
236
237/**
238 * poppler_media_save:
239 * @poppler_media: a #PopplerMedia
240 * @filename: name of file to save
241 * @error: (allow-none): return location for error, or %NULL.
242 *
243 * Saves embedded stream of @poppler_media to a file indicated by @filename.
244 * If @error is set, %FALSE will be returned.
245 * Possible errors include those in the #G_FILE_ERROR domain
246 * and whatever the save function generates.
247 *
248 * Return value: %TRUE, if the file successfully saved
249 *
250 * Since: 0.14
251 */
252gboolean poppler_media_save(PopplerMedia *poppler_media, const char *filename, GError **error)
253{
254 gboolean result;
255 FILE *f;
256
257 g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
258 g_return_val_if_fail(poppler_media->stream.isStream(), FALSE);
259
260 f = openFile(path: filename, mode: "wb");
261
262 if (f == nullptr) {
263 gchar *display_name = g_filename_display_name(filename);
264 g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(errno), format: "Failed to open '%s' for writing: %s", display_name, g_strerror(errno));
265 g_free(mem: display_name);
266 return FALSE;
267 }
268
269 result = poppler_media_save_to_callback(poppler_media, save_func: save_helper, user_data: f, error);
270
271 if (fclose(stream: f) < 0) {
272 gchar *display_name = g_filename_display_name(filename);
273 g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(errno), format: "Failed to close '%s', all data may not have been saved: %s", display_name, g_strerror(errno));
274 g_free(mem: display_name);
275 return FALSE;
276 }
277
278 return result;
279}
280
281#ifndef G_OS_WIN32
282
283/**
284 * poppler_media_save_to_fd:
285 * @poppler_media: a #PopplerMedia
286 * @fd: a valid file descriptor open for writing
287 * @error: (allow-none): return location for error, or %NULL.
288 *
289 * Saves embedded stream of @poppler_media to a file referred to by @fd.
290 * If @error is set, %FALSE will be returned.
291 * Possible errors include those in the #G_FILE_ERROR domain
292 * and whatever the save function generates.
293 * Note that this function takes ownership of @fd; you must not operate on it
294 * again, nor close it.
295 *
296 * Return value: %TRUE, if the file successfully saved
297 *
298 * Since: 21.12.0
299 */
300gboolean poppler_media_save_to_fd(PopplerMedia *poppler_media, int fd, GError **error)
301{
302 gboolean result;
303 FILE *f;
304
305 g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
306 g_return_val_if_fail(poppler_media->stream.isStream(), FALSE);
307
308 f = fdopen(fd: fd, modes: "wb");
309 if (f == nullptr) {
310 int errsv = errno;
311 g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(err_no: errsv), format: "Failed to open FD %d for writing: %s", fd, g_strerror(errnum: errsv));
312 close(fd: fd);
313 return FALSE;
314 }
315
316 result = poppler_media_save_to_callback(poppler_media, save_func: save_helper, user_data: f, error);
317
318 if (fclose(stream: f) < 0) {
319 int errsv = errno;
320 g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(err_no: errsv), format: "Failed to close FD %d, all data may not have been saved: %s", fd, g_strerror(errnum: errsv));
321 return FALSE;
322 }
323
324 return result;
325}
326
327#endif /* !G_OS_WIN32 */
328
329#define BUF_SIZE 1024
330
331/**
332 * poppler_media_save_to_callback:
333 * @poppler_media: a #PopplerMedia
334 * @save_func: (scope call): a function that is called to save each block of data that the save routine generates.
335 * @user_data: user data to pass to the save function.
336 * @error: (allow-none): return location for error, or %NULL.
337 *
338 * Saves embedded stream of @poppler_media by feeding the produced data to @save_func. Can be used
339 * when you want to store the media clip stream to something other than a file, such as
340 * an in-memory buffer or a socket. If @error is set, %FALSE will be
341 * returned. Possible errors include those in the #G_FILE_ERROR domain and
342 * whatever the save function generates.
343 *
344 * Return value: %TRUE, if the save successfully completed
345 *
346 * Since: 0.14
347 */
348gboolean poppler_media_save_to_callback(PopplerMedia *poppler_media, PopplerMediaSaveFunc save_func, gpointer user_data, GError **error)
349{
350 Stream *stream;
351 gchar buf[BUF_SIZE];
352 int i;
353 gboolean eof_reached = FALSE;
354
355 g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
356 g_return_val_if_fail(poppler_media->stream.isStream(), FALSE);
357
358 stream = poppler_media->stream.getStream();
359 stream->reset();
360
361 do {
362 int data;
363
364 for (i = 0; i < BUF_SIZE; i++) {
365 data = stream->getChar();
366 if (data == EOF) {
367 eof_reached = TRUE;
368 break;
369 }
370 buf[i] = data;
371 }
372
373 if (i > 0) {
374 if (!(save_func)(buf, i, user_data, error)) {
375 stream->close();
376 return FALSE;
377 }
378 }
379 } while (!eof_reached);
380
381 stream->close();
382
383 return TRUE;
384}
385

source code of poppler/glib/poppler-media.cc