diff options
author | Thibault Saunier <thibault.saunier@collabora.com> | 2011-08-02 12:37:02 +0200 |
---|---|---|
committer | Thibault Saunier <thibault.saunier@collabora.com> | 2011-08-31 09:18:16 -0300 |
commit | 3052147bf500edd1e15851765119d7c8e973e15b (patch) | |
tree | d7f0dab0997147cd6a505dd3e50dae1d693f0c97 | |
parent | f6e8467111e96291dc764927e383cec2076bf871 (diff) |
h264parse: Port to the new h.264 parsing library
-rw-r--r-- | gst/videoparsers/Makefile.am | 6 | ||||
-rw-r--r-- | gst/videoparsers/gsth264parse.c | 602 | ||||
-rw-r--r-- | gst/videoparsers/gsth264parse.h | 34 | ||||
-rw-r--r-- | gst/videoparsers/h264parse.c | 1046 | ||||
-rw-r--r-- | gst/videoparsers/h264parse.h | 186 |
5 files changed, 456 insertions, 1418 deletions
diff --git a/gst/videoparsers/Makefile.am b/gst/videoparsers/Makefile.am index 4fa87b8a7..811d5f860 100644 --- a/gst/videoparsers/Makefile.am +++ b/gst/videoparsers/Makefile.am @@ -2,9 +2,8 @@ plugin_LTLIBRARIES = libgstvideoparsersbad.la libgstvideoparsersbad_la_SOURCES = plugin.c \ h263parse.c gsth263parse.c \ - gsth264parse.c h264parse.c \ gstdiracparse.c dirac_parse.c \ - gstmpegvideoparse.c + gsth264parse.c gstmpegvideoparse.c libgstvideoparsersbad_la_CFLAGS = \ $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ @@ -16,9 +15,8 @@ libgstvideoparsersbad_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstvideoparsersbad_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gsth263parse.h h263parse.h \ - gsth264parse.h h264parse.h \ gstdiracparse.h dirac_parse.h \ - gstmpegvideoparse.h + gsth264parse.h gstmpegvideoparse.h Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer \ diff --git a/gst/videoparsers/gsth264parse.c b/gst/videoparsers/gsth264parse.c index 089b5a787..875f04f96 100644 --- a/gst/videoparsers/gsth264parse.c +++ b/gst/videoparsers/gsth264parse.c @@ -1,7 +1,10 @@ /* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> - * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Collabora ltd * Copyright (C) <2010> Nokia Corporation + * Copyright (C) <2011> Intel Corporation + * + * Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> + * Copyright (C) <2011> Thibault Saunier <thibault.saunier@collabora.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -163,9 +166,14 @@ gst_h264_parse_finalize (GObject * object) static void gst_h264_parse_reset_frame (GstH264Parse * h264parse) { + GST_DEBUG_OBJECT (h264parse, "reset frame"); + /* done parsing; reset state */ - h264parse->last_nal_pos = 0; - h264parse->next_sc_pos = 0; + h264parse->nalu.valid = FALSE; + h264parse->nalu.offset = 0; + h264parse->nalu.size = 0; + h264parse->current_off = 0; + h264parse->picture_start = FALSE; h264parse->update_caps = FALSE; h264parse->idr_pos = -1; @@ -202,7 +210,13 @@ gst_h264_parse_start (GstBaseParse * parse) GST_DEBUG_OBJECT (parse, "start"); gst_h264_parse_reset (h264parse); - gst_h264_params_create (&h264parse->params, GST_ELEMENT (h264parse)); + h264parse->nalparser = gst_h264_nal_parser_new (); + + h264parse->dts = GST_CLOCK_TIME_NONE; + h264parse->ts_trn_nb = GST_CLOCK_TIME_NONE; + h264parse->sei_pic_struct_pres_flag = FALSE; + h264parse->sei_pic_struct = 0; + h264parse->field_pic_flag = 0; gst_base_parse_set_min_frame_size (parse, 6); @@ -212,13 +226,19 @@ gst_h264_parse_start (GstBaseParse * parse) static gboolean gst_h264_parse_stop (GstBaseParse * parse) { + guint i; GstH264Parse *h264parse = GST_H264_PARSE (parse); GST_DEBUG_OBJECT (parse, "stop"); gst_h264_parse_reset (h264parse); - gst_h264_params_free (h264parse->params); - h264parse->params = NULL; + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) + gst_buffer_replace (&h264parse->sps_nals[i], NULL); + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) + gst_buffer_replace (&h264parse->pps_nals[i], NULL); + + g_free (h264parse->nalparser); + h264parse->nalparser = NULL; return TRUE; } @@ -318,6 +338,8 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data, GstBuffer *buf; const guint nl = h264parse->nal_length_size; + GST_DEBUG ("Nal length %d %d", size, h264parse->nal_length_size); + buf = gst_buffer_new_and_alloc (size + nl + 4); if (format == GST_H264_PARSE_FORMAT_AVC) { GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), size << (32 - 8 * nl)); @@ -332,65 +354,158 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data, return buf; } +static void +gst_h264_parser_store_nal (GstH264Parse * h264parse, guint id, + GstH264NalUnitType naltype, GstH264NalUnit * nalu) +{ + GstBuffer *buf, **store; + guint size = nalu->size, store_size; + + if (naltype == GST_H264_NAL_SPS) { + store_size = GST_H264_MAX_SPS_COUNT; + store = h264parse->sps_nals; + GST_DEBUG ("Storing sps %u", id); + } else if (naltype == GST_H264_NAL_PPS) { + store_size = GST_H264_MAX_PPS_COUNT; + store = h264parse->pps_nals; + GST_DEBUG ("Storing pps %u", id); + } else + return; + + if (id >= store_size) { + GST_DEBUG_OBJECT (h264parse, "unable to store nal, id out-of-range %d", id); + return; + } + + buf = gst_buffer_new_and_alloc (size); + memcpy (GST_BUFFER_DATA (buf), nalu->data + nalu->offset, size); + + if (store[id]) + gst_buffer_unref (store[id]); + + store[id] = buf; +} + /* SPS/PPS/IDR considered key, all others DELTA; * so downstream waiting for keyframe can pick up at SPS/PPS/IDR */ #define NAL_TYPE_IS_KEY(nt) (((nt) == 5) || ((nt) == 7) || ((nt) == 8)) /* caller guarantees 2 bytes of nal payload */ static void -gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data, - gint sc_pos, gint nal_pos, guint nal_size) +gst_h264_parse_process_nal (GstH264Parse * h264parse, GstH264NalUnit * nalu) { guint nal_type; + GstH264SliceHdr slice; + GstH264PPS pps; + GstH264SPS sps; + GstH264SEIMessage sei; + + gboolean slcparsed = FALSE; + GstH264NalParser *nalparser = h264parse->nalparser; - g_return_if_fail (nal_pos - sc_pos > 0 && nal_pos - sc_pos <= 4); /* nothing to do for broken input */ - if (G_UNLIKELY (nal_size < 2)) { - GST_DEBUG_OBJECT (h264parse, "not processing nal size %u", nal_size); + if (G_UNLIKELY (nalu->size < 2)) { + GST_DEBUG_OBJECT (h264parse, "not processing nal size %u", nalu->size); return; } - /* lower layer collects params */ - gst_h264_params_parse_nal (h264parse->params, data + nal_pos, nal_size); - /* we have a peek as well */ - nal_type = data[nal_pos] & 0x1f; + nal_type = nalu->type; h264parse->keyframe |= NAL_TYPE_IS_KEY (nal_type); + GST_DEBUG_OBJECT (h264parse, "processing nal of type %u, size %u", + nal_type, nalu->size); + switch (nal_type) { - case NAL_SPS: - case NAL_PPS: + case GST_H264_NAL_SPS: + gst_h264_parser_parse_sps (nalparser, nalu, &sps, TRUE); + + GST_DEBUG_OBJECT (h264parse, "triggering src caps check"); + h264parse->update_caps = TRUE; + /* found in stream, no need to forcibly push at start */ + h264parse->push_codec = FALSE; + + gst_h264_parser_store_nal (h264parse, sps.id, nal_type, nalu); + break; + case GST_H264_NAL_PPS: + gst_h264_parser_parse_pps (nalparser, nalu, &pps); /* parameters might have changed, force caps check */ GST_DEBUG_OBJECT (h264parse, "triggering src caps check"); h264parse->update_caps = TRUE; /* found in stream, no need to forcibly push at start */ h264parse->push_codec = FALSE; + + gst_h264_parser_store_nal (h264parse, pps.id, nal_type, nalu); break; - case NAL_SLICE: - case NAL_SLICE_DPA: - case NAL_SLICE_DPB: - case NAL_SLICE_DPC: + case GST_H264_NAL_SEI: + gst_h264_parser_parse_sei (nalparser, nalu, &sei); + switch (sei.payloadType) { + case GST_H264_SEI_PIC_TIMING: + h264parse->sei_pic_struct_pres_flag = + sei.pic_timing.pic_struct_present_flag; + h264parse->sei_cpb_removal_delay = sei.pic_timing.cpb_removal_delay; + if (h264parse->sei_pic_struct_pres_flag) + h264parse->sei_pic_struct = sei.pic_timing.pic_struct; + break; + case GST_H264_SEI_BUF_PERIOD: + if (h264parse->ts_trn_nb == GST_CLOCK_TIME_NONE || + h264parse->dts == GST_CLOCK_TIME_NONE) + h264parse->ts_trn_nb = 0; + else + h264parse->ts_trn_nb = h264parse->dts; + + GST_LOG_OBJECT (h264parse, + "new buffering period; ts_trn_nb updated: %" GST_TIME_FORMAT, + GST_TIME_ARGS (h264parse->ts_trn_nb)); + break; + } + break; + + case GST_H264_NAL_SLICE: + case GST_H264_NAL_SLICE_DPA: + case GST_H264_NAL_SLICE_DPB: + case GST_H264_NAL_SLICE_DPC: + slcparsed = TRUE; + if (gst_h264_parser_parse_slice_hdr (nalparser, nalu, + &slice, FALSE, FALSE) == GST_H264_PARSER_ERROR) + return; + /* real frame data */ - h264parse->frame_start |= (h264parse->params->first_mb_in_slice == 0); + h264parse->frame_start |= (slice.first_mb_in_slice == 0); /* if we need to sneak codec NALs into the stream, * this is a good place, so fake it as IDR * (which should be at start anyway) */ + GST_DEBUG ("Frame start: %i first_mb_in_slice %i", h264parse->frame_start, + slice.first_mb_in_slice); if (G_LIKELY (!h264parse->push_codec)) break; /* fall-through */ - case NAL_SLICE_IDR: + case GST_H264_NAL_SLICE_IDR: + if (!slcparsed) { + if (gst_h264_parser_parse_slice_hdr (nalparser, nalu, + &slice, FALSE, FALSE) == GST_H264_PARSER_ERROR) + return; + GST_DEBUG ("Frame start: %i first_mb_in_slice %i", + h264parse->frame_start, slice.first_mb_in_slice); + } /* real frame data */ - h264parse->frame_start |= (h264parse->params->first_mb_in_slice == 0); + h264parse->frame_start |= (slice.first_mb_in_slice == 0); + /* mark where config needs to go if interval expired */ /* mind replacement buffer if applicable */ if (h264parse->format == GST_H264_PARSE_FORMAT_AVC) h264parse->idr_pos = gst_adapter_available (h264parse->frame_out); else - h264parse->idr_pos = sc_pos; + h264parse->idr_pos = nalu->offset - 4; GST_DEBUG_OBJECT (h264parse, "marking IDR in frame at offset %d", h264parse->idr_pos); + + GST_DEBUG ("first MB: %u, slice type: %u", slice.first_mb_in_slice, + slice.type); break; + default: + gst_h264_parser_parse_nal (nalparser, nalu); } /* if AVC output needed, collect properly prefixed nal in adapter, @@ -400,7 +515,7 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data, GST_LOG_OBJECT (h264parse, "collecting NAL in AVC frame"); buf = gst_h264_parse_wrap_nal (h264parse, h264parse->format, - data + nal_pos, nal_size); + nalu->data + nalu->offset, nalu->size); gst_adapter_push (h264parse->frame_out, buf); } } @@ -408,21 +523,31 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data, /* caller guarantees at least 2 bytes of nal payload for each nal * returns TRUE if next_nal indicates that nal terminates an AU */ static inline gboolean -gst_h264_parse_collect_nal (GstH264Parse * h264parse, guint8 * nal, - guint8 * next_nal) +gst_h264_parse_collect_nal (GstH264Parse * h264parse, const guint8 * data, + guint size, GstH264NalUnit * nalu) { - gint nal_type; gboolean complete; + GstH264ParserResult parse_res; + GstH264NalUnitType nal_type = nalu->type; + GstH264NalUnit nnalu; + + GST_DEBUG ("Parsing collecte nal"); + parse_res = gst_h264_parser_identify_nalu (h264parse->nalparser, data, + nalu->offset + nalu->size, size, &nnalu); + + if (parse_res == GST_H264_PARSER_ERROR) + return FALSE; - if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) + if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) { return TRUE; + } /* determine if AU complete */ - nal_type = nal[0] & 0x1f; GST_LOG_OBJECT (h264parse, "nal type: %d", nal_type); /* coded slice NAL starts a picture, * i.e. other types become aggregated in front of it */ - h264parse->picture_start |= (nal_type == 1 || nal_type == 2 || nal_type == 5); + h264parse->picture_start |= (nal_type == GST_H264_NAL_SLICE || + nal_type == GST_H264_NAL_SLICE_DPA || nal_type == GST_H264_NAL_SLICE_IDR); /* consider a coded slices (IDR or not) to start a picture, * (so ending the previous one) if first_mb_in_slice == 0 @@ -431,35 +556,23 @@ gst_h264_parse_collect_nal (GstH264Parse * h264parse, guint8 * nal, * but in practice it works in sane cases, needs not much parsing, * and also works with broken frame_num in NAL * (where spec-wise would fail) */ - nal_type = next_nal[0] & 0x1f; + nal_type = nnalu.type; + complete = h264parse->picture_start && (nal_type >= GST_H264_NAL_SEI && + nal_type <= GST_H264_NAL_AU_DELIMITER); + GST_LOG_OBJECT (h264parse, "next nal type: %d", nal_type); - complete = h264parse->picture_start && (nal_type >= 6 && nal_type <= 9); complete |= h264parse->picture_start && - (nal_type == 1 || nal_type == 2 || nal_type == 5) && + (nal_type == GST_H264_NAL_SLICE || + nal_type == GST_H264_NAL_SLICE_DPA || + nal_type == GST_H264_NAL_SLICE_IDR) && /* first_mb_in_slice == 0 considered start of frame */ - (next_nal[1] & 0x80); + (nnalu.data[nnalu.offset + 1] & 0x80); GST_LOG_OBJECT (h264parse, "au complete: %d", complete); return complete; } -/* finds next startcode == 00 00 01, along with a subsequent byte */ -static guint -gst_h264_parse_find_sc (GstBuffer * buffer, guint skip) -{ - GstByteReader br; - guint sc_pos = -1; - - gst_byte_reader_init_from_buffer (&br, buffer); - - /* NALU not empty, so we can at least expect 1 (even 2) bytes following sc */ - sc_pos = gst_byte_reader_masked_scan_uint32 (&br, 0xffffff00, 0x00000100, - skip, gst_byte_reader_get_remaining (&br) - skip); - - return sc_pos; -} - /* FIXME move into baseparse, or anything equivalent; * see https://bugzilla.gnome.org/show_bug.cgi?id=650093 */ #define GST_BASE_PARSE_FRAME_FLAG_PARSING 0x10000 @@ -470,10 +583,11 @@ gst_h264_parse_check_valid_frame (GstBaseParse * parse, { GstH264Parse *h264parse = GST_H264_PARSE (parse); GstBuffer *buffer = frame->buffer; - gint sc_pos, nal_pos, next_sc_pos, next_nal_pos; guint8 *data; - guint size; + guint size, current_off = 0; gboolean drain; + GstH264NalParser *nalparser = h264parse->nalparser; + GstH264NalUnit nalu = h264parse->nalu; /* expect at least 3 bytes startcode == sc, and 2 bytes NALU payload */ if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < 5)) @@ -495,105 +609,108 @@ gst_h264_parse_check_valid_frame (GstBaseParse * parse, data = GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer); - GST_LOG_OBJECT (h264parse, "last_nal_pos: %d, last_scan_pos %d", - h264parse->last_nal_pos, h264parse->next_sc_pos); + drain = GST_BASE_PARSE_DRAINING (parse); + current_off = h264parse->current_off; - nal_pos = h264parse->last_nal_pos; - next_sc_pos = h264parse->next_sc_pos; + GST_DEBUG ("Last parse position %u", current_off); + while (TRUE) { + switch (gst_h264_parser_identify_nalu (nalparser, data, current_off, + size, &nalu)) { + case GST_H264_PARSER_OK: + GST_DEBUG ("Complete nal found. %u Off: %u, Size: %u", + current_off, nalu.offset, nalu.size); + + current_off = nalu.offset + nalu.size; + GST_DEBUG ("CURENT OFF. %u, %u", current_off, nalu.offset + nalu.size); + if (!h264parse->nalu.size && !h264parse->nalu.valid) + h264parse->nalu = nalu; + break; + case GST_H264_PARSER_BROKEN_LINK: + return FALSE; + case GST_H264_PARSER_ERROR: + current_off = size - 3; + goto parsing_error; + case GST_H264_PARSER_NO_NAL: + current_off = size - 3; + goto more; + case GST_H264_PARSER_BROKEN_DATA: + GST_WARNING_OBJECT (h264parse, "input stream is corrupt; " + "it contains a NAL unit of length %d", nalu.size); + + /* broken nal at start -> arrange to skip it, + * otherwise have it terminate current au + * (and so it will be skipped on next frame round) */ + if (nalu.sc_offset == h264parse->nalu.sc_offset) { + *skipsize = nalu.offset; + + GST_DEBUG ("Skiping broken nal"); + return FALSE; + } else { + nalu.size = 0; - if (!next_sc_pos) { - sc_pos = gst_h264_parse_find_sc (buffer, 0); + goto end; + } + case GST_H264_PARSER_NO_NAL_END: + GST_DEBUG ("Not a complete nal found at offset %u", nalu.offset); + + current_off = nalu.sc_offset; + /* We keep the reference to this nal so we start over the parsing + * here */ + if (!h264parse->nalu.size && !h264parse->nalu.valid) + h264parse->nalu = nalu; + + if (drain) { + GST_DEBUG ("Drainning NAL %u %u %u", size, h264parse->nalu.offset, + h264parse->nalu.size); + /* Can't parse the nalu */ + if (size - h264parse->nalu.offset < 2) { + *skipsize = nalu.offset; + return FALSE; + } - if (sc_pos == -1) { - /* SC not found, need more data */ - sc_pos = GST_BUFFER_SIZE (buffer) - 3; - /* avoid going < 0 later on */ - nal_pos = next_sc_pos = sc_pos; - goto more; + /* We parse it anyway */ + nalu.size = size - nalu.offset; + break; + } + goto more; } - nal_pos = sc_pos + 3; - next_sc_pos = nal_pos; - /* sc might have 2 or 3 0-bytes */ - if (sc_pos > 0 && data[sc_pos - 1] == 00) - sc_pos--; - GST_LOG_OBJECT (h264parse, "found sc at offset %d", sc_pos); - } else { - /* previous checks already arrange sc at start */ - sc_pos = 0; - } + current_off = nalu.offset + nalu.size; - drain = GST_BASE_PARSE_DRAINING (parse); - while (TRUE) { - gint prev_sc_pos; - - next_sc_pos = gst_h264_parse_find_sc (buffer, next_sc_pos); - if (next_sc_pos == -1) { - GST_LOG_OBJECT (h264parse, "no next sc"); - if (drain) { - /* FLUSH/EOS, it's okay if we can't find the next frame */ - next_sc_pos = size; - next_nal_pos = size; - } else { - next_sc_pos = size - 3; - goto more; - } - } else { - next_nal_pos = next_sc_pos + 3; - if (data[next_sc_pos - 1] == 00) - next_sc_pos--; - GST_LOG_OBJECT (h264parse, "found next sc at offset %d", next_sc_pos); - /* need at least 1 more byte of next NAL */ - if (!drain && (next_nal_pos == size - 1)) - goto more; - } + GST_DEBUG ("%p Complete nal found. Off: %u, Size: %u", data, nalu.offset, + nalu.size); - /* determine nal's sc position */ - prev_sc_pos = nal_pos - 3; - g_assert (prev_sc_pos >= 0); - if (prev_sc_pos > 0 && data[prev_sc_pos - 1] == 0) - prev_sc_pos--; - - /* already consume and gather info from NAL */ - if (G_UNLIKELY (next_sc_pos - nal_pos < 2)) { - GST_WARNING_OBJECT (h264parse, "input stream is corrupt; " - "it contains a NAL unit of length %d", next_sc_pos - nal_pos); - /* broken nal at start -> arrange to skip it, - * otherwise have it terminate current au - * (and so it will be skippd on next frame round) */ - if (prev_sc_pos == sc_pos) { - *skipsize = sc_pos + 2; - return FALSE; - } else { - next_sc_pos = prev_sc_pos; - break; - } - } - gst_h264_parse_process_nal (h264parse, data, prev_sc_pos, nal_pos, - next_sc_pos - nal_pos); - if (next_nal_pos >= size - 1 || - gst_h264_parse_collect_nal (h264parse, data + nal_pos, - data + next_nal_pos)) + gst_h264_parse_process_nal (h264parse, &nalu); + if (gst_h264_parse_collect_nal (h264parse, data, size, &nalu) || drain) break; - - /* move along */ - next_sc_pos = nal_pos = next_nal_pos; } - *skipsize = sc_pos; - *framesize = next_sc_pos - sc_pos; +end: + /* FIXME this shouldnt be needed */ + if (h264parse->nalu.sc_offset > 0 && data[h264parse->nalu.sc_offset - 1] == 0) + h264parse->nalu.sc_offset--; + + *skipsize = h264parse->nalu.sc_offset; + *framesize = nalu.offset + nalu.size - h264parse->nalu.sc_offset; /* CHECKME */ + h264parse->current_off = current_off; return TRUE; +parsing_error: + GST_DEBUG ("Error parsing Nal Unit"); more: + /* ask for best next available */ *framesize = G_MAXUINT; + if (!h264parse->nalu.size) { + /* skip up to initial startcode */ + *skipsize = h264parse->nalu.sc_offset; + } else { + *skipsize = 0; + } - /* skip up to initial startcode */ - *skipsize = sc_pos; - /* resume scanning here next time */ - h264parse->last_nal_pos = nal_pos - sc_pos; - h264parse->next_sc_pos = next_sc_pos - sc_pos; + /* Restart parsing from here next time */ + h264parse->current_off = current_off; return FALSE; } @@ -610,8 +727,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) /* only nal payload in stored nals */ - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((nal = h264parse->sps_nals[i])) { num_sps++; /* size bytes also count */ sps_size += GST_BUFFER_SIZE (nal) + 2; @@ -623,8 +740,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) } } } - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((nal = h264parse->pps_nals[i])) { num_pps++; /* size bytes also count */ pps_size += GST_BUFFER_SIZE (nal) + 2; @@ -648,8 +765,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) data[5] = 0xe0 | num_sps; /* number of SPSs */ data += 6; - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((nal = h264parse->sps_nals[i])) { GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal)); memcpy (data + 2, GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal)); data += 2 + GST_BUFFER_SIZE (nal); @@ -658,8 +775,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) data[0] = num_pps; data++; - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((nal = h264parse->pps_nals[i])) { GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal)); memcpy (data + 2, GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal)); data += 2 + GST_BUFFER_SIZE (nal); @@ -672,7 +789,7 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) static void gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) { - GstH264ParamsSPS *sps; + GstH264SPS *sps; GstCaps *sink_caps; gboolean modified = FALSE; GstBuffer *buf = NULL; @@ -695,7 +812,7 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) else sink_caps = gst_caps_new_simple ("video/x-h264", NULL); - sps = h264parse->params->sps; + sps = h264parse->nalparser->last_sps; GST_DEBUG_OBJECT (h264parse, "sps: %p", sps); /* only codec-data for nice-and-clean au aligned packetized avc format */ @@ -764,6 +881,134 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) gst_buffer_unref (buf); } +static void +gst_h264_parse_get_timestamp (GstH264Parse * h264parse, + GstClockTime * out_ts, GstClockTime * out_dur, gboolean frame) +{ + GstH264SPS *sps = h264parse->nalparser->last_sps; + GstClockTime upstream; + gint duration = 1; + + g_return_if_fail (out_dur != NULL); + g_return_if_fail (out_ts != NULL); + + upstream = *out_ts; + + if (!frame) { + GST_LOG_OBJECT (h264parse, "no frame data -> 0 duration"); + *out_dur = 0; + goto exit; + } else { + *out_ts = upstream; + } + + if (!sps) { + GST_DEBUG_OBJECT (h264parse, "referred SPS invalid"); + goto exit; + } else if (!sps->vui_parameters.timing_info_present_flag) { + GST_DEBUG_OBJECT (h264parse, + "unable to compute timestamp: timing info not present"); + goto exit; + } else if (sps->vui_parameters.time_scale == 0) { + GST_DEBUG_OBJECT (h264parse, + "unable to compute timestamp: time_scale = 0 " + "(this is forbidden in spec; bitstream probably contains error)"); + goto exit; + } + + if (h264parse->sei_pic_struct_pres_flag && + h264parse->sei_pic_struct != (guint8) - 1) { + /* Note that when h264parse->sei_pic_struct == -1 (unspecified), there + * are ways to infer its value. This is related to computing the + * TopFieldOrderCnt and BottomFieldOrderCnt, which looks + * complicated and thus not implemented for the time being. Yet + * the value we have here is correct for many applications + */ + switch (h264parse->sei_pic_struct) { + case GST_H264_SEI_PIC_STRUCT_TOP_FIELD: + case GST_H264_SEI_PIC_STRUCT_BOTTOM_FIELD: + duration = 1; + break; + case GST_H264_SEI_PIC_STRUCT_FRAME: + case GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM: + case GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP: + duration = 2; + break; + case GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP: + case GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: + duration = 3; + break; + case GST_H264_SEI_PIC_STRUCT_FRAME_DOUBLING: + duration = 4; + break; + case GST_H264_SEI_PIC_STRUCT_FRAME_TRIPLING: + duration = 6; + break; + default: + GST_DEBUG_OBJECT (h264parse, + "h264parse->sei_pic_struct of unknown value %d. Not parsed", + h264parse->sei_pic_struct); + break; + } + } else { + duration = h264parse->field_pic_flag ? 1 : 2; + } + + GST_LOG_OBJECT (h264parse, "frame tick duration %d", duration); + + /* + * h264parse.264 C.1.2 Timing of coded picture removal (equivalent to DTS): + * Tr,n(0) = initial_cpb_removal_delay[ SchedSelIdx ] / 90000 + * Tr,n(n) = Tr,n(nb) + Tc * cpb_removal_delay(n) + * where + * Tc = num_units_in_tick / time_scale + */ + + if (h264parse->ts_trn_nb != GST_CLOCK_TIME_NONE) { + GST_LOG_OBJECT (h264parse, "buffering based ts"); + /* buffering period is present */ + if (upstream != GST_CLOCK_TIME_NONE) { + /* If upstream timestamp is valid, we respect it and adjust current + * reference point */ + h264parse->ts_trn_nb = upstream - + (GstClockTime) gst_util_uint64_scale_int + (h264parse->sei_cpb_removal_delay * GST_SECOND, + sps->vui_parameters.num_units_in_tick, + sps->vui_parameters.time_scale); + } else { + /* If no upstream timestamp is given, we write in new timestamp */ + upstream = h264parse->dts = h264parse->ts_trn_nb + + (GstClockTime) gst_util_uint64_scale_int + (h264parse->sei_cpb_removal_delay * GST_SECOND, + sps->vui_parameters.num_units_in_tick, + sps->vui_parameters.time_scale); + } + } else { + GstClockTime dur; + + GST_LOG_OBJECT (h264parse, "duration based ts"); + /* naive method: no removal delay specified + * track upstream timestamp and provide best guess frame duration */ + dur = gst_util_uint64_scale_int (duration * GST_SECOND, + sps->vui_parameters.num_units_in_tick, sps->vui_parameters.time_scale); + /* sanity check */ + if (dur < GST_MSECOND) { + GST_DEBUG_OBJECT (h264parse, "discarding dur %" GST_TIME_FORMAT, + GST_TIME_ARGS (dur)); + } else { + *out_dur = dur; + } + } + +exit: + if (GST_CLOCK_TIME_IS_VALID (upstream)) + *out_ts = h264parse->dts = upstream; + + if (GST_CLOCK_TIME_IS_VALID (*out_dur) && + GST_CLOCK_TIME_IS_VALID (h264parse->dts)) + h264parse->dts += *out_dur; +} + static GstFlowReturn gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) { @@ -776,7 +1021,7 @@ gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) gst_h264_parse_update_src_caps (h264parse, NULL); - gst_h264_params_get_timestamp (h264parse->params, + gst_h264_parse_get_timestamp (h264parse, &GST_BUFFER_TIMESTAMP (buffer), &GST_BUFFER_DURATION (buffer), h264parse->frame_start); @@ -864,16 +1109,16 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) { /* send separate config NAL buffers */ GST_DEBUG_OBJECT (h264parse, "- sending SPS/PPS"); - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((codec_nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((codec_nal = h264parse->sps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "sending SPS nal"); gst_h264_parse_push_codec_buffer (h264parse, codec_nal, timestamp); h264parse->last_report = new_ts; } } - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((codec_nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((codec_nal = h264parse->pps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "sending PPS nal"); gst_h264_parse_push_codec_buffer (h264parse, codec_nal, timestamp); @@ -890,8 +1135,8 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (buffer), h264parse->idr_pos); GST_DEBUG_OBJECT (h264parse, "- inserting SPS/PPS"); - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((codec_nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((codec_nal = h264parse->sps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "inserting SPS nal"); gst_byte_writer_put_uint32_be (&bw, bs ? 1 : GST_BUFFER_SIZE (codec_nal)); @@ -900,8 +1145,8 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) h264parse->last_report = new_ts; } } - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((codec_nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((codec_nal = h264parse->pps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "inserting PPS nal"); gst_byte_writer_put_uint32_be (&bw, bs ? 1 : GST_BUFFER_SIZE (codec_nal)); @@ -937,7 +1182,9 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) GstStructure *str; const GValue *value; GstBuffer *codec_data = NULL; - guint size, format, align; + guint size, format, align, off; + GstH264NalUnit nalu; + GstH264ParserResult parseres; h264parse = GST_H264_PARSE (parse); @@ -959,7 +1206,7 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) if (format != GST_H264_PARSE_FORMAT_BYTE && (value = gst_structure_get_value (str, "codec_data"))) { guint8 *data; - guint num_sps, num_pps, profile, len; + guint num_sps, num_pps, profile; gint i; GST_DEBUG_OBJECT (h264parse, "have packetized h264"); @@ -992,28 +1239,29 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) GST_DEBUG_OBJECT (h264parse, "nal length %u", h264parse->nal_length_size); num_sps = data[5] & 0x1f; - data += 6; - size -= 6; + off = 6; for (i = 0; i < num_sps; i++) { - len = GST_READ_UINT16_BE (data); - if (size < len + 2 || len < 2) + parseres = gst_h264_parser_identify_nalu_avc (h264parse->nalparser, + data, off, size - off, 2, &nalu); + if (parseres != GST_H264_PARSER_OK) goto avcc_too_small; - /* digest for later reference */ - gst_h264_parse_process_nal (h264parse, data, 0, 2, len); - data += len + 2; - size -= len + 2; + + gst_h264_parse_process_nal (h264parse, &nalu); + off = nalu.offset + nalu.size; } + num_pps = data[0]; data++; size++; for (i = 0; i < num_pps; i++) { - len = GST_READ_UINT16_BE (data); - if (size < len + 2 || len < 2) + parseres = gst_h264_parser_identify_nalu_avc (h264parse->nalparser, + data, off, size - off, 2, &nalu); + if (parseres != GST_H264_PARSER_OK) { goto avcc_too_small; - /* digest for later reference */ - gst_h264_parse_process_nal (h264parse, data, 0, 2, len); - data += len + 2; - size -= len + 2; + } + + gst_h264_parse_process_nal (h264parse, &nalu); + off = nalu.offset + nalu.size; } h264parse->codec_data = gst_buffer_ref (codec_data); @@ -1089,10 +1337,12 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) GstBuffer *sub; GstFlowReturn ret = GST_FLOW_OK; guint32 len; + GstH264NalUnit nalu; const guint nl = h264parse->nal_length_size; GST_LOG_OBJECT (h264parse, "processing packet buffer of size %d", GST_BUFFER_SIZE (buffer)); + gst_byte_reader_init_from_buffer (&br, buffer); while (ret == GST_FLOW_OK && gst_byte_reader_get_remaining (&br)) { GST_DEBUG_OBJECT (h264parse, "AVC nal offset %d", @@ -1114,9 +1364,10 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) break; default: goto not_negotiated; - break; } + GST_DEBUG_OBJECT (h264parse, "AVC nal size %d", len); + if (gst_byte_reader_get_remaining (&br) < len) goto parse_failed; if (h264parse->split_packetized) { @@ -1133,9 +1384,10 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) /* NOTE: so if it is really configured to do so, * pre_push can/will still insert codec-data at intervals, * which is not really pure pass-through, but anyway ... */ - gst_h264_parse_process_nal (h264parse, + gst_h264_parser_identify_nalu (h264parse->nalparser, GST_BUFFER_DATA (buffer), gst_byte_reader_get_pos (&br) - nl, - gst_byte_reader_get_pos (&br), len); + GST_BUFFER_SIZE (buffer), &nalu); + gst_h264_parse_process_nal (h264parse, &nalu); gst_byte_reader_skip_unchecked (&br, len); } } diff --git a/gst/videoparsers/gsth264parse.h b/gst/videoparsers/gsth264parse.h index 1aa132321..e013a3fac 100644 --- a/gst/videoparsers/gsth264parse.h +++ b/gst/videoparsers/gsth264parse.h @@ -1,7 +1,10 @@ /* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> - * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Collabora ltd * Copyright (C) <2010> Nokia Corporation + * Copyright (C) <2011> Intel Corporation + * + * Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> + * Copyright (C) <2011> Thibault Saunier <thibault.saunier@collabora.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,8 +27,7 @@ #include <gst/gst.h> #include <gst/base/gstbaseparse.h> - -#include "h264parse.h" +#include <gst/codecparsers/gsth264parser.h> G_BEGIN_DECLS @@ -61,16 +63,34 @@ struct _GstH264Parse gboolean packetized; /* state */ - GstH264Params *params; + GstH264NalParser *nalparser; + GstH264NalUnit nalu; guint align; guint format; + guint current_off; GstClockTime last_report; gboolean push_codec; + /* collected SPS and PPS NALUs */ + GstBuffer *sps_nals[GST_H264_MAX_SPS_COUNT]; + GstBuffer *pps_nals[GST_H264_MAX_PPS_COUNT]; + + /* Infos we need to keep track of */ + guint32 sei_cpb_removal_delay; + guint8 sei_pic_struct; + guint8 sei_pic_struct_pres_flag; + guint field_pic_flag; + + /* cached timestamps */ + /* (trying to) track upstream dts and interpolate */ + GstClockTime dts; + /* dts at start of last buffering period */ + GstClockTime ts_trn_nb; + /* frame parsing */ - guint last_nal_pos; - guint next_sc_pos; + /*guint last_nal_pos;*/ + /*guint next_sc_pos;*/ gint idr_pos; gboolean update_caps; GstAdapter *frame_out; diff --git a/gst/videoparsers/h264parse.c b/gst/videoparsers/h264parse.c deleted file mode 100644 index a556d449c..000000000 --- a/gst/videoparsers/h264parse.c +++ /dev/null @@ -1,1046 +0,0 @@ -/* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> - * Copyright (C) <2010> Collabora Multimedia - * Copyright (C) <2010> Nokia Corporation - * - * Some bits C-c,C-v'ed and s/4/3 from h264parse: - * (C) 2005 Michal Benes <michal.benes@itonis.tv> - * (C) 2008 Wim Taymans <wim.taymans@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "h264parse.h" - -#include <string.h> - -GST_DEBUG_CATEGORY_EXTERN (h264_parse_debug); -#define GST_CAT_DEFAULT h264_parse_debug - -/* simple bitstream parser, automatically skips over - * emulation_prevention_three_bytes. */ -typedef struct -{ - const guint8 *orig_data; - const guint8 *data; - const guint8 *end; - /* bitpos in the cache of next bit */ - gint head; - /* cached bytes */ - guint64 cache; -} GstNalBs; - -static void -gst_nal_bs_init (GstNalBs * bs, const guint8 * data, guint size) -{ - bs->orig_data = data; - bs->data = data; - bs->end = data + size; - bs->head = 0; - /* fill with something other than 0 to detect emulation prevention bytes */ - bs->cache = 0xffffffff; -} - -static inline void -gst_nal_bs_get_data (GstNalBs * bs, const guint8 ** data, guint * size) -{ - *data = bs->orig_data; - *size = bs->end - bs->orig_data; -} - -static guint32 -gst_nal_bs_read (GstNalBs * bs, guint n) -{ - guint32 res = 0; - gint shift; - - if (n == 0) - return res; - - /* fill up the cache if we need to */ - while (bs->head < n) { - guint8 byte; - gboolean check_three_byte; - - check_three_byte = TRUE; - next_byte: - if (bs->data >= bs->end) { - /* we're at the end, can't produce more than head number of bits */ - n = bs->head; - break; - } - /* get the byte, this can be an emulation_prevention_three_byte that we need - * to ignore. */ - byte = *bs->data++; - if (check_three_byte && byte == 0x03 && ((bs->cache & 0xffff) == 0)) { - /* next byte goes unconditionally to the cache, even if it's 0x03 */ - check_three_byte = FALSE; - goto next_byte; - } - /* shift bytes in cache, moving the head bits of the cache left */ - bs->cache = (bs->cache << 8) | byte; - bs->head += 8; - } - - /* bring the required bits down and truncate */ - if ((shift = bs->head - n) > 0) - res = bs->cache >> shift; - else - res = bs->cache; - - /* mask out required bits */ - if (n < 32) - res &= (1 << n) - 1; - - bs->head = shift; - - return res; -} - -static gboolean -gst_nal_bs_eos (GstNalBs * bs) -{ - return (bs->data >= bs->end) && (bs->head == 0); -} - -/* read unsigned Exp-Golomb code */ -static gint -gst_nal_bs_read_ue (GstNalBs * bs) -{ - gint i = 0; - - while (gst_nal_bs_read (bs, 1) == 0 && !gst_nal_bs_eos (bs) && i < 32) - i++; - - return ((1 << i) - 1 + gst_nal_bs_read (bs, i)); -} - -/* read signed Exp-Golomb code */ -static gint -gst_nal_bs_read_se (GstNalBs * bs) -{ - gint i = 0; - - i = gst_nal_bs_read_ue (bs); - /* (-1)^(i+1) Ceil (i / 2) */ - i = (i + 1) / 2 * (i & 1 ? 1 : -1); - - return i; -} - -/* end parser helper */ - -static void -gst_h264_params_store_nal (GstH264Params * params, GstBuffer ** store, - gint store_size, gint id, GstNalBs * bs) -{ - const guint8 *data; - GstBuffer *buf; - guint size; - - if (id >= store_size) { - GST_DEBUG_OBJECT (params->el, - "unable to store nal, id out-of-range %d", id); - return; - } - - gst_nal_bs_get_data (bs, &data, &size); - buf = gst_buffer_new_and_alloc (size); - memcpy (GST_BUFFER_DATA (buf), data, size); - - if (store[id]) - gst_buffer_unref (store[id]); - - store[id] = buf; -} - -static GstH264ParamsSPS * -gst_h264_params_get_sps (GstH264Params * params, guint8 sps_id, gboolean set) -{ - GstH264ParamsSPS *sps; - - g_return_val_if_fail (params != NULL, NULL); - - if (G_UNLIKELY (sps_id >= MAX_SPS_COUNT)) { - GST_WARNING_OBJECT (params->el, - "requested sps_id=%04x out of range", sps_id); - return NULL; - } - - sps = ¶ms->sps_buffers[sps_id]; - if (set) { - if (sps->valid) { - params->sps = sps; - } else { - GST_WARNING_OBJECT (params->el, "invalid sps not selected"); - params->sps = NULL; - sps = NULL; - } - } - - return sps; -} - -static GstH264ParamsPPS * -gst_h264_params_get_pps (GstH264Params * params, guint8 pps_id, gboolean set) -{ - GstH264ParamsPPS *pps; - - g_return_val_if_fail (params != NULL, NULL); - - pps = ¶ms->pps_buffers[pps_id]; - if (set) { - if (pps->valid) { - params->pps = pps; - } else { - GST_WARNING_OBJECT (params->el, "invalid pps not selected"); - params->pps = NULL; - pps = NULL; - } - } - - return pps; -} - -static gboolean -gst_h264_params_decode_sps_vui_hrd (GstH264Params * params, - GstH264ParamsSPS * sps, GstNalBs * bs) -{ - gint sched_sel_idx; - - sps->cpb_cnt_minus1 = gst_nal_bs_read_ue (bs); - if (sps->cpb_cnt_minus1 > 31U) { - GST_WARNING_OBJECT (params->el, "cpb_cnt_minus1 = %d out of range", - sps->cpb_cnt_minus1); - return FALSE; - } - - /* bit_rate_scale */ - gst_nal_bs_read (bs, 4); - /* cpb_size_scale */ - gst_nal_bs_read (bs, 4); - - for (sched_sel_idx = 0; sched_sel_idx <= sps->cpb_cnt_minus1; sched_sel_idx++) { - /* bit_rate_value_minus1 */ - gst_nal_bs_read_ue (bs); - /* cpb_size_value_minus1 */ - gst_nal_bs_read_ue (bs); - /* cbr_flag */ - gst_nal_bs_read (bs, 1); - } - - sps->initial_cpb_removal_delay_length_minus1 = gst_nal_bs_read (bs, 5); - sps->cpb_removal_delay_length_minus1 = gst_nal_bs_read (bs, 5); - sps->dpb_output_delay_length_minus1 = gst_nal_bs_read (bs, 5); - sps->time_offset_length_minus1 = gst_nal_bs_read (bs, 5); - - return TRUE; -} - -static gboolean -gst_h264_params_decode_sps_vui (GstH264Params * params, GstH264ParamsSPS * sps, - GstNalBs * bs) -{ - if (G_UNLIKELY (!sps)) - return FALSE; - - /* aspect_ratio_info_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* aspect_ratio_idc */ - if (gst_nal_bs_read (bs, 8) == 255) { - /* Extended_SAR */ - /* sar_width */ - gst_nal_bs_read (bs, 16); - /* sar_height */ - gst_nal_bs_read (bs, 16); - } - } - - /* overscan_info_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* overscan_appropriate_flag */ - gst_nal_bs_read (bs, 1); - } - - /* video_signal_type_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* video_format */ - gst_nal_bs_read (bs, 3); - /* video_full_range_flag */ - gst_nal_bs_read (bs, 1); - - /* colour_description_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* colour_primaries */ - gst_nal_bs_read (bs, 8); - /* transfer_characteristics */ - gst_nal_bs_read (bs, 8); - /* matrix_coefficients */ - gst_nal_bs_read (bs, 8); - } - } - - /* chroma_loc_info_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* chroma_sample_loc_type_top_field */ - gst_nal_bs_read_ue (bs); - /* chroma_sample_loc_type_bottom_field */ - gst_nal_bs_read_ue (bs); - } - - sps->timing_info_present_flag = gst_nal_bs_read (bs, 1); - if (sps->timing_info_present_flag) { - guint32 num_units_in_tick = gst_nal_bs_read (bs, 32); - guint32 time_scale = gst_nal_bs_read (bs, 32); - - /* If any of these parameters = 0, discard all timing_info */ - if (time_scale == 0) { - GST_WARNING_OBJECT (params->el, - "time_scale = 0 detected in stream (incompliant to H.264 E.2.1)." - " Discarding related info."); - } else if (num_units_in_tick == 0) { - GST_WARNING_OBJECT (params->el, - "num_units_in_tick = 0 detected in stream (incompliant to H.264 E.2.1)." - " Discarding related info."); - } else { - sps->num_units_in_tick = num_units_in_tick; - sps->time_scale = time_scale; - sps->fixed_frame_rate_flag = gst_nal_bs_read (bs, 1); - GST_LOG_OBJECT (params->el, "timing info: dur=%d/%d fixed=%d", - num_units_in_tick, time_scale, sps->fixed_frame_rate_flag); - } - } - - sps->nal_hrd_parameters_present_flag = gst_nal_bs_read (bs, 1); - if (sps->nal_hrd_parameters_present_flag) { - gst_h264_params_decode_sps_vui_hrd (params, sps, bs); - } - sps->vcl_hrd_parameters_present_flag = gst_nal_bs_read (bs, 1); - if (sps->vcl_hrd_parameters_present_flag) { - gst_h264_params_decode_sps_vui_hrd (params, sps, bs); - } - if (sps->nal_hrd_parameters_present_flag - || sps->vcl_hrd_parameters_present_flag) { - gst_nal_bs_read (bs, 1); /* low_delay_hrd_flag */ - } - - sps->pic_struct_present_flag = gst_nal_bs_read (bs, 1); - - /* derive framerate */ - /* FIXME verify / also handle other cases */ - if (sps->fixed_frame_rate_flag && sps->frame_mbs_only_flag && - !sps->pic_struct_present_flag) { - sps->fps_num = sps->time_scale; - sps->fps_den = sps->num_units_in_tick; - /* picture is a frame = 2 fields */ - sps->fps_den *= 2; - GST_LOG_OBJECT (params->el, "framerate %d/%d", sps->fps_num, sps->fps_den); - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_sps (GstH264Params * params, GstNalBs * bs) -{ - guint8 profile_idc, level_idc; - guint8 sps_id; - GstH264ParamsSPS *sps = NULL; - guint subwc[] = { 1, 2, 2, 1 }; - guint subhc[] = { 1, 2, 1, 1 }; - guint chroma; - guint fc_top, fc_bottom, fc_left, fc_right; - gint width, height; - - profile_idc = gst_nal_bs_read (bs, 8); - /* constraint_set0_flag */ - gst_nal_bs_read (bs, 1); - /* constraint_set1_flag */ - gst_nal_bs_read (bs, 1); - /* constraint_set1_flag */ - gst_nal_bs_read (bs, 1); - /* constraint_set1_flag */ - gst_nal_bs_read (bs, 1); - /* reserved */ - gst_nal_bs_read (bs, 4); - level_idc = gst_nal_bs_read (bs, 8); - - sps_id = gst_nal_bs_read_ue (bs); - sps = gst_h264_params_get_sps (params, sps_id, FALSE); - if (G_UNLIKELY (sps == NULL)) - return FALSE; - - gst_h264_params_store_nal (params, params->sps_nals, MAX_SPS_COUNT, sps_id, - bs); - - /* could be redefined mid stream, arrange for clear state */ - memset (sps, 0, sizeof (*sps)); - - GST_LOG_OBJECT (params->el, "sps id %d", sps_id); - sps->valid = TRUE; - /* validate and force activate this one if it is the first SPS we see */ - if (params->sps == NULL) - params->sps = sps; - - sps->profile_idc = profile_idc; - sps->level_idc = level_idc; - - if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 - || profile_idc == 244 || profile_idc == 44 || - profile_idc == 83 || profile_idc == 86) { - gint scp_flag = 0; - - /* chroma_format_idc */ - if ((chroma = gst_nal_bs_read_ue (bs)) == 3) { - /* separate_colour_plane_flag */ - sps->scp_flag = gst_nal_bs_read (bs, 1); - } - /* bit_depth_luma_minus8 */ - gst_nal_bs_read_ue (bs); - /* bit_depth_chroma_minus8 */ - gst_nal_bs_read_ue (bs); - /* qpprime_y_zero_transform_bypass_flag */ - gst_nal_bs_read (bs, 1); - /* seq_scaling_matrix_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - gint i, j, m, d; - - m = (chroma != 3) ? 8 : 12; - for (i = 0; i < m; i++) { - /* seq_scaling_list_present_flag[i] */ - d = gst_nal_bs_read (bs, 1); - if (d) { - gint lastScale = 8, nextScale = 8, deltaScale; - - j = (i < 6) ? 16 : 64; - for (; j > 0; j--) { - if (nextScale != 0) { - deltaScale = gst_nal_bs_read_se (bs); - nextScale = (lastScale + deltaScale + 256) % 256; - } - if (nextScale != 0) - lastScale = nextScale; - } - } - } - } - if (scp_flag) - chroma = 0; - } else { - /* inferred value */ - chroma = 1; - } - - /* between 0 and 12 */ - sps->log2_max_frame_num_minus4 = gst_nal_bs_read_ue (bs); - if (sps->log2_max_frame_num_minus4 > 12) { - GST_WARNING_OBJECT (params->el, - "log2_max_frame_num_minus4 = %d out of range" " [0,12]", - sps->log2_max_frame_num_minus4); - return FALSE; - } - - sps->pic_order_cnt_type = gst_nal_bs_read_ue (bs); - if (sps->pic_order_cnt_type == 0) { - sps->log2_max_pic_order_cnt_lsb_minus4 = gst_nal_bs_read_ue (bs); - } else if (sps->pic_order_cnt_type == 1) { - gint d; - - /* delta_pic_order_always_zero_flag */ - gst_nal_bs_read (bs, 1); - /* offset_for_non_ref_pic */ - gst_nal_bs_read_ue (bs); - /* offset_for_top_to_bottom_field */ - gst_nal_bs_read_ue (bs); - /* num_ref_frames_in_pic_order_cnt_cycle */ - d = gst_nal_bs_read_ue (bs); - for (; d > 0; d--) { - /* offset_for_ref_frame[i] */ - gst_nal_bs_read_ue (bs); - } - } - - /* max_num_ref_frames */ - gst_nal_bs_read_ue (bs); - /* gaps_in_frame_num_value_allowed_flag */ - gst_nal_bs_read (bs, 1); - /* pic_width_in_mbs_minus1 */ - width = gst_nal_bs_read_ue (bs); - /* pic_height_in_map_units_minus1 */ - height = gst_nal_bs_read_ue (bs); - - sps->frame_mbs_only_flag = gst_nal_bs_read (bs, 1); - if (!sps->frame_mbs_only_flag) { - /* mb_adaptive_frame_field_flag */ - gst_nal_bs_read (bs, 1); - } - - width++; - width *= 16; - height++; - height *= 16 * (2 - sps->frame_mbs_only_flag); - - /* direct_8x8_inference_flag */ - gst_nal_bs_read (bs, 1); - /* frame_cropping_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* frame_crop_left_offset */ - fc_left = gst_nal_bs_read_ue (bs); - /* frame_crop_right_offset */ - fc_right = gst_nal_bs_read_ue (bs); - /* frame_crop_top_offset */ - fc_top = gst_nal_bs_read_ue (bs); - /* frame_crop_bottom_offset */ - fc_bottom = gst_nal_bs_read_ue (bs); - } else { - fc_left = fc_right = fc_top = fc_bottom = 0; - } - - GST_LOG_OBJECT (params->el, "decoding SPS: profile_idc = %d, " - "level_idc = %d, sps_id = %d, pic_order_cnt_type = %d, " - "frame_mbs_only_flag = %d", - sps->profile_idc, sps->level_idc, sps_id, sps->pic_order_cnt_type, - sps->frame_mbs_only_flag); - - /* calculate width and height */ - GST_LOG_OBJECT (params->el, "initial width=%d, height=%d", width, height); - GST_LOG_OBJECT (params->el, "crop (%d,%d)(%d,%d)", - fc_left, fc_top, fc_right, fc_bottom); - if (chroma > 3) { - GST_LOG_OBJECT (params->el, "chroma=%d in SPS is out of range", chroma); - return FALSE; - } - width -= (fc_left + fc_right) * subwc[chroma]; - height -= - (fc_top + fc_bottom) * subhc[chroma] * (2 - sps->frame_mbs_only_flag); - if (width < 0 || height < 0) { - GST_WARNING_OBJECT (params->el, "invalid width/height in SPS"); - return FALSE; - } - GST_LOG_OBJECT (params->el, "final width=%u, height=%u", width, height); - sps->width = width; - sps->height = height; - - sps->vui_parameters_present_flag = gst_nal_bs_read (bs, 1); - if (sps->vui_parameters_present_flag) { - /* discard parsing problem */ - gst_h264_params_decode_sps_vui (params, sps, bs); - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_pps (GstH264Params * params, GstNalBs * bs) -{ - gint pps_id; - GstH264ParamsPPS *pps = NULL; - - pps_id = gst_nal_bs_read_ue (bs); - if (G_UNLIKELY (pps_id >= MAX_PPS_COUNT)) { - GST_WARNING_OBJECT (params->el, - "requested pps_id=%04x out of range", pps_id); - return FALSE; - } - - - pps = gst_h264_params_get_pps (params, pps_id, FALSE); - if (G_UNLIKELY (pps == NULL)) - return FALSE; - - /* validate and set */ - pps->valid = TRUE; - params->pps = pps; - - gst_h264_params_store_nal (params, params->pps_nals, MAX_PPS_COUNT, pps_id, - bs); - - pps->sps_id = gst_nal_bs_read_ue (bs); - GST_LOG_OBJECT (params->el, "pps %d referencing sps %d", pps_id, pps->sps_id); - - /* activate referenced sps */ - if (!gst_h264_params_get_sps (params, pps->sps_id, TRUE)) - return FALSE; - - /* not parsing the rest for the time being */ - return TRUE; -} - -static gboolean -gst_h264_params_decode_sei_buffering_period (GstH264Params * params, - GstNalBs * bs) -{ -#ifdef EXTRA_PARSE - guint8 sps_id; - gint sched_sel_idx; - GstH264ParamsSPS *sps; - - sps_id = gst_nal_bs_read_ue (bs); - sps = gst_h264_params_get_sps (params, sps_id, TRUE); - if (G_UNLIKELY (sps == NULL)) - return FALSE; - - if (sps->nal_hrd_parameters_present_flag) { - for (sched_sel_idx = 0; sched_sel_idx <= sps->cpb_cnt_minus1; - sched_sel_idx++) { - params->initial_cpb_removal_delay[sched_sel_idx] - = gst_nal_bs_read (bs, - sps->initial_cpb_removal_delay_length_minus1 + 1); - /* initial_cpb_removal_delay_offset */ - gst_nal_bs_read (bs, sps->initial_cpb_removal_delay_length_minus1 + 1); - } - } - - if (sps->vcl_hrd_parameters_present_flag) { - for (sched_sel_idx = 0; sched_sel_idx <= sps->cpb_cnt_minus1; - sched_sel_idx++) { - params->initial_cpb_removal_delay[sched_sel_idx] - = gst_nal_bs_read (bs, - sps->initial_cpb_removal_delay_length_minus1 + 1); - /* initial_cpb_removal_delay_offset */ - gst_nal_bs_read (bs, sps->initial_cpb_removal_delay_length_minus1 + 1); - } - } -#endif - - if (params->ts_trn_nb == GST_CLOCK_TIME_NONE || - params->dts == GST_CLOCK_TIME_NONE) - params->ts_trn_nb = 0; - else - params->ts_trn_nb = params->dts; - - GST_LOG_OBJECT (params->el, - "new buffering period; ts_trn_nb updated: %" GST_TIME_FORMAT, - GST_TIME_ARGS (params->ts_trn_nb)); - - return 0; -} - -static gboolean -gst_h264_params_decode_sei_picture_timing (GstH264Params * params, - GstNalBs * bs) -{ - GstH264ParamsSPS *sps = params->sps; - - if (sps == NULL) { - GST_WARNING_OBJECT (params->el, - "SPS == NULL; delayed decoding of picture timing info not implemented "); - return FALSE; - } - - if (sps->nal_hrd_parameters_present_flag - || sps->vcl_hrd_parameters_present_flag) { - params->sei_cpb_removal_delay = - gst_nal_bs_read (bs, sps->cpb_removal_delay_length_minus1 + 1); - /* sei_dpb_output_delay */ - gst_nal_bs_read (bs, sps->dpb_output_delay_length_minus1 + 1); - } - - if (sps->pic_struct_present_flag) { -#ifdef EXTRA_PARSE - /* pic_struct to NumClockTS lookup table */ - static const guint8 sei_num_clock_ts_table[9] = - { 1, 1, 1, 2, 2, 3, 3, 2, 3 }; - guint i, num_clock_ts; - guint sei_ct_type = 0; -#endif - - params->sei_pic_struct = gst_nal_bs_read (bs, 4); - GST_LOG_OBJECT (params, "pic_struct:%d", params->sei_pic_struct); - if (params->sei_pic_struct > SEI_PIC_STRUCT_FRAME_TRIPLING) - return FALSE; - -#ifdef EXTRA_PARSE - num_clock_ts = sei_num_clock_ts_table[params->sei_pic_struct]; - - for (i = 0; i < num_clock_ts; i++) { - /* clock_timestamp_flag */ - if (gst_nal_bs_read (bs, 1)) { - guint full_timestamp_flag; - - sei_ct_type |= 1 << gst_nal_bs_read (bs, 2); - /* nuit_field_based_flag */ - gst_nal_bs_read (bs, 1); - /* counting_type */ - gst_nal_bs_read (bs, 5); - full_timestamp_flag = gst_nal_bs_read (bs, 1); - /* discontinuity_flag */ - gst_nal_bs_read (bs, 1); - /* cnt_dropped_flag */ - gst_nal_bs_read (bs, 1); - /* n_frames */ - gst_nal_bs_read (bs, 8); - if (full_timestamp_flag) { - /* seconds_value 0..59 */ - gst_nal_bs_read (bs, 6); - /* minutes_value 0..59 */ - gst_nal_bs_read (bs, 6); - /* hours_value 0..23 */ - gst_nal_bs_read (bs, 5); - } else { - /* seconds_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* seconds_value range 0..59 */ - gst_nal_bs_read (bs, 6); - /* minutes_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* minutes_value 0..59 */ - gst_nal_bs_read (bs, 6); - /* hours_flag */ - if (gst_nal_bs_read (bs, 1)) - /* hours_value 0..23 */ - gst_nal_bs_read (bs, 5); - } - } - } - if (sps->time_offset_length_minus1 >= 0) { - /* time_offset */ - gst_nal_bs_read (bs, sps->time_offset_length_minus1 + 1); - } - } - } - - GST_LOG_OBJECT (params, "ct_type:%X", sei_ct_type); -#endif - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_sei (GstH264Params * params, GstNalBs * bs) -{ - guint8 tmp; - GstH264ParamsSEIPayloadType payloadType = 0; - gint8 payloadSize = 0; - - do { - tmp = gst_nal_bs_read (bs, 8); - payloadType += tmp; - } while (tmp == 255); - do { - tmp = gst_nal_bs_read (bs, 8); - payloadSize += tmp; - } while (tmp == 255); - - GST_LOG_OBJECT (params->el, - "SEI message received: payloadType = %d, payloadSize = %d bytes", - payloadType, payloadSize); - - switch (payloadType) { - case SEI_BUF_PERIOD: - if (!gst_h264_params_decode_sei_buffering_period (params, bs)) - return FALSE; - break; - case SEI_PIC_TIMING: - /* TODO: According to H264 D2.2 Note1, it might be the case that the - * picture timing SEI message is encountered before the corresponding SPS - * is specified. Need to hold down the message and decode it later. */ - if (!gst_h264_params_decode_sei_picture_timing (params, bs)) - return FALSE; - break; - default: - GST_LOG_OBJECT (params->el, - "SEI message of payloadType = %d is received but not parsed", - payloadType); - break; - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_slice_header (GstH264Params * params, GstNalBs * bs) -{ - GstH264ParamsSPS *sps; - GstH264ParamsPPS *pps; - guint8 pps_id; - - params->first_mb_in_slice = gst_nal_bs_read_ue (bs); - params->slice_type = gst_nal_bs_read_ue (bs); - - pps_id = gst_nal_bs_read_ue (bs); - GST_LOG_OBJECT (params->el, "slice header references pps id %d", pps_id); - pps = gst_h264_params_get_pps (params, pps_id, TRUE); - if (G_UNLIKELY (pps == NULL)) - return FALSE; - sps = gst_h264_params_get_sps (params, pps->sps_id, TRUE); - if (G_UNLIKELY (sps == NULL)) - return FALSE; - - if (sps->scp_flag) { - /* colour_plane_id */ - gst_nal_bs_read (bs, 2); - } - - /* frame num */ - gst_nal_bs_read (bs, sps->log2_max_pic_order_cnt_lsb_minus4 + 4); - - if (!sps->frame_mbs_only_flag) { - params->field_pic_flag = gst_nal_bs_read (bs, 1); - if (params->field_pic_flag) - params->bottom_field_flag = gst_nal_bs_read (bs, 1); - } - - /* not parsing the rest for the time being */ - return TRUE; -} - -/* only payload in @data */ -gboolean -gst_h264_params_parse_nal (GstH264Params * params, guint8 * data, gint size) -{ - GstH264ParamsNalUnitType nal_type; - GstNalBs bs; - gint nal_ref_idc; - gboolean res = TRUE; - - g_return_val_if_fail (params != NULL, FALSE); - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (size != 0, FALSE); - - nal_type = (data[0] & 0x1f); - nal_ref_idc = (data[0] & 0x60) >> 5; - - GST_LOG_OBJECT (params->el, "NAL type: %d, ref_idc: %d", nal_type, - nal_ref_idc); - - gst_nal_bs_init (&bs, data + 1, size - 1); - /* optimality HACK */ - bs.orig_data = data; - - /* first parse some things needed to get to the frame type */ - switch (nal_type) { - case NAL_SLICE: - case NAL_SLICE_DPA: - case NAL_SLICE_DPB: - case NAL_SLICE_DPC: - case NAL_SLICE_IDR: - { - gint first_mb_in_slice, slice_type; - - gst_h264_params_decode_slice_header (params, &bs); - first_mb_in_slice = params->first_mb_in_slice; - slice_type = params->slice_type; - - GST_LOG_OBJECT (params->el, "first MB: %d, slice type: %d", - first_mb_in_slice, slice_type); - - switch (slice_type) { - case 0: - case 5: - case 3: - case 8: /* SP */ - /* P frames */ - GST_LOG_OBJECT (params->el, "we have a P slice"); - break; - case 1: - case 6: - /* B frames */ - GST_LOG_OBJECT (params->el, "we have a B slice"); - break; - case 2: - case 7: - case 4: - case 9: - /* I frames */ - GST_LOG_OBJECT (params->el, "we have an I slice"); - break; - } - break; - } - case NAL_SEI: - GST_LOG_OBJECT (params->el, "SEI NAL"); - res = gst_h264_params_decode_sei (params, &bs); - break; - case NAL_SPS: - GST_LOG_OBJECT (params->el, "SPS NAL"); - res = gst_h264_params_decode_sps (params, &bs); - break; - case NAL_PPS: - GST_LOG_OBJECT (params->el, "PPS NAL"); - res = gst_h264_params_decode_pps (params, &bs); - break; - case NAL_AU_DELIMITER: - GST_LOG_OBJECT (params->el, "AU delimiter NAL"); - break; - default: - GST_LOG_OBJECT (params->el, "unparsed NAL"); - break; - } - - return res; -} - -void -gst_h264_params_get_timestamp (GstH264Params * params, - GstClockTime * out_ts, GstClockTime * out_dur, gboolean frame) -{ - GstH264ParamsSPS *sps = params->sps; - GstClockTime upstream; - gint duration = 1; - - g_return_if_fail (out_dur != NULL); - g_return_if_fail (out_ts != NULL); - - upstream = *out_ts; - - if (!frame) { - GST_LOG_OBJECT (params->el, "no frame data -> 0 duration"); - *out_dur = 0; - goto exit; - } else { - *out_ts = upstream; - } - - if (!sps) { - GST_DEBUG_OBJECT (params->el, "referred SPS invalid"); - goto exit; - } else if (!sps->timing_info_present_flag) { - GST_DEBUG_OBJECT (params->el, - "unable to compute timestamp: timing info not present"); - goto exit; - } else if (sps->time_scale == 0) { - GST_DEBUG_OBJECT (params->el, - "unable to compute timestamp: time_scale = 0 " - "(this is forbidden in spec; bitstream probably contains error)"); - goto exit; - } - - if (sps->pic_struct_present_flag && params->sei_pic_struct != (guint8) - 1) { - /* Note that when h264parse->sei_pic_struct == -1 (unspecified), there - * are ways to infer its value. This is related to computing the - * TopFieldOrderCnt and BottomFieldOrderCnt, which looks - * complicated and thus not implemented for the time being. Yet - * the value we have here is correct for many applications - */ - switch (params->sei_pic_struct) { - case SEI_PIC_STRUCT_TOP_FIELD: - case SEI_PIC_STRUCT_BOTTOM_FIELD: - duration = 1; - break; - case SEI_PIC_STRUCT_FRAME: - case SEI_PIC_STRUCT_TOP_BOTTOM: - case SEI_PIC_STRUCT_BOTTOM_TOP: - duration = 2; - break; - case SEI_PIC_STRUCT_TOP_BOTTOM_TOP: - case SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: - duration = 3; - break; - case SEI_PIC_STRUCT_FRAME_DOUBLING: - duration = 4; - break; - case SEI_PIC_STRUCT_FRAME_TRIPLING: - duration = 6; - break; - default: - GST_DEBUG_OBJECT (params, - "h264parse->sei_pic_struct of unknown value %d. Not parsed", - params->sei_pic_struct); - break; - } - } else { - duration = params->field_pic_flag ? 1 : 2; - } - - GST_LOG_OBJECT (params->el, "frame tick duration %d", duration); - - /* - * h264parse.264 C.1.2 Timing of coded picture removal (equivalent to DTS): - * Tr,n(0) = initial_cpb_removal_delay[ SchedSelIdx ] / 90000 - * Tr,n(n) = Tr,n(nb) + Tc * cpb_removal_delay(n) - * where - * Tc = num_units_in_tick / time_scale - */ - - if (params->ts_trn_nb != GST_CLOCK_TIME_NONE) { - GST_LOG_OBJECT (params->el, "buffering based ts"); - /* buffering period is present */ - if (upstream != GST_CLOCK_TIME_NONE) { - /* If upstream timestamp is valid, we respect it and adjust current - * reference point */ - params->ts_trn_nb = upstream - - (GstClockTime) gst_util_uint64_scale_int - (params->sei_cpb_removal_delay * GST_SECOND, - sps->num_units_in_tick, sps->time_scale); - } else { - /* If no upstream timestamp is given, we write in new timestamp */ - upstream = params->dts = params->ts_trn_nb + - (GstClockTime) gst_util_uint64_scale_int - (params->sei_cpb_removal_delay * GST_SECOND, - sps->num_units_in_tick, sps->time_scale); - } - } else { - GstClockTime dur; - - GST_LOG_OBJECT (params->el, "duration based ts"); - /* naive method: no removal delay specified - * track upstream timestamp and provide best guess frame duration */ - dur = gst_util_uint64_scale_int (duration * GST_SECOND, - sps->num_units_in_tick, sps->time_scale); - /* sanity check */ - if (dur < GST_MSECOND) { - GST_DEBUG_OBJECT (params->el, "discarding dur %" GST_TIME_FORMAT, - GST_TIME_ARGS (dur)); - } else { - *out_dur = dur; - } - } - -exit: - if (GST_CLOCK_TIME_IS_VALID (upstream)) - *out_ts = params->dts = upstream; - - if (GST_CLOCK_TIME_IS_VALID (*out_dur) && - GST_CLOCK_TIME_IS_VALID (params->dts)) - params->dts += *out_dur; -} - -void -gst_h264_params_create (GstH264Params ** _params, GstElement * element) -{ - GstH264Params *params; - - g_return_if_fail (_params != NULL); - - params = g_new0 (GstH264Params, 1); - params->el = element; - - params->dts = GST_CLOCK_TIME_NONE; - params->ts_trn_nb = GST_CLOCK_TIME_NONE; - - *_params = params; -} - -void -gst_h264_params_free (GstH264Params * params) -{ - gint i; - - g_return_if_fail (params != NULL); - - for (i = 0; i < MAX_SPS_COUNT; i++) - gst_buffer_replace (¶ms->sps_nals[i], NULL); - for (i = 0; i < MAX_PPS_COUNT; i++) - gst_buffer_replace (¶ms->pps_nals[i], NULL); - - g_free (params); -} diff --git a/gst/videoparsers/h264parse.h b/gst/videoparsers/h264parse.h deleted file mode 100644 index 517c8542b..000000000 --- a/gst/videoparsers/h264parse.h +++ /dev/null @@ -1,186 +0,0 @@ -/* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> - * Copyright (C) <2010> Collabora Multimedia - * Copyright (C) <2010> Nokia Corporation - * - * Some bits C-c,C-v'ed and s/4/3 from h264parse: - * (C) 2005 Michal Benes <michal.benes@itonis.tv> - * (C) 2008 Wim Taymans <wim.taymans@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GST_H264_PARAMS_H__ -#define __GST_H264_PARAMS_H__ - -#include <gst/gst.h> - -G_BEGIN_DECLS - -typedef enum -{ - NAL_UNKNOWN = 0, - NAL_SLICE = 1, - NAL_SLICE_DPA = 2, - NAL_SLICE_DPB = 3, - NAL_SLICE_DPC = 4, - NAL_SLICE_IDR = 5, - NAL_SEI = 6, - NAL_SPS = 7, - NAL_PPS = 8, - NAL_AU_DELIMITER = 9, - NAL_SEQ_END = 10, - NAL_STREAM_END = 11, - NAL_FILTER_DATA = 12 -} GstH264ParamsNalUnitType; - -/* SEI type */ -typedef enum -{ - SEI_BUF_PERIOD = 0, - SEI_PIC_TIMING = 1 - /* and more... */ -} GstH264ParamsSEIPayloadType; - -/* SEI pic_struct type */ -typedef enum -{ - SEI_PIC_STRUCT_FRAME = 0, - SEI_PIC_STRUCT_TOP_FIELD = 1, - SEI_PIC_STRUCT_BOTTOM_FIELD = 2, - SEI_PIC_STRUCT_TOP_BOTTOM = 3, - SEI_PIC_STRUCT_BOTTOM_TOP = 4, - SEI_PIC_STRUCT_TOP_BOTTOM_TOP = 5, - SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM = 6, - SEI_PIC_STRUCT_FRAME_DOUBLING = 7, - SEI_PIC_STRUCT_FRAME_TRIPLING = 8 -} GstH264ParamsSEIPicStructType; - -typedef struct _GstH264Params GstH264Params; -typedef struct _GstH264ParamsSPS GstH264ParamsSPS; -typedef struct _GstH264ParamsPPS GstH264ParamsPPS; - -#define MAX_SPS_COUNT 32 -#define MAX_PPS_COUNT 256 - -/* SPS: sequential parameter sets */ -struct _GstH264ParamsSPS -{ - gboolean valid; - - /* raw values */ - guint8 profile_idc; - guint8 level_idc; - - guint8 sps_id; - - guint8 pic_order_cnt_type; - - guint8 log2_max_frame_num_minus4; - gboolean frame_mbs_only_flag; - guint8 log2_max_pic_order_cnt_lsb_minus4; - - gboolean frame_cropping_flag; - gboolean scp_flag; - - /* VUI parameters */ - gboolean vui_parameters_present_flag; - - gboolean timing_info_present_flag; - guint32 num_units_in_tick; - guint32 time_scale; - gboolean fixed_frame_rate_flag; - - gboolean nal_hrd_parameters_present_flag; - gboolean vcl_hrd_parameters_present_flag; - - /* hrd parameters */ - guint8 cpb_cnt_minus1; - gint initial_cpb_removal_delay_length_minus1; - gint cpb_removal_delay_length_minus1; - gint dpb_output_delay_length_minus1; - gboolean time_offset_length_minus1; - - gboolean pic_struct_present_flag; - - /* ... and probably more ... */ - - /* derived values */ - gint width, height; - gint fps_num, fps_den; -}; - -/* PPS: pic parameter sets */ -struct _GstH264ParamsPPS -{ - gboolean valid; - - /* raw values */ - guint8 pps_id; - guint8 sps_id; -}; - -struct _GstH264Params -{ - /* debug purposes */ - GstElement *el; - - /* SPS: sequential parameter set */ - GstH264ParamsSPS sps_buffers[MAX_SPS_COUNT]; - /* current SPS; most recent one in stream or referenced by PPS */ - GstH264ParamsSPS *sps; - /* PPS: sequential parameter set */ - GstH264ParamsPPS pps_buffers[MAX_PPS_COUNT]; - /* current PPS; most recent one in stream */ - GstH264ParamsPPS *pps; - - /* extracted from slice header or otherwise relevant nal */ - guint8 first_mb_in_slice; - guint8 slice_type; - gboolean field_pic_flag; - gboolean bottom_field_flag; - - /* SEI: supplemental enhancement messages */ -#ifdef EXTRA_PARSE - /* buffering period */ - guint32 initial_cpb_removal_delay[32]; -#endif - /* picture timing */ - guint32 sei_cpb_removal_delay; - guint8 sei_pic_struct; - /* And more... */ - - /* cached timestamps */ - /* (trying to) track upstream dts and interpolate */ - GstClockTime dts; - /* dts at start of last buffering period */ - GstClockTime ts_trn_nb; - - /* collected SPS and PPS NALUs */ - GstBuffer *sps_nals[MAX_SPS_COUNT]; - GstBuffer *pps_nals[MAX_PPS_COUNT]; -}; - -gboolean gst_h264_params_parse_nal (GstH264Params * params, guint8 * nal, gint size); -void gst_h264_params_get_timestamp (GstH264Params * params, - GstClockTime * out_ts, GstClockTime * out_dur, - gboolean frame); -void gst_h264_params_create (GstH264Params ** _params, GstElement * element); -void gst_h264_params_free (GstH264Params * params); - - -G_END_DECLS -#endif |