mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-20 00:31:13 +00:00
ext/: Remove STREAM locks as they are taken in core now.
Original commit message from CVS: * ext/ogg/gstoggdemux.c: (gst_ogg_pad_dispose), (gst_ogg_pad_typefind), (gst_ogg_pad_submit_packet), (gst_ogg_chain_new_stream), (gst_ogg_demux_perform_seek), (gst_ogg_demux_chain), (gst_ogg_demux_loop), (gst_ogg_demux_sink_activate): * ext/theora/theoradec.c: (theora_dec_src_event), (theora_handle_comment_packet), (theora_dec_chain), (theora_dec_change_state): * ext/vorbis/vorbisdec.c: (vorbis_dec_sink_event), (vorbis_handle_data_packet), (vorbis_dec_chain), (vorbis_dec_change_state): Remove STREAM locks as they are taken in core now. Never set bogus granulepos on vorbis/theora. Fix leaks in theoradec tag parsing.
This commit is contained in:
parent
656e0f6acd
commit
2bed7c60aa
4 changed files with 85 additions and 89 deletions
17
ChangeLog
17
ChangeLog
|
@ -1,3 +1,20 @@
|
|||
2005-05-25 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* ext/ogg/gstoggdemux.c: (gst_ogg_pad_dispose),
|
||||
(gst_ogg_pad_typefind), (gst_ogg_pad_submit_packet),
|
||||
(gst_ogg_chain_new_stream), (gst_ogg_demux_perform_seek),
|
||||
(gst_ogg_demux_chain), (gst_ogg_demux_loop),
|
||||
(gst_ogg_demux_sink_activate):
|
||||
* ext/theora/theoradec.c: (theora_dec_src_event),
|
||||
(theora_handle_comment_packet), (theora_dec_chain),
|
||||
(theora_dec_change_state):
|
||||
* ext/vorbis/vorbisdec.c: (vorbis_dec_sink_event),
|
||||
(vorbis_handle_data_packet), (vorbis_dec_chain),
|
||||
(vorbis_dec_change_state):
|
||||
Remove STREAM locks as they are taken in core now.
|
||||
Never set bogus granulepos on vorbis/theora.
|
||||
Fix leaks in theoradec tag parsing.
|
||||
|
||||
2005-05-25 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* ext/gnomevfs/gstgnomevfssrc.c: (gst_gnomevfssrc_create):
|
||||
|
|
|
@ -89,10 +89,15 @@ typedef enum
|
|||
GST_OGG_PAD_MODE_STREAMING, /* we are streaming buffers to the outside */
|
||||
} GstOggPadMode;
|
||||
|
||||
//#define PARENT GstPad
|
||||
//#define PARENTCLASS GstPadClass
|
||||
#define PARENT GstRealPad
|
||||
#define PARENTCLASS GstRealPadClass
|
||||
|
||||
/* all information needed for one ogg stream */
|
||||
struct _GstOggPad
|
||||
{
|
||||
GstRealPad pad; /* subclass GstRealPad */
|
||||
PARENT pad; /* subclass GstPad */
|
||||
|
||||
GstOggPadMode mode;
|
||||
|
||||
|
@ -109,7 +114,6 @@ struct _GstOggPad
|
|||
gint64 packetno;
|
||||
gint64 offset;
|
||||
|
||||
GstEvent *new_segment;
|
||||
gint64 start;
|
||||
gint64 stop;
|
||||
|
||||
|
@ -130,7 +134,7 @@ struct _GstOggPad
|
|||
|
||||
struct _GstOggPadClass
|
||||
{
|
||||
GstRealPadClass parent_class;
|
||||
PARENTCLASS parent_class;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
|
@ -197,7 +201,7 @@ static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
|
|||
static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
|
||||
static GstCaps *gst_ogg_type_find (ogg_packet * packet);
|
||||
|
||||
static GstRealPadClass *ogg_pad_parent_class = NULL;
|
||||
static GstPadClass *ogg_pad_parent_class = NULL;
|
||||
|
||||
static GType
|
||||
gst_ogg_pad_get_type (void)
|
||||
|
@ -278,10 +282,6 @@ gst_ogg_pad_dispose (GObject * object)
|
|||
g_list_free (pad->headers);
|
||||
pad->headers = NULL;
|
||||
|
||||
if (pad->new_segment) {
|
||||
gst_event_unref (pad->new_segment);
|
||||
pad->new_segment = NULL;
|
||||
}
|
||||
ogg_stream_reset (&pad->stream);
|
||||
|
||||
G_OBJECT_CLASS (ogg_pad_parent_class)->dispose (object);
|
||||
|
@ -560,6 +560,8 @@ gst_ogg_pad_typefind (GstOggPad * pad, ogg_packet * packet)
|
|||
gst_element_factory_create (GST_ELEMENT_FACTORY (factories->data),
|
||||
NULL);
|
||||
if (element) {
|
||||
GstCaps *any;
|
||||
|
||||
/* this is ours */
|
||||
gst_object_ref (GST_OBJECT (element));
|
||||
gst_object_sink (GST_OBJECT (element));
|
||||
|
@ -573,7 +575,9 @@ gst_ogg_pad_typefind (GstOggPad * pad, ogg_packet * packet)
|
|||
gst_pad_set_chain_function (pad->elem_out,
|
||||
gst_ogg_pad_internal_chain);
|
||||
gst_pad_set_element_private (pad->elem_out, pad);
|
||||
gst_pad_set_caps (pad->elem_out, gst_caps_new_any ());
|
||||
any = gst_caps_new_any ();
|
||||
gst_pad_set_caps (pad->elem_out, any);
|
||||
gst_caps_unref (any);
|
||||
gst_pad_set_active (pad->elem_out, TRUE);
|
||||
|
||||
/* and this pad may not be named src.. */
|
||||
|
@ -648,10 +652,6 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
|
|||
"%p streaming to peer serial %08lx, packetno %lld", pad, pad->serialno,
|
||||
pad->packetno);
|
||||
|
||||
if (pad->new_segment) {
|
||||
ret = gst_pad_push_event (GST_PAD (pad), pad->new_segment);
|
||||
pad->new_segment = NULL;
|
||||
}
|
||||
if (buf) {
|
||||
memcpy (buf->data, packet->packet, packet->bytes);
|
||||
|
||||
|
@ -683,7 +683,7 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
|
|||
GST_BUFFER_OFFSET (buf) = -1;
|
||||
GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
|
||||
|
||||
ret = GST_RPAD_CHAINFUNC (pad->elem_pad) (pad->elem_pad, buf);
|
||||
ret = gst_pad_chain (pad->elem_pad, buf);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -795,7 +795,7 @@ gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno)
|
|||
list = gst_tag_list_new ();
|
||||
name = g_strdup_printf ("serial_%08lx", serialno);
|
||||
|
||||
GST_RPAD_DIRECTION (ret) = GST_PAD_SRC;
|
||||
GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
|
||||
ret->chain = chain;
|
||||
ret->ogg = chain->ogg;
|
||||
gst_object_set_name (GST_OBJECT (ret), name);
|
||||
|
@ -1381,16 +1381,18 @@ gst_ogg_demux_perform_seek (GstOggDemux * ogg, gint64 pos)
|
|||
for (j = 0; j < chain->streams->len; j++) {
|
||||
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
|
||||
|
||||
gst_event_ref (event);
|
||||
/* queue the event for the streaming thread */
|
||||
pad->new_segment = event;
|
||||
gst_pad_push_event (GST_PAD (pad), gst_event_new_flush (TRUE));
|
||||
|
||||
/* and the discont */
|
||||
gst_event_ref (event);
|
||||
gst_pad_push_event (GST_PAD (pad), event);
|
||||
}
|
||||
}
|
||||
gst_event_unref (event);
|
||||
/* restart our task since it might have been stopped when we did the
|
||||
* flush. */
|
||||
gst_task_start (GST_RPAD_TASK (ogg->sinkpad));
|
||||
gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
|
||||
ogg->sinkpad);
|
||||
}
|
||||
|
||||
/* switch to different chain */
|
||||
|
@ -1792,7 +1794,7 @@ no_first_chain:
|
|||
* the serialno, submit pages and packets to the oggpads
|
||||
*/
|
||||
static GstFlowReturn
|
||||
gst_ogg_demux_chain_unlocked (GstPad * pad, GstBuffer * buffer)
|
||||
gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
|
||||
{
|
||||
GstOggDemux *ogg;
|
||||
gint ret = -1;
|
||||
|
@ -1868,18 +1870,6 @@ gst_ogg_demux_chain_unlocked (GstPad * pad, GstBuffer * buffer)
|
|||
return result;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
|
||||
{
|
||||
GstFlowReturn ret;
|
||||
|
||||
GST_STREAM_LOCK (pad);
|
||||
ret = gst_ogg_demux_chain_unlocked (pad, buffer);
|
||||
GST_STREAM_UNLOCK (pad);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ogg_demux_send_eos (GstOggDemux * ogg)
|
||||
{
|
||||
|
@ -1914,7 +1904,6 @@ gst_ogg_demux_loop (GstOggPad * pad)
|
|||
|
||||
ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
|
||||
|
||||
GST_STREAM_LOCK (pad);
|
||||
if (ogg->need_chains) {
|
||||
gboolean got_chains;
|
||||
|
||||
|
@ -1944,19 +1933,16 @@ gst_ogg_demux_loop (GstOggPad * pad)
|
|||
|
||||
ogg->offset += GST_BUFFER_SIZE (buffer);
|
||||
|
||||
ret = gst_ogg_demux_chain_unlocked (ogg->sinkpad, buffer);
|
||||
ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
|
||||
if (ret != GST_FLOW_OK) {
|
||||
GST_LOG_OBJECT (ogg, "got unexpected %d, pausing", ret);
|
||||
goto pause;
|
||||
}
|
||||
|
||||
GST_STREAM_UNLOCK (pad);
|
||||
return;
|
||||
|
||||
pause:
|
||||
GST_LOG_OBJECT (ogg, "pausing task");
|
||||
gst_task_pause (GST_RPAD_TASK (ogg->sinkpad));
|
||||
GST_STREAM_UNLOCK (pad);
|
||||
gst_pad_pause_task (ogg->sinkpad);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1975,35 +1961,18 @@ gst_ogg_demux_sink_activate (GstPad * sinkpad, GstActivateMode mode)
|
|||
break;
|
||||
case GST_ACTIVATE_PULL:
|
||||
/* if we have a scheduler we can start the task */
|
||||
if (GST_ELEMENT_SCHEDULER (ogg)) {
|
||||
gst_pad_peer_set_active (sinkpad, mode);
|
||||
GST_STREAM_LOCK (sinkpad);
|
||||
GST_RPAD_TASK (sinkpad) =
|
||||
gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (ogg),
|
||||
(GstTaskFunction) gst_ogg_demux_loop, sinkpad);
|
||||
|
||||
ogg->need_chains = TRUE;
|
||||
ogg->seekable = TRUE;
|
||||
gst_task_start (GST_RPAD_TASK (sinkpad));
|
||||
GST_STREAM_UNLOCK (sinkpad);
|
||||
result = TRUE;
|
||||
}
|
||||
result =
|
||||
gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
|
||||
sinkpad);
|
||||
break;
|
||||
case GST_ACTIVATE_NONE:
|
||||
/* step 1, unblock clock sync (if any) */
|
||||
|
||||
/* step 2, make sure streaming finishes */
|
||||
GST_STREAM_LOCK (sinkpad);
|
||||
|
||||
/* step 3, stop the task */
|
||||
if (GST_RPAD_TASK (sinkpad)) {
|
||||
gst_task_stop (GST_RPAD_TASK (sinkpad));
|
||||
gst_object_unref (GST_OBJECT (GST_RPAD_TASK (sinkpad)));
|
||||
GST_RPAD_TASK (sinkpad) = NULL;
|
||||
}
|
||||
GST_STREAM_UNLOCK (sinkpad);
|
||||
|
||||
result = TRUE;
|
||||
result = gst_pad_stop_task (sinkpad);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -535,10 +535,7 @@ theora_dec_src_event (GstPad * pad, GstEvent * event)
|
|||
format, value);
|
||||
|
||||
res = gst_pad_send_event (GST_PAD_PEER (dec->sinkpad), real_seek);
|
||||
if (!res)
|
||||
goto error;
|
||||
|
||||
error:
|
||||
gst_event_unref (event);
|
||||
break;
|
||||
}
|
||||
|
@ -548,6 +545,10 @@ theora_dec_src_event (GstPad * pad, GstEvent * event)
|
|||
}
|
||||
|
||||
return res;
|
||||
|
||||
error:
|
||||
gst_event_unref (event);
|
||||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -633,7 +634,7 @@ theora_handle_comment_packet (GstTheoraDec * dec, ogg_packet * packet)
|
|||
GST_DEBUG ("parsing comment packet");
|
||||
|
||||
buf = gst_buffer_new_and_alloc (packet->bytes);
|
||||
GST_BUFFER_DATA (buf) = packet->packet;
|
||||
memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
|
||||
|
||||
list =
|
||||
gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\201theora", 7,
|
||||
|
@ -656,6 +657,7 @@ theora_handle_comment_packet (GstTheoraDec * dec, ogg_packet * packet)
|
|||
GST_TAG_VIDEO_CODEC, "Theora", NULL);
|
||||
|
||||
//gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, 0, list);
|
||||
gst_tag_list_free (list);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
@ -913,8 +915,6 @@ theora_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
|
||||
dec = GST_THEORA_DEC (GST_PAD_PARENT (pad));
|
||||
|
||||
GST_STREAM_LOCK (pad);
|
||||
|
||||
if (dec->packetno >= 3) {
|
||||
/* try timestamp first */
|
||||
outtime = GST_BUFFER_TIMESTAMP (buf);
|
||||
|
@ -939,7 +939,6 @@ theora_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
* offset before we can generate valid timestamps */
|
||||
dec->queued = g_list_append (dec->queued, buf);
|
||||
GST_DEBUG_OBJECT (dec, "queued buffer");
|
||||
GST_STREAM_UNLOCK (pad);
|
||||
return GST_FLOW_OK;
|
||||
} else {
|
||||
/* granulepos to time */
|
||||
|
@ -1013,7 +1012,6 @@ theora_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
done:
|
||||
dec->packetno++;
|
||||
_inc_granulepos (dec);
|
||||
GST_STREAM_UNLOCK (pad);
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
|
@ -1040,13 +1038,11 @@ theora_dec_change_state (GstElement * element)
|
|||
case GST_STATE_PLAYING_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_PAUSED_TO_READY:
|
||||
GST_STREAM_LOCK (dec->sinkpad);
|
||||
theora_clear (&dec->state);
|
||||
theora_comment_clear (&dec->comment);
|
||||
theora_info_clear (&dec->info);
|
||||
dec->packetno = 0;
|
||||
dec->granulepos = -1;
|
||||
GST_STREAM_UNLOCK (dec->sinkpad);
|
||||
break;
|
||||
case GST_STATE_READY_TO_NULL:
|
||||
break;
|
||||
|
|
|
@ -376,6 +376,7 @@ vorbis_dec_sink_event (GstPad * pad, GstEvent * event)
|
|||
GST_LOG_OBJECT (dec, "handling event");
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_DISCONTINUOUS:
|
||||
GST_STREAM_LOCK (pad);
|
||||
if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT,
|
||||
(gint64 *) & start_value, &end_value)) {
|
||||
dec->granulepos = start_value;
|
||||
|
@ -392,18 +393,25 @@ vorbis_dec_sink_event (GstPad * pad, GstEvent * event)
|
|||
} else {
|
||||
GST_WARNING_OBJECT (dec,
|
||||
"discont event didn't include offset, we might set it wrong now");
|
||||
dec->granulepos = -1;
|
||||
}
|
||||
}
|
||||
GST_DEBUG ("vd: discont %lld", dec->granulepos);
|
||||
|
||||
dec->granulepos = -1;
|
||||
|
||||
if (dec->packetno < 3) {
|
||||
if (dec->granulepos != 0)
|
||||
GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL),
|
||||
("can't handle discont before parsing first 3 packets"));
|
||||
dec->packetno = 0;
|
||||
#if 0
|
||||
gst_pad_push_event (dec->srcpad, gst_event_new_discontinuous (FALSE,
|
||||
GST_FORMAT_TIME, (guint64) 0, GST_FORMAT_DEFAULT,
|
||||
(guint64) 0, GST_FORMAT_BYTES, (guint64) 0, 0));
|
||||
#endif
|
||||
gst_event_ref (event);
|
||||
gst_pad_push_event (dec->srcpad, event);
|
||||
} else {
|
||||
GstFormat time_format, default_format, bytes_format;
|
||||
|
||||
|
@ -417,22 +425,30 @@ vorbis_dec_sink_event (GstPad * pad, GstEvent * event)
|
|||
dec->granulepos, &time_format, &time)
|
||||
&& vorbis_dec_convert (dec->srcpad, GST_FORMAT_DEFAULT,
|
||||
dec->granulepos, &bytes_format, &bytes)) {
|
||||
|
||||
gst_event_ref (event);
|
||||
gst_pad_push_event (dec->srcpad, event);
|
||||
/*
|
||||
gst_pad_push_event (dec->srcpad,
|
||||
gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME,
|
||||
time, GST_FORMAT_DEFAULT, dec->granulepos,
|
||||
GST_FORMAT_BYTES, bytes, 0));
|
||||
*/
|
||||
} else {
|
||||
GST_ERROR_OBJECT (dec,
|
||||
"failed to parse data for DISCONT event, not sending any");
|
||||
gst_event_ref (event);
|
||||
gst_pad_push_event (dec->srcpad, event);
|
||||
}
|
||||
#ifdef HAVE_VORBIS_SYNTHESIS_RESTART
|
||||
vorbis_synthesis_restart (&dec->vd);
|
||||
#endif
|
||||
}
|
||||
GST_STREAM_UNLOCK (pad);
|
||||
gst_event_unref (event);
|
||||
break;
|
||||
default:
|
||||
ret = gst_pad_event_default (dec->sinkpad, event);
|
||||
ret = gst_pad_event_default (pad, event);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
|
@ -657,11 +673,15 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet)
|
|||
|
||||
GST_BUFFER_OFFSET (out) = vd->granulepos;
|
||||
GST_BUFFER_OFFSET_END (out) = vd->granulepos + sample_count;
|
||||
if (vd->granulepos != -1)
|
||||
GST_BUFFER_TIMESTAMP (out) = vd->granulepos * GST_SECOND / vd->vi.rate;
|
||||
else
|
||||
GST_BUFFER_TIMESTAMP (out) = -1;
|
||||
GST_BUFFER_DURATION (out) = sample_count * GST_SECOND / vd->vi.rate;
|
||||
|
||||
result = gst_pad_push (vd->srcpad, out);
|
||||
|
||||
if (vd->granulepos != -1)
|
||||
vd->granulepos += sample_count;
|
||||
} else {
|
||||
/* no buffer.. */
|
||||
|
@ -703,14 +723,12 @@ vorbis_dec_chain (GstPad * pad, GstBuffer * buffer)
|
|||
ogg_packet packet;
|
||||
GstFlowReturn result = GST_FLOW_OK;
|
||||
|
||||
GST_STREAM_LOCK (pad);
|
||||
|
||||
vd = GST_VORBIS_DEC (GST_PAD_PARENT (pad));
|
||||
|
||||
/* make ogg_packet out of the buffer */
|
||||
packet.packet = GST_BUFFER_DATA (buffer);
|
||||
packet.bytes = GST_BUFFER_SIZE (buffer);
|
||||
packet.granulepos = GST_BUFFER_OFFSET_END (buffer);
|
||||
packet.granulepos = vd->granulepos;
|
||||
packet.packetno = vd->packetno++;
|
||||
/*
|
||||
* FIXME. Is there anyway to know that this is the last packet and
|
||||
|
@ -731,13 +749,13 @@ vorbis_dec_chain (GstPad * pad, GstBuffer * buffer)
|
|||
result = vorbis_handle_data_packet (vd, &packet);
|
||||
}
|
||||
|
||||
GST_DEBUG ("offset end: %lld", GST_BUFFER_OFFSET_END (buffer));
|
||||
|
||||
/* granulepos is the last sample in the packet */
|
||||
if (GST_BUFFER_OFFSET_END_IS_VALID (buffer))
|
||||
vd->granulepos = GST_BUFFER_OFFSET_END (buffer);;
|
||||
|
||||
done:
|
||||
GST_STREAM_UNLOCK (pad);
|
||||
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
return result;
|
||||
|
@ -759,7 +777,7 @@ vorbis_dec_change_state (GstElement * element)
|
|||
vorbis_info_init (&vd->vi);
|
||||
vorbis_comment_init (&vd->vc);
|
||||
vd->initialized = FALSE;
|
||||
vd->granulepos = 0;
|
||||
vd->granulepos = -1;
|
||||
vd->packetno = 0;
|
||||
break;
|
||||
case GST_STATE_PAUSED_TO_PLAYING:
|
||||
|
@ -774,14 +792,10 @@ vorbis_dec_change_state (GstElement * element)
|
|||
case GST_STATE_PLAYING_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_PAUSED_TO_READY:
|
||||
GST_STREAM_LOCK (vd->sinkpad);
|
||||
vorbis_block_clear (&vd->vb);
|
||||
vorbis_dsp_clear (&vd->vd);
|
||||
vorbis_comment_clear (&vd->vc);
|
||||
vorbis_info_clear (&vd->vi);
|
||||
vd->packetno = 0;
|
||||
vd->granulepos = 0;
|
||||
GST_STREAM_UNLOCK (vd->sinkpad);
|
||||
break;
|
||||
case GST_STATE_READY_TO_NULL:
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue