diff options
Diffstat (limited to 'gst/mpegvideoparse/mpegvideoparse.c')
-rw-r--r-- | gst/mpegvideoparse/mpegvideoparse.c | 238 |
1 files changed, 83 insertions, 155 deletions
diff --git a/gst/mpegvideoparse/mpegvideoparse.c b/gst/mpegvideoparse/mpegvideoparse.c index 18464507..6c776c54 100644 --- a/gst/mpegvideoparse/mpegvideoparse.c +++ b/gst/mpegvideoparse/mpegvideoparse.c @@ -28,17 +28,18 @@ /* FIXME: there are still some things to do in this element. * + Handle Sequence Display Extension to output the display size * rather than the encoded size. - * + Collect a list of regions and the sequence headers that apply - * to each region so that we properly handle SEQUENCE_END followed - * by a new sequence. - * + At least detect when the sequence changes and error out instead. * + Do all the other stuff (documentation, tests) to get it into * ugly or good. * + low priority: * - handle seeking in raw elementary streams * - calculate timestamps for all un-timestamped frames, taking into * account frame re-ordering. Doing this probably requires introducing - * an extra end-to-end delay, however so might not be really desirable. + * an extra end-to-end delay however, so might not be really desirable. + * - Collect a list of regions and the sequence headers that apply + * to each region so that we properly handle SEQUENCE_END followed + * by a new sequence. At the moment, the caps will change if the + * sequence changes, but if we then seek to a different spot it might + * be wrong. Fortunately almost every stream only has 1 sequence. */ GST_DEBUG_CATEGORY_STATIC (mpv_parse_debug); #define GST_CAT_DEFAULT mpv_parse_debug @@ -190,150 +191,22 @@ gst_mpegvideoparse_dispose (MpegVideoParse * mpegvideoparse) gst_buffer_replace (&mpegvideoparse->seq_hdr_buf, NULL); } -/* Set the Pixel Aspect Ratio in our hdr from a DAR code in the data */ -static void -set_par_from_dar (MPEGSeqHdr * hdr, guint8 asr_code) -{ - /* Pixel_width = DAR_width * display_vertical_size */ - /* Pixel_height = DAR_height * display_horizontal_size */ - switch (asr_code) { - case 0x02: /* 3:4 DAR = 4:3 pixels */ - hdr->par_w = 4 * hdr->height; - hdr->par_h = 3 * hdr->width; - break; - case 0x03: /* 9:16 DAR */ - hdr->par_w = 16 * hdr->height; - hdr->par_h = 9 * hdr->width; - break; - case 0x04: /* 1:2.21 DAR */ - hdr->par_w = 221 * hdr->height; - hdr->par_h = 100 * hdr->width; - break; - case 0x01: /* Square pixels */ - default: - hdr->par_w = hdr->par_h = 1; - break; - } -} - -static void -set_fps_from_code (MPEGSeqHdr * hdr, guint8 fps_code) -{ - const gint framerates[][2] = { - {30, 1}, {24000, 1001}, {24, 1}, {25, 1}, - {30000, 1001}, {30, 1}, {50, 1}, {60000, 1001}, - {60, 1}, {30, 1} - }; - - if (fps_code < 10) { - hdr->fps_n = framerates[fps_code][0]; - hdr->fps_d = framerates[fps_code][1]; - } else { - /* Force a valid framerate */ - hdr->fps_n = 30; - hdr->fps_d = 1; - } -} - -static void -mpegvideoparse_parse_seq (MpegVideoParse * mpegvideoparse, GstBuffer * buf) +static gboolean +mpegvideoparse_handle_sequence (MpegVideoParse * mpegvideoparse, + GstBuffer * buf) { MPEGSeqHdr new_hdr; - guint32 code; - guint8 dar_idx, fps_idx; - gint seq_data_length; - guint32 sync_word = 0xffffffff; guint8 *cur, *end; - gboolean constrained_flag; - gboolean load_intra_flag; - gboolean load_non_intra_flag; cur = GST_BUFFER_DATA (buf); end = GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf); - if (GST_BUFFER_SIZE (buf) < 12) - return; /* Too small to be a sequence header */ - - seq_data_length = 12; /* minimum length. */ - - /* Skip the sync word */ - cur += 4; - - /* Parse the MPEG 1 bits */ - new_hdr.mpeg_version = 1; - - code = GST_READ_UINT32_BE (cur); - new_hdr.width = (code >> 20) & 0xfff; - new_hdr.height = (code >> 8) & 0xfff; - - dar_idx = (code >> 4) & 0xf; - set_par_from_dar (&new_hdr, dar_idx); - fps_idx = code & 0xf; - set_fps_from_code (&new_hdr, fps_idx); - - constrained_flag = (cur[7] >> 2) & 0x01; - load_intra_flag = (cur[7] >> 1) & 0x01; - if (load_intra_flag) { - seq_data_length += 64; /* 8 rows of 8 bytes of intra matrix */ - if (GST_BUFFER_SIZE (buf) < seq_data_length) - return; - cur += 64; - } - - load_non_intra_flag = cur[7] & 0x01; - if (load_non_intra_flag) { - seq_data_length += 64; /* 8 rows of 8 bytes of non-intra matrix */ - if (GST_BUFFER_SIZE (buf) < seq_data_length) - return; - cur += 64; - } - - /* Skip the rest of the MPEG-1 header */ - cur += 8; + memset (&new_hdr, 0, sizeof (MPEGSeqHdr)); - /* Read MPEG-2 sequence extensions */ - cur = mpeg_find_start_code (&sync_word, cur, end); - while (cur != NULL) { - /* Cur points at the last byte of the start code */ - if (cur[0] == MPEG_PACKET_EXTENSION) { - guint8 ext_code; - - if ((end - cur - 1) < 1) - return; /* short extension packet extension */ - - ext_code = cur[1] >> 4; - if (ext_code == MPEG_PACKET_EXT_SEQUENCE) { - /* Parse a Sequence Extension */ - guint8 horiz_size_ext, vert_size_ext; - guint8 fps_n_ext, fps_d_ext; - - if ((end - cur - 1) < 7) - /* need at least 10 bytes, minus 3 for the start code 000001 */ - return; - - horiz_size_ext = ((cur[2] << 1) & 0x02) | ((cur[3] >> 7) & 0x01); - vert_size_ext = (cur[3] >> 5) & 0x03; - fps_n_ext = (cur[6] >> 5) & 0x03; - fps_d_ext = cur[6] & 0x1f; - - new_hdr.fps_n *= (fps_n_ext + 1); - new_hdr.fps_d *= (fps_d_ext + 1); - new_hdr.width += (horiz_size_ext << 12); - new_hdr.height += (vert_size_ext << 12); - } - - new_hdr.mpeg_version = 2; - } - cur = mpeg_find_start_code (&sync_word, cur, end); - } + if (G_UNLIKELY (!mpeg_util_parse_sequence_hdr (&new_hdr, cur, end))) + return FALSE; - if (new_hdr.par_w != mpegvideoparse->seq_hdr.par_w || - new_hdr.par_h != mpegvideoparse->seq_hdr.par_h || - new_hdr.fps_n != mpegvideoparse->seq_hdr.fps_n || - new_hdr.fps_d != mpegvideoparse->seq_hdr.fps_d || - new_hdr.width != mpegvideoparse->seq_hdr.width || - new_hdr.height != mpegvideoparse->seq_hdr.height || - new_hdr.mpeg_version != mpegvideoparse->seq_hdr.mpeg_version) { + if (memcmp (&mpegvideoparse, &new_hdr, sizeof (MPEGSeqHdr)) != 0) { GstCaps *caps; GstBuffer *seq_buf; @@ -344,7 +217,7 @@ mpegvideoparse_parse_seq (MpegVideoParse * mpegvideoparse, GstBuffer * buf) gst_buffer_unref (seq_buf); /* And update the new_hdr into our stored version */ - memcpy (&mpegvideoparse->seq_hdr, &new_hdr, sizeof (MPEGSeqHdr)); + mpegvideoparse->seq_hdr = new_hdr; caps = gst_caps_new_simple ("video/mpeg", "systemstream", G_TYPE_BOOLEAN, FALSE, @@ -357,8 +230,41 @@ mpegvideoparse_parse_seq (MpegVideoParse * mpegvideoparse, GstBuffer * buf) "codec_data", GST_TYPE_BUFFER, seq_buf, NULL); GST_DEBUG ("New mpegvideoparse caps: %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (mpegvideoparse->srcpad, caps); + if (!gst_pad_set_caps (mpegvideoparse->srcpad, caps)) + return FALSE; } + + return TRUE; +} + +static gboolean +mpegvideoparse_handle_picture (MpegVideoParse * mpegvideoparse, GstBuffer * buf) +{ + guint8 *cur, *end; + guint32 sync_word = 0xffffffff; + + cur = GST_BUFFER_DATA (buf); + end = GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf); + + cur = mpeg_util_find_start_code (&sync_word, cur, end); + while (cur != NULL) { + /* Cur points at the last byte of the start code */ + if (cur[0] == MPEG_PACKET_PICTURE) { + guint8 *pic_data = cur - 3; + MPEGPictureHdr hdr; + + /* pic_data points to the first byte of the sync word now */ + if (!mpeg_util_parse_picture_hdr (&hdr, pic_data, end)) + return FALSE; + + GST_LOG_OBJECT (mpegvideoparse, "Picture type is %u", hdr.pic_type); + /* FIXME: Can use the picture type and number of fields to track a + * timestamp */ + } + cur = mpeg_util_find_start_code (&sync_word, cur, end); + } + + return TRUE; } #if 0 @@ -392,11 +298,11 @@ static GstFlowReturn mpegvideoparse_drain_avail (MpegVideoParse * mpegvideoparse) { MPEGBlockInfo *cur; - GstBuffer *buf; + GstBuffer *buf = NULL; GstFlowReturn res = GST_FLOW_OK; cur = mpeg_packetiser_get_block (&mpegvideoparse->packer, &buf); - while (cur != NULL) { + while ((cur != NULL) && (res == GST_FLOW_OK)) { /* Handle the block */ GST_LOG_OBJECT (mpegvideoparse, "Have block of size %u with pack_type 0x%02x and flags 0x%02x\n", @@ -404,7 +310,11 @@ mpegvideoparse_drain_avail (MpegVideoParse * mpegvideoparse) /* Don't start pushing out buffers until we've seen a sequence header */ if (mpegvideoparse->seq_hdr.mpeg_version == 0) { - if ((cur->flags & MPEG_BLOCK_FLAG_SEQUENCE) == 0) { + if (cur->flags & MPEG_BLOCK_FLAG_SEQUENCE) { + /* Found a sequence header */ + if (!mpegvideoparse_handle_sequence (mpegvideoparse, buf)) + goto error; + } else { if (buf) { GST_DEBUG_OBJECT (mpegvideoparse, "No sequence header yet. Dropping buffer of %u bytes", @@ -412,9 +322,22 @@ mpegvideoparse_drain_avail (MpegVideoParse * mpegvideoparse) gst_buffer_unref (buf); buf = NULL; } - } else { - /* Found a sequence header */ - mpegvideoparse_parse_seq (mpegvideoparse, buf); + } + } + + if (buf != NULL) { + /* If outputting a PICTURE packet, we can calculate the duration + and possibly the timestamp */ + if (cur->flags & MPEG_BLOCK_FLAG_PICTURE) { + if (!mpegvideoparse_handle_picture (mpegvideoparse, buf)) { + /* Corrupted picture. Drop it. */ + GST_DEBUG_OBJECT (mpegvideoparse, + "Corrupted picture header. Dropping buffer of %u bytes", + GST_BUFFER_SIZE (buf)); + mpegvideoparse->need_discont = TRUE; + gst_buffer_unref (buf); + buf = NULL; + } } } @@ -423,7 +346,9 @@ mpegvideoparse_drain_avail (MpegVideoParse * mpegvideoparse) "mpegvideoparse: pushing buffer of %u bytes with ts %" GST_TIME_FORMAT, GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + gst_buffer_set_caps (buf, GST_PAD_CAPS (mpegvideoparse->srcpad)); + if (mpegvideoparse->need_discont) { GST_DEBUG_OBJECT (mpegvideoparse, "setting discont flag on outgoing buffer"); @@ -431,8 +356,6 @@ mpegvideoparse_drain_avail (MpegVideoParse * mpegvideoparse) mpegvideoparse->need_discont = FALSE; } res = gst_pad_push (mpegvideoparse->srcpad, buf); - if (res != GST_FLOW_OK) - break; } /* Advance to the next data block */ @@ -441,6 +364,10 @@ mpegvideoparse_drain_avail (MpegVideoParse * mpegvideoparse) }; return res; +error: + if (buf != NULL) + gst_buffer_unref (buf); + return GST_FLOW_ERROR; } static GstFlowReturn @@ -459,8 +386,8 @@ gst_mpegvideoparse_chain (GstPad * pad, GstBuffer * buf) GST_DEBUG_OBJECT (mpegvideoparse, "mpegvideoparse: received buffer of %u bytes with ts %" - GST_TIME_FORMAT, GST_BUFFER_SIZE (buf), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + GST_TIME_FORMAT " and offset %" G_GINT64_FORMAT, GST_BUFFER_SIZE (buf), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_OFFSET (buf)); /* If we have an offset, and the incoming offset doesn't match, or we have a discont, handle it first by flushing out data @@ -477,6 +404,7 @@ gst_mpegvideoparse_chain (GstPad * pad, GstBuffer * buf) } } + /* Clear out any existing stuff if the new buffer is discontinuous */ if (have_discont) { GST_DEBUG_OBJECT (mpegvideoparse, "Have discont packet, draining data"); mpegvideoparse->need_discont = TRUE; @@ -557,9 +485,10 @@ mpv_parse_sink_event (GstPad * pad, GstEvent * event) gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, &format, &start, &stop, &pos); + GST_DEBUG_OBJECT (mpegvideoparse, - "Pushing newseg rate %g, applied rate %g, " - "format %d, start %lld, stop %lld, pos %lld\n", + "Pushing newseg rate %g, applied rate %g, format %d, start %" + G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT ", pos %" G_GINT64_FORMAT, rate, applied_rate, format, start, stop, pos); res = gst_pad_event_default (pad, event); @@ -570,7 +499,6 @@ mpv_parse_sink_event (GstPad * pad, GstEvent * event) res = gst_pad_event_default (pad, event); break; case GST_EVENT_EOS: - /* Push any remaining buffers out, then flush. */ mpeg_packetiser_handle_eos (&mpegvideoparse->packer); mpegvideoparse_drain_avail (mpegvideoparse); |