static GstFlowReturn mpegts_base_scan (MpegTSBase * base) { GstFlowReturn ret = GST_FLOW_OK; GstBuffer *buf = NULL; guint i; gboolean done = FALSE; MpegTSPacketizerPacketReturn pret; gint64 tmpval; gint64 upstream_size, seek_pos, reverse_limit; GstFormat format; guint initial_pcr_seen; GST_DEBUG ("Scanning for initial sync point"); /* Find initial sync point and at least 5 PCR values */ for (i = 0; i < 20 && !done; i++) { GST_DEBUG ("Grabbing %d => %d", i * 65536, (i + 1) * 65536); ret = gst_pad_pull_range (base->sinkpad, i * 65536, 65536, &buf); if (G_UNLIKELY (ret == GST_FLOW_EOS)) break; if (G_UNLIKELY (ret != GST_FLOW_OK)) goto beach; /* Push to packetizer */ mpegts_packetizer_push (base->packetizer, buf); buf = NULL; if (mpegts_packetizer_has_packets (base->packetizer)) { if (base->seek_offset == -1) { /* Mark the initial sync point and remember the packetsize */ base->seek_offset = base->packetizer->offset; GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset); base->packetsize = base->packetizer->packet_size; } while (1) { /* Eat up all packets */ pret = mpegts_packetizer_process_next_packet (base->packetizer); if (pret == PACKET_NEED_MORE) break; if (pret != PACKET_BAD && base->packetizer->nb_seen_offsets >= 5) { GST_DEBUG ("Got enough initial PCR"); done = TRUE; break; } } } } initial_pcr_seen = base->packetizer->nb_seen_offsets; if (G_UNLIKELY (initial_pcr_seen == 0)) goto no_initial_pcr; GST_DEBUG ("Seen %d initial PCR", initial_pcr_seen); /* Now send data from the end */ /* Get the size of upstream */ format = GST_FORMAT_BYTES; if (!gst_pad_peer_query_duration (base->sinkpad, format, &tmpval)) goto beach; upstream_size = tmpval; /* The scanning takes place on the last 2048kB. Considering PCR should * be present at least every 100ms, this should cope with streams * up to 160Mbit/s */ reverse_limit = MAX (0, upstream_size - 2097152); /* Find last PCR value, searching backwards by chunks of 300 MPEG-ts packets */ for (seek_pos = MAX (0, upstream_size - 56400); seek_pos >= reverse_limit; seek_pos -= 56400) { mpegts_packetizer_clear (base->packetizer); GST_DEBUG ("Grabbing %" G_GUINT64_FORMAT " => %" G_GUINT64_FORMAT, seek_pos, seek_pos + 56400); ret = gst_pad_pull_range (base->sinkpad, seek_pos, 56400, &buf); if (G_UNLIKELY (ret == GST_FLOW_EOS)) break; if (G_UNLIKELY (ret != GST_FLOW_OK)) goto beach; /* Push to packetizer */ mpegts_packetizer_push (base->packetizer, buf); buf = NULL; if (mpegts_packetizer_has_packets (base->packetizer)) { pret = PACKET_OK; /* Eat up all packets, really try to get last PCR(s) */ while (pret != PACKET_NEED_MORE) pret = mpegts_packetizer_process_next_packet (base->packetizer); if (base->packetizer->nb_seen_offsets > initial_pcr_seen) { GST_DEBUG ("Got last PCR(s) (total seen:%d)", base->packetizer->nb_seen_offsets); break; } } } beach: mpegts_packetizer_clear (base->packetizer); return ret; no_initial_pcr: mpegts_packetizer_clear (base->packetizer); GST_WARNING_OBJECT (base, "Couldn't find any PCR within the first %d bytes", 10 * 65536); return GST_FLOW_ERROR; }
static GstFlowReturn mpegts_base_scan (MpegTSBase * base) { GstFlowReturn ret = GST_FLOW_OK; GstBuffer *buf = NULL; guint i; gboolean done = FALSE; MpegTSPacketizerPacketReturn pret; gint64 tmpval; guint64 upstream_size, seek_pos; GstFormat format; guint initial_pcr_seen; GST_DEBUG ("Scanning for initial sync point"); /* Find initial sync point and at least 5 PCR values */ for (i = 0; i < 10 && !done; i++) { GST_DEBUG ("Grabbing %d => %d", i * 65536, 65536); ret = gst_pad_pull_range (base->sinkpad, i * 65536, 65536, &buf); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto beach; /* Push to packetizer */ mpegts_packetizer_push (base->packetizer, buf); buf = NULL; if (mpegts_packetizer_has_packets (base->packetizer)) { if (base->seek_offset == -1) { /* Mark the initial sync point and remember the packetsize */ base->seek_offset = base->packetizer->offset; GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset); base->packetsize = base->packetizer->packet_size; } while (1) { /* Eat up all packets */ pret = mpegts_packetizer_process_next_packet (base->packetizer); if (pret == PACKET_NEED_MORE) break; if (pret != PACKET_BAD && mpegts_packetizer_get_seen_pcr (base->packetizer) >= 5) { GST_DEBUG ("Got enough initial PCR"); done = TRUE; break; } } } } initial_pcr_seen = mpegts_packetizer_get_seen_pcr (base->packetizer); if (G_UNLIKELY (initial_pcr_seen == 0)) goto no_initial_pcr; GST_DEBUG ("Seen %d initial PCR", initial_pcr_seen); /* Now send data from the end */ mpegts_packetizer_clear (base->packetizer); /* Get the size of upstream */ format = GST_FORMAT_BYTES; if (!gst_pad_peer_query_duration (base->sinkpad, format, &tmpval)) goto beach; upstream_size = tmpval; done = FALSE; /* Find last PCR value */ for (seek_pos = MAX (0, upstream_size - 655360); seek_pos < upstream_size && !done; seek_pos += 65536) { GST_DEBUG ("Grabbing %" G_GUINT64_FORMAT " => %d", seek_pos, 65536); ret = gst_pad_pull_range (base->sinkpad, seek_pos, 65536, &buf); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto beach; /* Push to packetizer */ mpegts_packetizer_push (base->packetizer, buf); buf = NULL; if (mpegts_packetizer_has_packets (base->packetizer)) { while (1) { /* Eat up all packets */ pret = mpegts_packetizer_process_next_packet (base->packetizer); if (pret == PACKET_NEED_MORE) break; if (pret != PACKET_BAD && mpegts_packetizer_get_seen_pcr (base->packetizer) > initial_pcr_seen) { GST_DEBUG ("Got last PCR"); done = TRUE; break; } } } } beach: mpegts_packetizer_clear (base->packetizer); return ret; no_initial_pcr: mpegts_packetizer_clear (base->packetizer); GST_WARNING_OBJECT (base, "Couldn't find any PCR within the first %d bytes", 10 * 65536); return GST_FLOW_ERROR; }
static GstFlowReturn mpegts_base_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) { GstFlowReturn res = GST_FLOW_OK; MpegTSBase *base; MpegTSPacketizerPacketReturn pret; MpegTSPacketizer2 *packetizer; MpegTSPacketizerPacket packet; MpegTSBaseClass *klass; base = GST_MPEGTS_BASE (parent); klass = GST_MPEGTS_BASE_GET_CLASS (base); packetizer = base->packetizer; if (klass->input_done) gst_buffer_ref (buf); if (GST_BUFFER_IS_DISCONT (buf)) { GST_DEBUG_OBJECT (base, "Got DISCONT buffer, flushing"); res = mpegts_base_drain (base); if (G_UNLIKELY (res != GST_FLOW_OK)) return res; mpegts_base_flush (base, FALSE); /* In the case of discontinuities in push-mode with TIME segment * we want to drop all previous observations (hard:TRUE) from * the packetizer */ if (base->mode == BASE_MODE_PUSHING && base->segment.format == GST_FORMAT_TIME) mpegts_packetizer_flush (base->packetizer, TRUE); else mpegts_packetizer_flush (base->packetizer, FALSE); } mpegts_packetizer_push (base->packetizer, buf); while (res == GST_FLOW_OK) { pret = mpegts_packetizer_next_packet (base->packetizer, &packet); /* If we don't have enough data, return */ if (G_UNLIKELY (pret == PACKET_NEED_MORE)) break; if (G_UNLIKELY (pret == PACKET_BAD)) { /* bad header, skip the packet */ GST_DEBUG_OBJECT (base, "bad packet, skipping"); goto next; } if (klass->inspect_packet) klass->inspect_packet (base, &packet); /* If it's a known PES, push it */ if (MPEGTS_BIT_IS_SET (base->is_pes, packet.pid)) { /* push the packet downstream */ if (base->push_data) res = klass->push (base, &packet, NULL); } else if (packet.payload && MPEGTS_BIT_IS_SET (base->known_psi, packet.pid)) { /* base PSI data */ GList *others, *tmp; GstMpegtsSection *section; section = mpegts_packetizer_push_section (packetizer, &packet, &others); if (section) mpegts_base_handle_psi (base, section); if (G_UNLIKELY (others)) { for (tmp = others; tmp; tmp = tmp->next) mpegts_base_handle_psi (base, (GstMpegtsSection *) tmp->data); g_list_free (others); } /* we need to push section packet downstream */ if (base->push_section) res = klass->push (base, &packet, section); } else if (packet.payload && packet.pid != 0x1fff) GST_LOG ("PID 0x%04x Saw packet on a pid we don't handle", packet.pid); next: mpegts_packetizer_clear_packet (base->packetizer, &packet); } if (klass->input_done) { if (res == GST_FLOW_OK) res = klass->input_done (base, buf); else gst_buffer_unref (buf); } return res; }
static GstFlowReturn mpegts_base_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) { GstFlowReturn res = GST_FLOW_OK; MpegTSBase *base; MpegTSPacketizerPacketReturn pret; MpegTSPacketizer2 *packetizer; MpegTSPacketizerPacket packet; MpegTSBaseClass *klass; base = GST_MPEGTS_BASE (parent); klass = GST_MPEGTS_BASE_GET_CLASS (base); packetizer = base->packetizer; if (G_UNLIKELY (base->queried_latency == FALSE)) { query_upstream_latency (base); } if (klass->input_done) gst_buffer_ref (buf); mpegts_packetizer_push (base->packetizer, buf); while (res == GST_FLOW_OK) { pret = mpegts_packetizer_next_packet (base->packetizer, &packet); /* If we don't have enough data, return */ if (G_UNLIKELY (pret == PACKET_NEED_MORE)) break; if (G_UNLIKELY (pret == PACKET_BAD)) { /* bad header, skip the packet */ GST_DEBUG_OBJECT (base, "bad packet, skipping"); goto next; } /* If it's a known PES, push it */ if (MPEGTS_BIT_IS_SET (base->is_pes, packet.pid)) { /* push the packet downstream */ if (base->push_data) res = klass->push (base, &packet, NULL); } else if (packet.payload && MPEGTS_BIT_IS_SET (base->known_psi, packet.pid)) { /* base PSI data */ GList *others, *tmp; GstMpegTsSection *section; section = mpegts_packetizer_push_section (packetizer, &packet, &others); if (section) mpegts_base_handle_psi (base, section); if (G_UNLIKELY (others)) { for (tmp = others; tmp; tmp = tmp->next) mpegts_base_handle_psi (base, (GstMpegTsSection *) tmp->data); g_list_free (others); } /* we need to push section packet downstream */ if (base->push_section) res = klass->push (base, &packet, section); } else if (packet.payload && packet.pid != 0x1fff) GST_LOG ("PID 0x%04x Saw packet on a pid we don't handle", packet.pid); next: mpegts_packetizer_clear_packet (base->packetizer, &packet); } if (klass->input_done) { if (res == GST_FLOW_OK) res = klass->input_done (base, buf); else gst_buffer_unref (buf); } return res; }