mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
j2kpay: use SOP markers to split bitstream
When parsing the bitstream, look for SOP markers because we are allowed to split packets on those marker boundaries. Rework the parsing code a little so that we can pack multiple Packetization units in one RTP packet.
This commit is contained in:
parent
29363d6068
commit
005e27fa79
1 changed files with 178 additions and 73 deletions
|
@ -72,6 +72,8 @@ typedef enum
|
||||||
J2K_MARKER = 0xFF,
|
J2K_MARKER = 0xFF,
|
||||||
J2K_MARKER_SOC = 0x4F,
|
J2K_MARKER_SOC = 0x4F,
|
||||||
J2K_MARKER_SOT = 0x90,
|
J2K_MARKER_SOT = 0x90,
|
||||||
|
J2K_MARKER_SOP = 0x91,
|
||||||
|
J2K_MARKER_SOD = 0x93,
|
||||||
J2K_MARKER_EOC = 0xD9
|
J2K_MARKER_EOC = 0xD9
|
||||||
} RtpJ2KMarker;
|
} RtpJ2KMarker;
|
||||||
|
|
||||||
|
@ -226,64 +228,126 @@ gst_rtp_j2k_pay_scan_marker (const guint8 * data, guint size, guint * offset)
|
||||||
while ((data[(*offset)++] != J2K_MARKER) && ((*offset) < size));
|
while ((data[(*offset)++] != J2K_MARKER) && ((*offset) < size));
|
||||||
|
|
||||||
if (G_UNLIKELY ((*offset) >= size)) {
|
if (G_UNLIKELY ((*offset) >= size)) {
|
||||||
GST_LOG ("end of data, return EOC");
|
|
||||||
return J2K_MARKER_EOC;
|
return J2K_MARKER_EOC;
|
||||||
} else {
|
} else {
|
||||||
guint8 marker = data[(*offset)++];
|
guint8 marker = data[(*offset)++];
|
||||||
GST_LOG ("found %02x marker", marker);
|
|
||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
RtpJ2KHeader header;
|
||||||
|
gboolean bitstream;
|
||||||
|
guint n_tiles;
|
||||||
|
guint next_sot;
|
||||||
|
gboolean force_packet;
|
||||||
|
} RtpJ2KState;
|
||||||
|
|
||||||
static guint
|
static guint
|
||||||
find_pu_end (GstRtpJ2KPay * pay, const guint8 * data, guint size,
|
find_pu_end (GstRtpJ2KPay * pay, const guint8 * data, guint size,
|
||||||
guint offset, RtpJ2KHeader * header)
|
guint offset, RtpJ2KState * state)
|
||||||
{
|
{
|
||||||
|
gboolean cut_sop = FALSE;
|
||||||
|
RtpJ2KMarker marker;
|
||||||
|
|
||||||
/* parse the j2k header for 'start of codestream' */
|
/* parse the j2k header for 'start of codestream' */
|
||||||
|
GST_LOG_OBJECT (pay, "checking from offset %u", offset);
|
||||||
while (offset < size) {
|
while (offset < size) {
|
||||||
GST_LOG_OBJECT (pay, "checking from offset %u", offset);
|
marker = gst_rtp_j2k_pay_scan_marker (data, size, &offset);
|
||||||
switch (gst_rtp_j2k_pay_scan_marker (data, size, &offset)) {
|
|
||||||
case J2K_MARKER_SOC:
|
|
||||||
GST_DEBUG_OBJECT (pay, "found SOC at %u", offset);
|
|
||||||
header->MHF = 1;
|
|
||||||
break;
|
|
||||||
case J2K_MARKER_SOT:
|
|
||||||
{
|
|
||||||
guint len, Psot;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (pay, "found SOT at %u", offset);
|
if (state->bitstream) {
|
||||||
/* we found SOT but also had a header first */
|
/* parsing bitstream, only look for SOP */
|
||||||
if (header->MHF)
|
switch (marker) {
|
||||||
return offset - 2;
|
case J2K_MARKER_SOP:
|
||||||
|
GST_LOG_OBJECT (pay, "found SOP at %u", offset);
|
||||||
/* parse SOT but do some sanity checks first */
|
if (cut_sop)
|
||||||
len = gst_rtp_j2k_pay_header_size (data, offset);
|
return offset - 2;
|
||||||
GST_DEBUG_OBJECT (pay, "SOT length %u", len);
|
cut_sop = TRUE;
|
||||||
if (len < 8)
|
break;
|
||||||
return size;
|
default:
|
||||||
if (offset + len >= size)
|
if (offset >= state->next_sot) {
|
||||||
return size;
|
GST_LOG_OBJECT (pay, "reached next SOT at %u", offset);
|
||||||
|
state->bitstream = FALSE;
|
||||||
/* we have a valid tile number now, keep it in the header */
|
state->force_packet = TRUE;
|
||||||
header->T = 0;
|
if (marker == J2K_MARKER_EOC)
|
||||||
header->tile = GST_READ_UINT16_BE (&data[offset + 2]);
|
/* include EOC */
|
||||||
|
return state->next_sot + 2;
|
||||||
/* get offset of next tile, if it's 0, it goes all the way to the end of
|
else
|
||||||
* the data */
|
return state->next_sot;
|
||||||
Psot = GST_READ_UINT32_BE (&data[offset + 4]);
|
}
|
||||||
if (Psot == 0)
|
break;
|
||||||
offset = size;
|
}
|
||||||
else
|
} else {
|
||||||
offset += Psot;
|
switch (marker) {
|
||||||
GST_DEBUG_OBJECT (pay, "Isot %u, Psot %u", header->tile, Psot);
|
case J2K_MARKER_SOC:
|
||||||
break;
|
GST_LOG_OBJECT (pay, "found SOC at %u", offset);
|
||||||
|
state->header.MHF = 1;
|
||||||
|
break;
|
||||||
|
case J2K_MARKER_SOT:
|
||||||
|
{
|
||||||
|
guint len, Psot;
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (pay, "found SOT at %u", offset);
|
||||||
|
/* we found SOT but also had a header first */
|
||||||
|
if (state->header.MHF) {
|
||||||
|
state->force_packet = TRUE;
|
||||||
|
return offset - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse SOT but do some sanity checks first */
|
||||||
|
len = gst_rtp_j2k_pay_header_size (data, offset);
|
||||||
|
GST_LOG_OBJECT (pay, "SOT length %u", len);
|
||||||
|
if (len < 8)
|
||||||
|
return size;
|
||||||
|
if (offset + len >= size)
|
||||||
|
return size;
|
||||||
|
|
||||||
|
if (state->n_tiles == 0)
|
||||||
|
/* first tile, T is valid */
|
||||||
|
state->header.T = 0;
|
||||||
|
else
|
||||||
|
/* more tiles, T becomes invalid */
|
||||||
|
state->header.T = 1;
|
||||||
|
state->header.tile = GST_READ_UINT16_BE (&data[offset + 2]);
|
||||||
|
state->n_tiles++;
|
||||||
|
|
||||||
|
/* get offset of next tile, if it's 0, it goes all the way to the end of
|
||||||
|
* the data */
|
||||||
|
Psot = GST_READ_UINT32_BE (&data[offset + 4]);
|
||||||
|
if (Psot == 0)
|
||||||
|
state->next_sot = size;
|
||||||
|
else
|
||||||
|
state->next_sot = offset - 2 + Psot;
|
||||||
|
|
||||||
|
offset += len;
|
||||||
|
GST_LOG_OBJECT (pay, "Isot %u, Psot %u, next %u", state->header.tile,
|
||||||
|
Psot, state->next_sot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case J2K_MARKER_SOD:
|
||||||
|
GST_LOG_OBJECT (pay, "found SOD at %u", offset);
|
||||||
|
/* can't have more tiles now */
|
||||||
|
state->n_tiles = 0;
|
||||||
|
/* go to bitstream parsing */
|
||||||
|
state->bitstream = TRUE;
|
||||||
|
/* cut at the next SOP or else include all data */
|
||||||
|
cut_sop = TRUE;
|
||||||
|
/* force a new packet when we see SOP, this can be optional but the
|
||||||
|
* spec recommends packing headers separately */
|
||||||
|
state->force_packet = TRUE;
|
||||||
|
break;
|
||||||
|
case J2K_MARKER_EOC:
|
||||||
|
GST_LOG_OBJECT (pay, "found EOC at %u", offset);
|
||||||
|
return offset;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
guint len = gst_rtp_j2k_pay_header_size (data, offset);
|
||||||
|
GST_LOG_OBJECT (pay, "skip 0x%02x len %u", marker, len);
|
||||||
|
offset += len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case J2K_MARKER_EOC:
|
|
||||||
GST_DEBUG_OBJECT (pay, "found EOC");
|
|
||||||
return offset;
|
|
||||||
default:
|
|
||||||
offset += gst_rtp_j2k_pay_header_size (data, offset);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GST_DEBUG_OBJECT (pay, "reached end of data");
|
GST_DEBUG_OBJECT (pay, "reached end of data");
|
||||||
|
@ -297,13 +361,14 @@ gst_rtp_j2k_pay_handle_buffer (GstBaseRTPPayload * basepayload,
|
||||||
GstRtpJ2KPay *pay;
|
GstRtpJ2KPay *pay;
|
||||||
GstClockTime timestamp;
|
GstClockTime timestamp;
|
||||||
GstFlowReturn ret = GST_FLOW_ERROR;
|
GstFlowReturn ret = GST_FLOW_ERROR;
|
||||||
RtpJ2KHeader j2k_header;
|
RtpJ2KState state;
|
||||||
GstBufferList *list = NULL;
|
GstBufferList *list = NULL;
|
||||||
GstBufferListIterator *it = NULL;
|
GstBufferListIterator *it = NULL;
|
||||||
guint8 *data;
|
guint8 *data;
|
||||||
guint size;
|
guint size;
|
||||||
guint mtu;
|
guint mtu, max_size;
|
||||||
guint offset;
|
guint offset;
|
||||||
|
guint end, pos;
|
||||||
|
|
||||||
pay = GST_RTP_J2K_PAY (basepayload);
|
pay = GST_RTP_J2K_PAY (basepayload);
|
||||||
mtu = GST_BASE_RTP_PAYLOAD_MTU (pay);
|
mtu = GST_BASE_RTP_PAYLOAD_MTU (pay);
|
||||||
|
@ -311,47 +376,87 @@ gst_rtp_j2k_pay_handle_buffer (GstBaseRTPPayload * basepayload,
|
||||||
size = GST_BUFFER_SIZE (buffer);
|
size = GST_BUFFER_SIZE (buffer);
|
||||||
data = GST_BUFFER_DATA (buffer);
|
data = GST_BUFFER_DATA (buffer);
|
||||||
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
||||||
offset = 0;
|
offset = pos = end = 0;
|
||||||
|
|
||||||
GST_LOG_OBJECT (pay, "got buffer size %u, timestamp %" GST_TIME_FORMAT, size,
|
GST_LOG_OBJECT (pay, "got buffer size %u, timestamp %" GST_TIME_FORMAT, size,
|
||||||
GST_TIME_ARGS (timestamp));
|
GST_TIME_ARGS (timestamp));
|
||||||
|
|
||||||
/* do some header defaults first */
|
/* do some header defaults first */
|
||||||
j2k_header.tp = 0; /* only progressive scan */
|
state.header.tp = 0; /* only progressive scan */
|
||||||
j2k_header.MHF = 0; /* no header */
|
state.header.MHF = 0; /* no header */
|
||||||
j2k_header.mh_id = 0; /* always 0 for now */
|
state.header.mh_id = 0; /* always 0 for now */
|
||||||
j2k_header.T = 1; /* invalid tile */
|
state.header.T = 1; /* invalid tile */
|
||||||
j2k_header.priority = 255; /* always 255 for now */
|
state.header.priority = 255; /* always 255 for now */
|
||||||
j2k_header.tile = 0; /* no tile number */
|
state.header.tile = 0; /* no tile number */
|
||||||
j2k_header.reserved = 0;
|
state.header.reserved = 0;
|
||||||
|
state.bitstream = FALSE;
|
||||||
|
state.n_tiles = 0;
|
||||||
|
state.next_sot = 0;
|
||||||
|
state.force_packet = FALSE;
|
||||||
|
|
||||||
if (pay->buffer_list) {
|
if (pay->buffer_list) {
|
||||||
list = gst_buffer_list_new ();
|
list = gst_buffer_list_new ();
|
||||||
it = gst_buffer_list_iterate (list);
|
it = gst_buffer_list_iterate (list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* get max packet length */
|
||||||
|
max_size = gst_rtp_buffer_calc_payload_len (mtu - HEADER_SIZE, 0, 0);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
GstBuffer *outbuf;
|
GstBuffer *outbuf;
|
||||||
guint8 *header;
|
guint8 *header;
|
||||||
guint pu_size, end;
|
guint payload_size;
|
||||||
|
guint pu_size;
|
||||||
|
|
||||||
/* scan next packetization unit and fill in the header */
|
/* try to pack as much as we can */
|
||||||
end = find_pu_end (pay, data, size, offset, &j2k_header);
|
do {
|
||||||
pu_size = end - offset;
|
/* see how much we have scanned already */
|
||||||
|
pu_size = end - offset;
|
||||||
|
GST_DEBUG_OBJECT (pay, "scanned pu size %u", pu_size);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (pay, "pu of size %u", pu_size);
|
/* we need to make a new packet */
|
||||||
|
if (state.force_packet) {
|
||||||
|
GST_DEBUG_OBJECT (pay, "need to force a new packet");
|
||||||
|
state.force_packet = FALSE;
|
||||||
|
pos = end;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* else see if we have enough */
|
||||||
|
if (pu_size > max_size) {
|
||||||
|
if (pos != offset)
|
||||||
|
/* the packet became too large, use previous scanpos */
|
||||||
|
pu_size = pos - offset;
|
||||||
|
else
|
||||||
|
/* the already scanned data was already too big, make sure we start
|
||||||
|
* scanning from the last searched position */
|
||||||
|
pos = end;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (pay, "max size exceeded pu_size %u", pu_size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = end;
|
||||||
|
/* scan next packetization unit and fill in the header */
|
||||||
|
end = find_pu_end (pay, data, size, pos, &state);
|
||||||
|
} while (TRUE);
|
||||||
|
|
||||||
while (pu_size > 0) {
|
while (pu_size > 0) {
|
||||||
guint packet_size, payload_size, data_size;
|
guint packet_size, data_size;
|
||||||
|
|
||||||
/* calculate the packet size */
|
/* calculate the packet size */
|
||||||
packet_size =
|
packet_size =
|
||||||
gst_rtp_buffer_calc_packet_len (pu_size + HEADER_SIZE, 0, 0);
|
gst_rtp_buffer_calc_packet_len (pu_size + HEADER_SIZE, 0, 0);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (pay, "needed packet size %u", packet_size);
|
if (packet_size > mtu) {
|
||||||
|
GST_DEBUG_OBJECT (pay, "needed packet size %u clamped to MTU %u",
|
||||||
|
packet_size, mtu);
|
||||||
|
packet_size = mtu;
|
||||||
|
} else {
|
||||||
|
GST_DEBUG_OBJECT (pay, "needed packet size %u fits in MTU %u",
|
||||||
|
packet_size, mtu);
|
||||||
|
}
|
||||||
|
|
||||||
/* make sure it fits the MTU */
|
|
||||||
packet_size = (packet_size < mtu ? packet_size : mtu);
|
|
||||||
/* get total payload size and data size */
|
/* get total payload size and data size */
|
||||||
payload_size = gst_rtp_buffer_calc_payload_len (packet_size, 0, 0);
|
payload_size = gst_rtp_buffer_calc_payload_len (packet_size, 0, 0);
|
||||||
data_size = payload_size - HEADER_SIZE;
|
data_size = payload_size - HEADER_SIZE;
|
||||||
|
@ -371,13 +476,13 @@ gst_rtp_j2k_pay_handle_buffer (GstBaseRTPPayload * basepayload,
|
||||||
pu_size -= data_size;
|
pu_size -= data_size;
|
||||||
if (pu_size == 0) {
|
if (pu_size == 0) {
|
||||||
/* reached the end of a packetization unit */
|
/* reached the end of a packetization unit */
|
||||||
if (j2k_header.MHF) {
|
if (state.header.MHF) {
|
||||||
/* we were doing a header, see if all fit in one packet or if
|
/* we were doing a header, see if all fit in one packet or if
|
||||||
* we had to fragment it */
|
* we had to fragment it */
|
||||||
if (offset == 0)
|
if (offset == 0)
|
||||||
j2k_header.MHF = 3;
|
state.header.MHF = 3;
|
||||||
else
|
else
|
||||||
j2k_header.MHF = 2;
|
state.header.MHF = 2;
|
||||||
}
|
}
|
||||||
if (end >= size)
|
if (end >= size)
|
||||||
gst_rtp_buffer_set_marker (outbuf, TRUE);
|
gst_rtp_buffer_set_marker (outbuf, TRUE);
|
||||||
|
@ -385,12 +490,12 @@ gst_rtp_j2k_pay_handle_buffer (GstBaseRTPPayload * basepayload,
|
||||||
|
|
||||||
/* copy the header and push the packet */
|
/* copy the header and push the packet */
|
||||||
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
|
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
|
||||||
j2k_header.offset = ((offset & 0x0000FF) << 16) |
|
state.header.offset = ((offset & 0x0000FF) << 16) |
|
||||||
((offset & 0xFF0000) >> 16) | (offset & 0x00FF00);
|
((offset & 0xFF0000) >> 16) | (offset & 0x00FF00);
|
||||||
#else
|
#else
|
||||||
j2k_header.offset = offset;
|
state.header.offset = offset;
|
||||||
#endif
|
#endif
|
||||||
memcpy (header, &j2k_header, HEADER_SIZE);
|
memcpy (header, &state.header, HEADER_SIZE);
|
||||||
|
|
||||||
if (pay->buffer_list) {
|
if (pay->buffer_list) {
|
||||||
GstBuffer *paybuf;
|
GstBuffer *paybuf;
|
||||||
|
@ -414,13 +519,13 @@ gst_rtp_j2k_pay_handle_buffer (GstBaseRTPPayload * basepayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reset header for next round */
|
/* reset header for next round */
|
||||||
j2k_header.MHF = 0;
|
state.header.MHF = 0;
|
||||||
j2k_header.T = 1;
|
state.header.T = 1;
|
||||||
j2k_header.tile = 0;
|
state.header.tile = 0;
|
||||||
|
|
||||||
offset += data_size;
|
offset += data_size;
|
||||||
}
|
}
|
||||||
offset = end;
|
offset = pos;
|
||||||
} while (offset < size);
|
} while (offset < size);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
Loading…
Reference in a new issue