mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 02:31:03 +00:00
metdata: more logging and code cleanups
Sprinkle more debug log statements into the code. Move some repeaded string constant into header files and use sizeof instead of manually counted bytes. Add comments.
This commit is contained in:
parent
4b3e2b6e4c
commit
5540ec23ec
7 changed files with 94 additions and 67 deletions
|
@ -50,6 +50,12 @@
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/*
|
||||||
|
* defines
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define EXIF_HEADER "Exif\0"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GST_META_EXIF_BYTE_ORDER_MOTOROLA,
|
GST_META_EXIF_BYTE_ORDER_MOTOROLA,
|
||||||
GST_META_EXIF_BYTE_ORDER_INTEL
|
GST_META_EXIF_BYTE_ORDER_INTEL
|
||||||
|
|
|
@ -50,6 +50,12 @@
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/*
|
||||||
|
* defines
|
||||||
|
*/
|
||||||
|
#define PHOTOSHOP_HEADER "Photoshop 3.0"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* external function prototypes
|
* external function prototypes
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -77,6 +77,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "metadatamuxjpeg.h"
|
#include "metadatamuxjpeg.h"
|
||||||
|
#include "metadataxmp.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -203,12 +204,12 @@ metadatamux_jpeg_parse (JpegMuxData * jpeg_data, guint8 * buf,
|
||||||
{
|
{
|
||||||
|
|
||||||
int ret = META_PARSING_DONE;
|
int ret = META_PARSING_DONE;
|
||||||
guint8 mark[2] = { 0x00, 0x00 };
|
|
||||||
const guint8 *step_buf = buf;
|
const guint8 *step_buf = buf;
|
||||||
|
|
||||||
*next_start = buf;
|
*next_start = buf;
|
||||||
|
|
||||||
if (jpeg_data->state == JPEG_MUX_NULL) {
|
if (jpeg_data->state == JPEG_MUX_NULL) {
|
||||||
|
guint8 mark[2];
|
||||||
|
|
||||||
if (*bufsize < 2) {
|
if (*bufsize < 2) {
|
||||||
GST_INFO ("need more data");
|
GST_INFO ("need more data");
|
||||||
|
@ -249,9 +250,8 @@ metadatamux_jpeg_parse (JpegMuxData * jpeg_data, guint8 * buf,
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
GST_INFO ("finishing: %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -268,46 +268,43 @@ done:
|
||||||
void
|
void
|
||||||
metadatamux_jpeg_lazy_update (JpegMuxData * jpeg_data)
|
metadatamux_jpeg_lazy_update (JpegMuxData * jpeg_data)
|
||||||
{
|
{
|
||||||
gint i = 0;
|
gsize i;
|
||||||
gboolean has_exif = FALSE;
|
gboolean has_exif = FALSE;
|
||||||
|
MetadataChunkArray *chunks = jpeg_data->inject_chunks;
|
||||||
|
|
||||||
while (i < jpeg_data->inject_chunks->len) {
|
GST_INFO ("checking %d chunks", chunks->len);
|
||||||
if (jpeg_data->inject_chunks->chunk[i].size > 0 &&
|
|
||||||
jpeg_data->inject_chunks->chunk[i].data) {
|
for (i = 0; i < chunks->len; ++i) {
|
||||||
switch (jpeg_data->inject_chunks->chunk[i].type) {
|
|
||||||
|
GST_INFO ("checking chunk[%" G_GSIZE_FORMAT "], type=%d, len=%u",
|
||||||
|
i, chunks->chunk[i].type, chunks->chunk[i].size);
|
||||||
|
|
||||||
|
if (chunks->chunk[i].size > 0 && chunks->chunk[i].data) {
|
||||||
|
switch (chunks->chunk[i].type) {
|
||||||
case MD_CHUNK_EXIF:
|
case MD_CHUNK_EXIF:
|
||||||
metadatamux_wrap_chunk (&jpeg_data->inject_chunks->chunk[i], NULL, 0,
|
metadatamux_wrap_chunk (&chunks->chunk[i], NULL, 0, 0xFF, 0xE1);
|
||||||
0xFF, 0xE1);
|
|
||||||
has_exif = TRUE;
|
has_exif = TRUE;
|
||||||
break;
|
break;
|
||||||
case MD_CHUNK_IPTC:
|
case MD_CHUNK_IPTC:
|
||||||
#ifdef HAVE_IPTC
|
#ifdef HAVE_IPTC
|
||||||
{
|
if (metadatamux_wrap_iptc_with_ps3 (&chunks->chunk[i].data,
|
||||||
if (metadatamux_wrap_iptc_with_ps3 (&jpeg_data->inject_chunks->
|
&chunks->chunk[i].size)) {
|
||||||
chunk[i].data, &jpeg_data->inject_chunks->chunk[i].size)) {
|
metadatamux_wrap_chunk (&chunks->chunk[i], NULL, 0, 0xFF, 0xED);
|
||||||
metadatamux_wrap_chunk (&jpeg_data->inject_chunks->chunk[i], NULL,
|
|
||||||
0, 0xFF, 0xED);
|
|
||||||
} else {
|
} else {
|
||||||
GST_ERROR ("Invalid IPTC chunk\n");
|
GST_ERROR ("Invalid IPTC chunk\n");
|
||||||
metadata_chunk_array_remove_by_index (jpeg_data->inject_chunks, i);
|
metadata_chunk_array_remove_by_index (chunks, i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif /* #ifdef HAVE_IPTC */
|
#endif /* #ifdef HAVE_IPTC */
|
||||||
break;
|
break;
|
||||||
case MD_CHUNK_XMP:
|
case MD_CHUNK_XMP:
|
||||||
{
|
metadatamux_wrap_chunk (&chunks->chunk[i],
|
||||||
static const char XmpHeader[] = "http://ns.adobe.com/xap/1.0/";
|
(guint8 *) XMP_HEADER, sizeof (XMP_HEADER), 0xFF, 0xE1);
|
||||||
|
|
||||||
metadatamux_wrap_chunk (&jpeg_data->inject_chunks->chunk[i],
|
|
||||||
(guint8 *) XmpHeader, sizeof (XmpHeader), 0xFF, 0xE1);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!has_exif) {
|
if (!has_exif) {
|
||||||
|
@ -392,22 +389,28 @@ metadatamux_jpeg_reading (JpegMuxData * jpeg_data, guint8 ** buf,
|
||||||
mark[0] = READ (*buf, *bufsize);
|
mark[0] = READ (*buf, *bufsize);
|
||||||
mark[1] = READ (*buf, *bufsize);
|
mark[1] = READ (*buf, *bufsize);
|
||||||
|
|
||||||
|
GST_DEBUG ("parsing JPEG marker : 0x%02x%02x", mark[0], mark[1]);
|
||||||
|
|
||||||
if (mark[0] == 0xFF) {
|
if (mark[0] == 0xFF) {
|
||||||
|
|
||||||
chunk_size = READ (*buf, *bufsize) << 8;
|
chunk_size = READ (*buf, *bufsize) << 8;
|
||||||
chunk_size += READ (*buf, *bufsize);
|
chunk_size += READ (*buf, *bufsize);
|
||||||
|
|
||||||
if (mark[1] == 0xE0) { /* may be JFIF */
|
if (mark[1] == 0xE0) { /* APP0 - may be JFIF */
|
||||||
|
|
||||||
if (chunk_size >= 16) {
|
/* FIXME: whats the 14 ? according to
|
||||||
if (*bufsize < 5) {
|
* http://en.wikipedia.org/wiki/JFIF#JFIF_segment_format
|
||||||
|
* its the jfif segment without thumbnails
|
||||||
|
*/
|
||||||
|
if (chunk_size >= 14 + 2) {
|
||||||
|
if (*bufsize < sizeof (JfifHeader)) {
|
||||||
GST_INFO ("need more data");
|
GST_INFO ("need more data");
|
||||||
*next_size = (*buf - *next_start) + 5;
|
*next_size = (*buf - *next_start) + sizeof (JfifHeader);
|
||||||
ret = META_PARSING_NEED_MORE_DATA;
|
ret = META_PARSING_NEED_MORE_DATA;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 == memcmp (JfifHeader, *buf, 5)) {
|
if (0 == memcmp (JfifHeader, *buf, sizeof (JfifHeader))) {
|
||||||
jfif_found = TRUE;
|
jfif_found = TRUE;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -344,15 +344,19 @@ void
|
||||||
metadatamux_png_lazy_update (PngMuxData * png_data)
|
metadatamux_png_lazy_update (PngMuxData * png_data)
|
||||||
{
|
{
|
||||||
gsize i;
|
gsize i;
|
||||||
|
MetadataChunkArray *chunks = png_data->inject_chunks;
|
||||||
|
|
||||||
for (i = 0; i < png_data->inject_chunks->len; ++i) {
|
GST_INFO ("checking %d chunks", chunks->len);
|
||||||
if (png_data->inject_chunks->chunk[i].size > 0 &&
|
|
||||||
png_data->inject_chunks->chunk[i].data) {
|
for (i = 0; i < chunks->len; ++i) {
|
||||||
switch (png_data->inject_chunks->chunk[i].type) {
|
|
||||||
|
GST_INFO ("checking chunk[%" G_GSIZE_FORMAT "], type=%d, len=%u",
|
||||||
|
i, chunks->chunk[i].type, chunks->chunk[i].size);
|
||||||
|
|
||||||
|
if (chunks->chunk[i].size > 0 && chunks->chunk[i].data) {
|
||||||
|
switch (chunks->chunk[i].type) {
|
||||||
case MD_CHUNK_XMP:
|
case MD_CHUNK_XMP:
|
||||||
{
|
metadatamux_wrap_xmp_chunk (&chunks->chunk[i]);
|
||||||
metadatamux_wrap_xmp_chunk (&png_data->inject_chunks->chunk[i]);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
GST_ERROR ("Unexpected chunk for PNG muxer.");
|
GST_ERROR ("Unexpected chunk for PNG muxer.");
|
||||||
|
|
|
@ -82,9 +82,12 @@
|
||||||
|
|
||||||
#include "metadataparsejpeg.h"
|
#include "metadataparsejpeg.h"
|
||||||
#include "metadataparseutil.h"
|
#include "metadataparseutil.h"
|
||||||
|
#include "metadataexif.h"
|
||||||
|
#include "metadataxmp.h"
|
||||||
|
|
||||||
#ifdef HAVE_IPTC
|
#ifdef HAVE_IPTC
|
||||||
#include <libiptcdata/iptc-jpeg.h>
|
#include <libiptcdata/iptc-jpeg.h>
|
||||||
|
#include "metadataiptc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -226,9 +229,7 @@ metadataparse_jpeg_parse (JpegParseData * jpeg_data, guint8 * buf,
|
||||||
guint32 * bufsize, const guint32 offset, guint8 ** next_start,
|
guint32 * bufsize, const guint32 offset, guint8 ** next_start,
|
||||||
guint32 * next_size)
|
guint32 * next_size)
|
||||||
{
|
{
|
||||||
|
|
||||||
int ret = META_PARSING_DONE;
|
int ret = META_PARSING_DONE;
|
||||||
guint8 mark[2] = { 0x00, 0x00 };
|
|
||||||
const guint8 *step_buf = buf;
|
const guint8 *step_buf = buf;
|
||||||
|
|
||||||
/* step_buf holds where buf starts. this const value will be passed to
|
/* step_buf holds where buf starts. this const value will be passed to
|
||||||
|
@ -240,6 +241,7 @@ metadataparse_jpeg_parse (JpegParseData * jpeg_data, guint8 * buf,
|
||||||
*next_start = buf;
|
*next_start = buf;
|
||||||
|
|
||||||
if (jpeg_data->state == JPEG_PARSE_NULL) {
|
if (jpeg_data->state == JPEG_PARSE_NULL) {
|
||||||
|
guint8 mark[2];
|
||||||
|
|
||||||
/* only the first time this function is called it will verify the stream
|
/* only the first time this function is called it will verify the stream
|
||||||
type to be sure it is a JPEG */
|
type to be sure it is a JPEG */
|
||||||
|
@ -259,9 +261,7 @@ metadataparse_jpeg_parse (JpegParseData * jpeg_data, guint8 * buf,
|
||||||
ret = META_PARSING_ERROR;
|
ret = META_PARSING_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
jpeg_data->state = JPEG_PARSE_READING;
|
jpeg_data->state = JPEG_PARSE_READING;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (ret == META_PARSING_DONE) {
|
while (ret == META_PARSING_DONE) {
|
||||||
|
@ -309,9 +309,8 @@ metadataparse_jpeg_parse (JpegParseData * jpeg_data, guint8 * buf,
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
GST_INFO ("finishing: %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -389,12 +388,6 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
|
||||||
guint16 chunk_size = 0;
|
guint16 chunk_size = 0;
|
||||||
|
|
||||||
static const char JfifHeader[] = "JFIF";
|
static const char JfifHeader[] = "JFIF";
|
||||||
static const unsigned char ExifHeader[] =
|
|
||||||
{ 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
|
|
||||||
#ifdef HAVE_IPTC
|
|
||||||
static const char PhotoshopHeader[] = "Photoshop 3.0";
|
|
||||||
#endif
|
|
||||||
static const char XmpHeader[] = "http://ns.adobe.com/xap/1.0/";
|
|
||||||
|
|
||||||
*next_start = *buf;
|
*next_start = *buf;
|
||||||
|
|
||||||
|
@ -407,12 +400,14 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
|
||||||
mark[0] = READ (*buf, *bufsize);
|
mark[0] = READ (*buf, *bufsize);
|
||||||
mark[1] = READ (*buf, *bufsize);
|
mark[1] = READ (*buf, *bufsize);
|
||||||
|
|
||||||
|
GST_DEBUG ("parsing JPEG marker : 0x%02x%02x", mark[0], mark[1]);
|
||||||
|
|
||||||
if (mark[0] == 0xFF) {
|
if (mark[0] == 0xFF) {
|
||||||
if (mark[1] == 0xD9) { /* end of image */
|
if (mark[1] == 0xD9) { /* EOI - end of image */
|
||||||
ret = META_PARSING_DONE;
|
ret = META_PARSING_DONE;
|
||||||
jpeg_data->state = JPEG_PARSE_DONE;
|
jpeg_data->state = JPEG_PARSE_DONE;
|
||||||
goto done;
|
goto done;
|
||||||
} else if (mark[1] == 0xDA) {
|
} else if (mark[1] == 0xDA) { /* SOS - start of scan */
|
||||||
/* start of scan image, lets not look behind of this */
|
/* start of scan image, lets not look behind of this */
|
||||||
ret = META_PARSING_DONE;
|
ret = META_PARSING_DONE;
|
||||||
jpeg_data->state = JPEG_PARSE_DONE;
|
jpeg_data->state = JPEG_PARSE_DONE;
|
||||||
|
@ -428,30 +423,34 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
|
||||||
chunk_size = READ (*buf, *bufsize) << 8;
|
chunk_size = READ (*buf, *bufsize) << 8;
|
||||||
chunk_size += READ (*buf, *bufsize);
|
chunk_size += READ (*buf, *bufsize);
|
||||||
|
|
||||||
if (mark[1] == 0xE0) { /* may be JFIF */
|
if (mark[1] == 0xE0) { /* APP0 - may be JFIF */
|
||||||
|
|
||||||
if (chunk_size >= 16) {
|
/* FIXME: whats the 14 ? according to
|
||||||
|
* http://en.wikipedia.org/wiki/JFIF#JFIF_segment_format
|
||||||
|
* its the jfif segment without thumbnails
|
||||||
|
*/
|
||||||
|
if (chunk_size >= 14 + 2) {
|
||||||
if (*bufsize < 14) {
|
if (*bufsize < 14) {
|
||||||
*next_size = (*buf - *next_start) + 14;
|
*next_size = (*buf - *next_start) + 14;
|
||||||
ret = META_PARSING_NEED_MORE_DATA;
|
ret = META_PARSING_NEED_MORE_DATA;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 == memcmp (JfifHeader, *buf, 5)) {
|
if (0 == memcmp (JfifHeader, *buf, sizeof (JfifHeader))) {
|
||||||
jpeg_data->jfif_found = TRUE;
|
jpeg_data->jfif_found = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (mark[1] == 0xE1) { /* may be it is Exif or XMP */
|
} else if (mark[1] == 0xE1) { /* APP1 - may be it is Exif or XMP */
|
||||||
|
|
||||||
if (chunk_size >= 8) { /* size2 'EXIF' 0x00 0x00 */
|
if (chunk_size >= sizeof (EXIF_HEADER) + 2) {
|
||||||
if (*bufsize < 6) {
|
if (*bufsize < sizeof (EXIF_HEADER)) {
|
||||||
*next_size = (*buf - *next_start) + 6;
|
*next_size = (*buf - *next_start) + sizeof (EXIF_HEADER);
|
||||||
ret = META_PARSING_NEED_MORE_DATA;
|
ret = META_PARSING_NEED_MORE_DATA;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 == memcmp (ExifHeader, *buf, 6)) {
|
if (0 == memcmp (EXIF_HEADER, *buf, sizeof (EXIF_HEADER))) {
|
||||||
MetadataChunk chunk;
|
MetadataChunk chunk;
|
||||||
|
|
||||||
if (!jpeg_data->parse_only) {
|
if (!jpeg_data->parse_only) {
|
||||||
|
@ -502,14 +501,14 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chunk_size >= 31) { /* size2 "http://ns.adobe.com/xap/1.0/" */
|
if (chunk_size >= sizeof (XMP_HEADER) + 2) {
|
||||||
if (*bufsize < 29) {
|
if (*bufsize < sizeof (XMP_HEADER)) {
|
||||||
*next_size = (*buf - *next_start) + 29;
|
*next_size = (*buf - *next_start) + sizeof (XMP_HEADER);
|
||||||
ret = META_PARSING_NEED_MORE_DATA;
|
ret = META_PARSING_NEED_MORE_DATA;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 == memcmp (XmpHeader, *buf, 29)) {
|
if (0 == memcmp (XMP_HEADER, *buf, sizeof (XMP_HEADER))) {
|
||||||
|
|
||||||
if (!jpeg_data->parse_only) {
|
if (!jpeg_data->parse_only) {
|
||||||
|
|
||||||
|
@ -527,9 +526,9 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
|
||||||
|
|
||||||
/* if adapter has been provided, prepare to hold chunk */
|
/* if adapter has been provided, prepare to hold chunk */
|
||||||
if (jpeg_data->xmp_adapter) {
|
if (jpeg_data->xmp_adapter) {
|
||||||
*buf += 29;
|
*buf += sizeof (XMP_HEADER);
|
||||||
*bufsize -= 29;
|
*bufsize -= sizeof (XMP_HEADER);
|
||||||
jpeg_data->read = chunk_size - 2 - 29;
|
jpeg_data->read = chunk_size - 2 - sizeof (XMP_HEADER);
|
||||||
jpeg_data->state = JPEG_PARSE_XMP;
|
jpeg_data->state = JPEG_PARSE_XMP;
|
||||||
ret = META_PARSING_DONE;
|
ret = META_PARSING_DONE;
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -550,8 +549,7 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (0 == memcmp (PHOTOSHOP_HEADER, *buf, sizeof (PHOTOSHOP_HEADER))) {
|
||||||
if (0 == memcmp (PhotoshopHeader, *buf, 14)) {
|
|
||||||
|
|
||||||
if (!jpeg_data->parse_only) {
|
if (!jpeg_data->parse_only) {
|
||||||
|
|
||||||
|
|
|
@ -363,6 +363,11 @@ metadataparse_png_reading (PngParseData * png_data, guint8 ** buf,
|
||||||
mark[2] = READ (*buf, *bufsize);
|
mark[2] = READ (*buf, *bufsize);
|
||||||
mark[3] = READ (*buf, *bufsize);
|
mark[3] = READ (*buf, *bufsize);
|
||||||
|
|
||||||
|
/* FIXME: use FOURCECC macros */
|
||||||
|
|
||||||
|
GST_DEBUG ("parsing png : 0x%02x%02x%02x%02x",
|
||||||
|
mark[0], mark[1], mark[2], mark[3]);
|
||||||
|
|
||||||
if (mark[0] == 'I' && mark[1] == 'E' && mark[2] == 'N' && mark[3] == 'D') {
|
if (mark[0] == 'I' && mark[1] == 'E' && mark[2] == 'N' && mark[3] == 'D') {
|
||||||
ret = META_PARSING_DONE;
|
ret = META_PARSING_DONE;
|
||||||
png_data->state = PNG_PARSE_DONE;
|
png_data->state = PNG_PARSE_DONE;
|
||||||
|
|
|
@ -50,6 +50,11 @@
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/*
|
||||||
|
* defines
|
||||||
|
*/
|
||||||
|
#define XMP_HEADER "http://ns.adobe.com/xap/1.0/"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* external function prototypes
|
* external function prototypes
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue