]> git.sesse.net Git - vlc/blob - modules/demux/image.c
decoder: do not wait for buffering when there is no data
[vlc] / modules / demux / image.c
1 /*****************************************************************************
2  * image.c: Image demuxer
3  *****************************************************************************
4  * Copyright (C) 2010 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_demux.h>
35 #include <vlc_image.h>
36 #include "mxpeg_helper.h"
37
38 /*****************************************************************************
39  * Module descriptor
40  *****************************************************************************/
41 static int  Open (vlc_object_t *);
42 static void Close(vlc_object_t *);
43
44 #define ID_TEXT N_("ES ID")
45 #define ID_LONGTEXT N_( \
46     "Set the ID of the elementary stream")
47
48 #define GROUP_TEXT N_("Group")
49 #define GROUP_LONGTEXT N_(\
50     "Set the group of the elementary stream")
51
52 #define DECODE_TEXT N_("Decode")
53 #define DECODE_LONGTEXT N_( \
54     "Decode at the demuxer stage")
55
56 #define CHROMA_TEXT N_("Forced chroma")
57 #define CHROMA_LONGTEXT N_( \
58     "If non empty and image-decode is true, the image will be " \
59     "converted to the specified chroma.")
60
61 #define DURATION_TEXT N_("Duration in seconds")
62 #define DURATION_LONGTEXT N_( \
63     "Duration in seconds before simulating an end of file. " \
64     "A negative value means an unlimited play time.")
65
66 #define FPS_TEXT N_("Frame rate")
67 #define FPS_LONGTEXT N_( \
68     "Frame rate of the elementary stream produced.")
69
70 #define RT_TEXT N_("Real-time")
71 #define RT_LONGTEXT N_( \
72     "Use real-time mode suitable for being used as a master input and " \
73     "real-time input slaves.")
74
75 vlc_module_begin()
76     set_description(N_("Image demuxer"))
77     set_shortname(N_("Image"))
78     set_category(CAT_INPUT)
79     set_subcategory(SUBCAT_INPUT_DEMUX)
80     add_integer("image-id", -1, ID_TEXT, ID_LONGTEXT, true)
81         change_safe()
82     add_integer("image-group", 0, GROUP_TEXT, GROUP_LONGTEXT, true)
83         change_safe()
84     add_bool("image-decode", true, DECODE_TEXT, DECODE_LONGTEXT, true)
85         change_safe()
86     add_string("image-chroma", "", CHROMA_TEXT, CHROMA_LONGTEXT, true)
87         change_safe()
88     add_float("image-duration", 10, DURATION_TEXT, DURATION_LONGTEXT, false)
89         change_safe()
90     add_string("image-fps", "10/1", FPS_TEXT, FPS_LONGTEXT, true)
91         change_safe()
92     add_bool("image-realtime", false, RT_TEXT, RT_LONGTEXT, true)
93         change_safe()
94     set_capability("demux", 10)
95     set_callbacks(Open, Close)
96 vlc_module_end()
97
98 /*****************************************************************************
99  * Local prototypes
100  *****************************************************************************/
101 struct demux_sys_t
102 {
103     block_t     *data;
104     es_out_id_t *es;
105     mtime_t     duration;
106     bool        is_realtime;
107     mtime_t     pts_origin;
108     mtime_t     pts_next;
109     date_t        pts;
110 };
111
112 static block_t *Load(demux_t *demux)
113 {
114     const int     max_size = 4096 * 4096 * 8;
115     const int64_t size = stream_Size(demux->s);
116     if (size < 0 || size > max_size) {
117         msg_Err(demux, "Rejecting image based on its size (%"PRId64" > %d)", size, max_size);
118         return NULL;
119     }
120
121     if (size > 0)
122         return stream_Block(demux->s, size);
123     /* TODO */
124     return NULL;
125 }
126
127 static block_t *Decode(demux_t *demux,
128                        video_format_t *fmt, vlc_fourcc_t chroma, block_t *data)
129 {
130     image_handler_t *handler = image_HandlerCreate(demux);
131     if (!handler) {
132         block_Release(data);
133         return NULL;
134     }
135
136     video_format_t decoded;
137     video_format_Init(&decoded, chroma);
138
139     picture_t *image = image_Read(handler, data, fmt, &decoded);
140     image_HandlerDelete(handler);
141
142     if (!image)
143         return NULL;
144
145     video_format_Clean(fmt);
146     *fmt = decoded;
147
148     size_t size = 0;
149     for (int i = 0; i < image->i_planes; i++)
150         size += image->p[i].i_visible_pitch *
151                 image->p[i].i_visible_lines;
152
153     data = block_Alloc(size);
154     if (!data) {
155         picture_Release(image);
156         return NULL;
157     }
158
159     size_t offset = 0;
160     for (int i = 0; i < image->i_planes; i++) {
161         const plane_t *src = &image->p[i];
162         for (int y = 0; y < src->i_visible_lines; y++) {
163             memcpy(&data->p_buffer[offset],
164                    &src->p_pixels[y * src->i_pitch],
165                    src->i_visible_pitch);
166             offset += src->i_visible_pitch;
167         }
168     }
169
170     picture_Release(image);
171     return data;
172 }
173
174 static int Demux(demux_t *demux)
175 {
176     demux_sys_t *sys = demux->p_sys;
177
178     if (!sys->data)
179         return 0;
180
181     mtime_t deadline;
182     const mtime_t pts_first = sys->pts_origin + date_Get(&sys->pts);
183     if (sys->pts_next > VLC_TS_INVALID) {
184         deadline = sys->pts_next;
185     } else if (sys->is_realtime) {
186         deadline = mdate();
187         const mtime_t max_wait = CLOCK_FREQ / 50;
188         if (deadline + max_wait < pts_first) {
189             es_out_Control(demux->out, ES_OUT_SET_PCR, deadline);
190             /* That's ugly, but not yet easily fixable */
191             mwait(deadline + max_wait);
192             return 1;
193         }
194     } else {
195         deadline = 1 + pts_first;
196     }
197
198     for (;;) {
199         const mtime_t pts = sys->pts_origin + date_Get(&sys->pts);
200         if (sys->duration >= 0 && pts >= sys->pts_origin + sys->duration)
201             return 0;
202
203         if (pts >= deadline)
204             return 1;
205
206         block_t *data = block_Duplicate(sys->data);
207         if (!data)
208             return -1;
209
210         data->i_dts =
211         data->i_pts = VLC_TS_0 + pts;
212         es_out_Control(demux->out, ES_OUT_SET_PCR, data->i_pts);
213         es_out_Send(demux->out, sys->es, data);
214
215         date_Increment(&sys->pts, 1);
216     }
217 }
218
219 static int Control(demux_t *demux, int query, va_list args)
220 {
221     demux_sys_t *sys = demux->p_sys;
222
223     switch (query) {
224     case DEMUX_GET_POSITION: {
225         double *position = va_arg(args, double *);
226         if (sys->duration > 0)
227             *position = date_Get(&sys->pts) / (double)sys->duration;
228         else
229             *position = 0;
230         return VLC_SUCCESS;
231     }
232     case DEMUX_SET_POSITION: {
233         if (sys->duration < 0 || sys->is_realtime)
234             return VLC_EGENERIC;
235         double position = va_arg(args, double);
236         date_Set(&sys->pts, position * sys->duration);
237         return VLC_SUCCESS;
238     }
239     case DEMUX_GET_TIME: {
240         int64_t *time = va_arg(args, int64_t *);
241         *time = sys->pts_origin + date_Get(&sys->pts);
242         return VLC_SUCCESS;
243     }
244     case DEMUX_SET_TIME: {
245         if (sys->duration < 0 || sys->is_realtime)
246             return VLC_EGENERIC;
247         int64_t time = va_arg(args, int64_t);
248         date_Set(&sys->pts, VLC_CLIP(time - sys->pts_origin, 0, sys->duration));
249         return VLC_SUCCESS;
250     }
251     case DEMUX_SET_NEXT_DEMUX_TIME: {
252         int64_t pts_next = VLC_TS_0 + va_arg(args, int64_t);
253         if (sys->pts_next <= VLC_TS_INVALID)
254             sys->pts_origin = pts_next;
255         sys->pts_next = pts_next;
256         return VLC_SUCCESS;
257     }
258     case DEMUX_GET_LENGTH: {
259         int64_t *length = va_arg(args, int64_t *);
260         *length = __MAX(sys->duration, 0);
261         return VLC_SUCCESS;
262     }
263     case DEMUX_GET_FPS: {
264         double *fps = va_arg(args, double *);
265         *fps = (double)sys->pts.i_divider_num / sys->pts.i_divider_den;
266         return VLC_SUCCESS;
267     }
268     case DEMUX_GET_META:
269     case DEMUX_HAS_UNSUPPORTED_META:
270     case DEMUX_GET_ATTACHMENTS:
271     default:
272         return VLC_EGENERIC;
273     }
274 }
275
276 static bool IsBmp(stream_t *s)
277 {
278     const uint8_t *header;
279     if (stream_Peek(s, &header, 18) < 18)
280         return false;
281     if (memcmp(header, "BM", 2) &&
282         memcmp(header, "BA", 2) &&
283         memcmp(header, "CI", 2) &&
284         memcmp(header, "CP", 2) &&
285         memcmp(header, "IC", 2) &&
286         memcmp(header, "PT", 2))
287         return false;
288     uint32_t file_size   = GetDWLE(&header[2]);
289     uint32_t data_offset = GetDWLE(&header[10]);
290     uint32_t header_size = GetDWLE(&header[14]);
291     if (file_size != 14 && file_size != 14 + header_size &&
292         file_size <= data_offset)
293         return false;
294     if (data_offset < header_size + 14)
295         return false;
296     if (header_size != 12 && header_size < 40)
297         return false;
298     return true;
299 }
300
301 static bool IsPcx(stream_t *s)
302 {
303     const uint8_t *header;
304     if (stream_Peek(s, &header, 66) < 66)
305         return false;
306     if (header[0] != 0x0A ||                        /* marker */
307         (header[1] != 0x00 && header[1] != 0x02 &&
308          header[1] != 0x03 && header[1] != 0x05) || /* version */
309         (header[2] != 0 && header[2] != 1) ||       /* encoding */
310         (header[3] != 1 && header[3] != 2 &&
311          header[3] != 4 && header[3] != 8) ||       /* bits per pixel per plane */
312         header[64] != 0 ||                          /* reserved */
313         header[65] == 0 || header[65] > 4)          /* plane count */
314         return false;
315     if (GetWLE(&header[4]) > GetWLE(&header[8]) ||  /* xmin vs xmax */
316         GetWLE(&header[6]) > GetWLE(&header[10]))   /* ymin vs ymax */
317         return false;
318     return true;
319 }
320
321 static bool IsLbm(stream_t *s)
322 {
323     const uint8_t *header;
324     if (stream_Peek(s, &header, 12) < 12)
325         return false;
326     if (memcmp(&header[0], "FORM", 4) ||
327         GetDWBE(&header[4]) <= 4 ||
328         (memcmp(&header[8], "ILBM", 4) && memcmp(&header[8], "PBM ", 4)))
329         return false;
330     return true;
331 }
332 static bool IsPnmBlank(uint8_t v)
333 {
334     return v == ' ' || v == '\t' || v == '\r' || v == '\n';
335 }
336 static bool IsPnm(stream_t *s)
337 {
338     const uint8_t *header;
339     int size = stream_Peek(s, &header, 256);
340     if (size < 3)
341         return false;
342     if (header[0] != 'P' ||
343         header[1] < '1' || header[1] > '6' ||
344         !IsPnmBlank(header[2]))
345         return false;
346
347     int number_count = 0;
348     for (int i = 3, parsing_number = 0; i < size && number_count < 2; i++) {
349         if (IsPnmBlank(header[i])) {
350             if (parsing_number) {
351                 parsing_number = 0;
352                 number_count++;
353             }
354         } else {
355             if (header[i] < '0' || header[i] > '9')
356                 break;
357             parsing_number = 1;
358         }
359     }
360     if (number_count < 2)
361         return false;
362     return true;
363 }
364
365 static uint8_t FindJpegMarker(int *position, const uint8_t *data, int size)
366 {
367     for (int i = *position; i + 1 < size; i++) {
368         if (data[i + 0] != 0xff || data[i + 1] == 0x00)
369             return 0xff;
370         if (data[i + 1] != 0xff) {
371             *position = i + 2;
372             return data[i + 1];
373         }
374     }
375     return 0xff;
376 }
377 static bool IsJfif(stream_t *s)
378 {
379     const uint8_t *header;
380     int size = stream_Peek(s, &header, 256);
381     int position = 0;
382
383     if (FindJpegMarker(&position, header, size) != 0xd8)
384         return false;
385     if (FindJpegMarker(&position, header, size) != 0xe0)
386         return false;
387     position += 2;  /* Skip size */
388     if (position + 5 > size)
389         return false;
390     if (memcmp(&header[position], "JFIF\0", 5))
391         return false;
392     return true;
393 }
394
395 static bool IsSpiff(stream_t *s)
396 {
397     const uint8_t *header;
398     if (stream_Peek(s, &header, 36) < 36) /* SPIFF header size */
399         return false;
400     if (header[0] != 0xff || header[1] != 0xd8 ||
401         header[2] != 0xff || header[3] != 0xe8)
402         return false;
403     if (memcmp(&header[6], "SPIFF\0", 6))
404         return false;
405     return true;
406 }
407
408 static bool IsExif(stream_t *s)
409 {
410     const uint8_t *header;
411     int size = stream_Peek(s, &header, 256);
412     int position = 0;
413
414     if (FindJpegMarker(&position, header, size) != 0xd8)
415         return false;
416     if (FindJpegMarker(&position, header, size) != 0xe1)
417         return false;
418     position += 2;  /* Skip size */
419     if (position + 5 > size)
420         return false;
421     if (memcmp(&header[position], "Exif\0", 5))
422         return false;
423     return true;
424 }
425
426 static bool FindSVGmarker(int *position, const uint8_t *data, const int size, const char *marker)
427 {
428     for( int i = *position; i < size; i++)
429     {
430         if (memcmp(&data[i], marker, strlen(marker)) == 0)
431         {
432             *position = i;
433             return true;
434         }
435     }
436     return false;
437 }
438
439 static bool IsSVG(stream_t *s)
440 {
441     char *ext = strstr(s->psz_path, ".svg");
442     if (!ext) return false;
443
444     const uint8_t *header;
445     int size = stream_Peek(s, &header, 4096);
446     int position = 0;
447
448     const char xml[] = "<?xml version=\"";
449     if (!FindSVGmarker(&position, header, size, xml))
450         return false;
451     if (position != 0)
452         return false;
453
454     const char endxml[] = ">\0";
455     if (!FindSVGmarker(&position, header, size, endxml))
456         return false;
457     if (position <= 15)
458         return false;
459
460     const char svg[] = "<svg";
461     if (!FindSVGmarker(&position, header, size, svg))
462         return false;
463     if (position < 19)
464         return false;
465
466     /* SVG Scalable Vector Graphics image */
467
468     /* NOTE: some SVG images have the mimetype set in a meta data section
469      * and some do not */
470     return true;
471 }
472
473 static bool IsTarga(stream_t *s)
474 {
475     /* The header is not enough to ensure proper detection, we need
476      * to have a look at the footer. But doing so can be slow. So
477      * try to avoid it when possible */
478     const uint8_t *header;
479     if (stream_Peek(s, &header, 18) < 18)   /* Targa fixed header */
480         return false;
481     if (header[1] > 1)                      /* Color Map Type */
482         return false;
483     if ((header[1] != 0 || header[3 + 4] != 0) &&
484         header[3 + 4] != 8  &&
485         header[3 + 4] != 15 && header[3 + 4] != 16 &&
486         header[3 + 4] != 24 && header[3 + 4] != 32)
487         return false;
488     if ((header[2] > 3 && header[2] < 9) || header[2] > 11) /* Image Type */
489         return false;
490     if (GetWLE(&header[8 + 4]) <= 0 ||      /* Width */
491         GetWLE(&header[8 + 6]) <= 0)        /* Height */
492         return false;
493     if (header[8 + 8] != 8  &&
494         header[8 + 8] != 15 && header[8 + 8] != 16 &&
495         header[8 + 8] != 24 && header[8 + 8] != 32)
496         return false;
497     if (header[8 + 9] & 0xc0)               /* Reserved bits */
498         return false;
499
500     const int64_t size = stream_Size(s);
501     if (size <= 18 + 26)
502         return false;
503     bool can_seek;
504     if (stream_Control(s, STREAM_CAN_SEEK, &can_seek) || !can_seek)
505         return false;
506
507     const int64_t position = stream_Tell(s);
508     if (stream_Seek(s, size - 26))
509         return false;
510
511     const uint8_t *footer;
512     bool is_targa = stream_Peek(s, &footer, 26) >= 26 &&
513                     !memcmp(&footer[8], "TRUEVISION-XFILE.\x00", 18);
514     stream_Seek(s, position);
515     return is_targa;
516 }
517
518 typedef struct {
519     vlc_fourcc_t  codec;
520     int           marker_size;
521     const uint8_t marker[14];
522     bool          (*detect)(stream_t *s);
523 } image_format_t;
524
525 #define VLC_CODEC_XCF VLC_FOURCC('X', 'C', 'F', ' ')
526 #define VLC_CODEC_LBM VLC_FOURCC('L', 'B', 'M', ' ')
527 static const image_format_t formats[] = {
528     { .codec = VLC_CODEC_XCF,
529       .marker_size = 9 + 4 + 1,
530       .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
531                   'f', 'i', 'l', 'e', '\0' }
532     },
533     { .codec = VLC_CODEC_XCF,
534       .marker_size = 9 + 4 + 1,
535       .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
536                   'v', '0', '0', '1', '\0' }
537     },
538     { .codec = VLC_CODEC_XCF,
539       .marker_size = 9 + 4 + 1,
540       .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
541                   'v', '0', '0', '2', '\0' }
542     },
543     { .codec = VLC_CODEC_PNG,
544       .marker_size = 8,
545       .marker = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }
546     },
547     { .codec = VLC_CODEC_GIF,
548       .marker_size = 6,
549       .marker = { 'G', 'I', 'F', '8', '7', 'a' }
550     },
551     { .codec = VLC_CODEC_GIF,
552       .marker_size = 6,
553       .marker = { 'G', 'I', 'F', '8', '9', 'a' }
554     },
555     /* XXX TIFF detection may be a bit weak */
556     { .codec = VLC_CODEC_TIFF,
557       .marker_size = 4,
558       .marker = { 'I', 'I', 0x2a, 0x00 },
559     },
560     { .codec = VLC_CODEC_TIFF,
561       .marker_size = 4,
562       .marker = { 'M', 'M', 0x00, 0x2a },
563     },
564     { .codec = VLC_CODEC_BMP,
565       .detect = IsBmp,
566     },
567     { .codec = VLC_CODEC_PCX,
568       .detect = IsPcx,
569     },
570     { .codec = VLC_CODEC_LBM,
571       .detect = IsLbm,
572     },
573     { .codec = VLC_CODEC_PNM,
574       .detect = IsPnm,
575     },
576     { .codec = VLC_CODEC_MXPEG,
577       .detect = IsMxpeg,
578     },
579     { .codec = VLC_CODEC_JPEG,
580       .detect = IsJfif,
581     },
582     { .codec = VLC_CODEC_JPEG,
583       .detect = IsSpiff,
584     },
585     { .codec = VLC_CODEC_JPEG,
586       .detect = IsExif,
587     },
588     { .codec = VLC_CODEC_BPG,
589       .marker_size = 4,
590       .marker = { 'B', 'P', 'G', 0xFB },
591     },
592     { .codec = VLC_CODEC_SVG,
593       .detect = IsSVG,
594     },
595     { .codec = VLC_CODEC_TARGA,
596       .detect = IsTarga,
597     },
598     { .codec = 0 }
599 };
600
601 static int Open(vlc_object_t *object)
602 {
603     demux_t *demux = (demux_t*)object;
604
605     /* Detect the image type */
606     const image_format_t *img;
607
608     const uint8_t *peek;
609     int peek_size = 0;
610     for (int i = 0; ; i++) {
611         img = &formats[i];
612         if (!img->codec)
613             return VLC_EGENERIC;
614
615         if (img->detect) {
616             if (img->detect(demux->s))
617                 break;
618         } else {
619             if (peek_size < img->marker_size)
620                 peek_size = stream_Peek(demux->s, &peek, img->marker_size);
621             if (peek_size >= img->marker_size &&
622                 !memcmp(peek, img->marker, img->marker_size))
623                 break;
624         }
625     }
626     msg_Dbg(demux, "Detected image: %s",
627             vlc_fourcc_GetDescription(VIDEO_ES, img->codec));
628
629     if( img->codec == VLC_CODEC_MXPEG )
630     {
631         return VLC_EGENERIC; //let avformat demux this file
632     }
633
634     /* Load and if selected decode */
635     es_format_t fmt;
636     es_format_Init(&fmt, VIDEO_ES, img->codec);
637     fmt.video.i_chroma = fmt.i_codec;
638
639     block_t *data = Load(demux);
640     if (data && var_InheritBool(demux, "image-decode")) {
641         char *string = var_InheritString(demux, "image-chroma");
642         vlc_fourcc_t chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, string);
643         free(string);
644
645         data = Decode(demux, &fmt.video, chroma, data);
646         fmt.i_codec = fmt.video.i_chroma;
647     }
648     fmt.i_id    = var_InheritInteger(demux, "image-id");
649     fmt.i_group = var_InheritInteger(demux, "image-group");
650     if (var_InheritURational(demux,
651                              &fmt.video.i_frame_rate,
652                              &fmt.video.i_frame_rate_base,
653                              "image-fps") ||
654         fmt.video.i_frame_rate <= 0 || fmt.video.i_frame_rate_base <= 0) {
655         msg_Err(demux, "Invalid frame rate, using 10/1 instead");
656         fmt.video.i_frame_rate      = 10;
657         fmt.video.i_frame_rate_base = 1;
658     }
659
660     /* If loadind failed, we still continue to avoid mis-detection
661      * by other demuxers. */
662     if (!data)
663         msg_Err(demux, "Failed to load the image");
664
665     /* */
666     demux_sys_t *sys = malloc(sizeof(*sys));
667     if (!sys) {
668         if (data)
669             block_Release(data);
670         es_format_Clean(&fmt);
671         return VLC_ENOMEM;
672     }
673
674     sys->data        = data;
675     sys->es          = es_out_Add(demux->out, &fmt);
676     sys->duration    = CLOCK_FREQ * var_InheritFloat(demux, "image-duration");
677     sys->is_realtime = var_InheritBool(demux, "image-realtime");
678     sys->pts_origin  = sys->is_realtime ? mdate() : 0;
679     sys->pts_next    = VLC_TS_INVALID;
680     date_Init(&sys->pts, fmt.video.i_frame_rate, fmt.video.i_frame_rate_base);
681     date_Set(&sys->pts, 0);
682
683     es_format_Clean(&fmt);
684
685     demux->pf_demux   = Demux;
686     demux->pf_control = Control;
687     demux->p_sys      = sys;
688     return VLC_SUCCESS;
689 }
690
691 static void Close(vlc_object_t *object)
692 {
693     demux_t     *demux = (demux_t*)object;
694     demux_sys_t *sys   = demux->p_sys;
695
696     if (sys->data)
697         block_Release(sys->data);
698     free(sys);
699 }
700