diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c index 26da8a608e..7a91c910c3 100644 --- a/ext/ogg/gstoggmux.c +++ b/ext/ogg/gstoggmux.c @@ -62,6 +62,11 @@ GST_DEBUG_CATEGORY_STATIC (gst_ogg_mux_debug); ? GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf) \ : GST_BUFFER_TIMESTAMP (buf)) +#define GST_BUFFER_RUNNING_TIME(buf, oggpad) \ + (GST_BUFFER_DURATION_IS_VALID (buf) \ + ? gst_segment_to_running_time (&(oggpad)->segment, GST_FORMAT_TIME, \ + GST_BUFFER_TIMESTAMP (buf)) : 0) + #define GST_GP_FORMAT "[gp %8" G_GINT64_FORMAT "]" #define GST_GP_CAST(_gp) ((gint64) _gp) @@ -319,11 +324,30 @@ gst_ogg_mux_sink_event (GstPad * pad, GstEvent * event) GST_DEBUG_PAD_NAME (pad)); switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT: - /* We don't support NEWSEGMENT events */ - gst_event_unref (event); - ret = FALSE; + case GST_EVENT_NEWSEGMENT:{ + gboolean update; + gdouble rate; + gdouble applied_rate; + GstFormat format; + gint64 start, stop, position; + + gst_event_parse_new_segment_full (event, &update, &rate, + &applied_rate, &format, &start, &stop, &position); + + /* We don't support non time NEWSEGMENT events */ + if (format != GST_FORMAT_TIME) { + gst_event_unref (event); + return FALSE; + } + gst_segment_set_newsegment_full (&ogg_pad->segment, update, rate, + applied_rate, format, start, stop, position); + break; + } + case GST_EVENT_FLUSH_STOP:{ + gst_segment_init (&ogg_pad->segment, GST_FORMAT_TIME); + break; + } default: ret = TRUE; break; @@ -428,6 +452,8 @@ gst_ogg_mux_request_new_pad (GstElement * element, oggpad->map.headers = NULL; oggpad->map.queued = NULL; + gst_segment_init (&oggpad->segment, GST_FORMAT_TIME); + oggpad->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad); gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_ogg_mux_sink_event)); @@ -512,7 +538,8 @@ gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta) } static GstFlowReturn -gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer) +gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer, + GstOggPadData * oggpad) { GstCaps *caps; @@ -523,11 +550,11 @@ gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer) /* Ensure we have monotonically increasing timestamps in the output. */ if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { - if (mux->last_ts != GST_CLOCK_TIME_NONE && - GST_BUFFER_TIMESTAMP (buffer) < mux->last_ts) + gint64 run_time = GST_BUFFER_RUNNING_TIME (buffer, oggpad); + if (mux->last_ts != GST_CLOCK_TIME_NONE && run_time < mux->last_ts) GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts; else - mux->last_ts = GST_BUFFER_TIMESTAMP (buffer); + mux->last_ts = run_time; } caps = gst_pad_get_negotiated_caps (mux->srcpad); @@ -602,7 +629,7 @@ gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret) while (buf && GST_BUFFER_OFFSET_END (buf) == -1) { GST_LOG_OBJECT (pad->collect.pad, "[gp -1] pushing page"); g_queue_pop_head (pad->pagebuffers); - *flowret = gst_ogg_mux_push_buffer (mux, buf); + *flowret = gst_ogg_mux_push_buffer (mux, buf, pad); buf = g_queue_peek_head (pad->pagebuffers); ret = TRUE; } @@ -636,7 +663,7 @@ gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret) GST_GP_FORMAT " pushing oldest page buffer %p (granulepos time %" GST_TIME_FORMAT ")", GST_BUFFER_OFFSET_END (buf), buf, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf))); - *flowret = gst_ogg_mux_push_buffer (mux, buf); + *flowret = gst_ogg_mux_push_buffer (mux, buf, opad); ret = TRUE; } @@ -727,6 +754,11 @@ gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPadData * first, if (secondtime == GST_CLOCK_TIME_NONE) return 1; + firsttime = gst_segment_to_running_time (&first->segment, GST_FORMAT_TIME, + firsttime); + secondtime = gst_segment_to_running_time (&second->segment, GST_FORMAT_TIME, + secondtime); + /* first buffer has higher timestamp, second one should go first */ if (secondtime < firsttime) return 1; @@ -1173,7 +1205,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux) if (hwalk == NULL) { GST_LOG_OBJECT (mux, "flushing page as packet %" G_GUINT64_FORMAT " is first or " - "last packet", packet.packetno); + "last packet", (guint64) packet.packetno); while (ogg_stream_flush (&pad->map.stream, &page)) { GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE); @@ -1209,7 +1241,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux) hbufs = g_list_delete_link (hbufs, hbufs); - if ((ret = gst_ogg_mux_push_buffer (mux, buf)) != GST_FLOW_OK) + if ((ret = gst_ogg_mux_push_buffer (mux, buf, NULL)) != GST_FLOW_OK) break; } /* free any remaining nodes/buffers in case we couldn't push them */ @@ -1685,6 +1717,8 @@ gst_ogg_mux_init_collectpads (GstCollectPads * collect) oggpad->data_pushed = FALSE; oggpad->pagebuffers = g_queue_new (); + gst_segment_init (&oggpad->segment, GST_FORMAT_TIME); + walk = g_slist_next (walk); } } @@ -1715,6 +1749,8 @@ gst_ogg_mux_clear_collectpads (GstCollectPads * collect) gst_buffer_unref (oggpad->next_buffer); oggpad->next_buffer = NULL; } + + gst_segment_init (&oggpad->segment, GST_FORMAT_TIME); } } diff --git a/ext/ogg/gstoggmux.h b/ext/ogg/gstoggmux.h index 6812f82ca2..933226d07a 100644 --- a/ext/ogg/gstoggmux.h +++ b/ext/ogg/gstoggmux.h @@ -53,6 +53,8 @@ typedef struct GstOggStream map; gboolean have_type; + GstSegment segment; + /* These two buffers make a very simple queue - they enter as 'next_buffer' * and (usually) leave as 'buffer', except at EOS, when buffer will be NULL */ GstBuffer *buffer; /* the first waiting buffer for the pad */ diff --git a/ext/pango/gsttextoverlay.c b/ext/pango/gsttextoverlay.c index 4957ce88bf..64524f7539 100644 --- a/ext/pango/gsttextoverlay.c +++ b/ext/pango/gsttextoverlay.c @@ -143,6 +143,13 @@ GST_DEBUG_CATEGORY (pango_debug); ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \ } +#define OVER(ret, alphaA, Ca, alphaB, Cb, alphaNew) \ +{ \ + gint _tmp; \ + _tmp = (Ca * alphaA + Cb * alphaB * (255 - alphaA) / 255) / alphaNew; \ + ret = CLAMP (_tmp, 0, 255); \ +} + #if G_BYTE_ORDER == G_LITTLE_ENDIAN # define CAIRO_ARGB_A 3 # define CAIRO_ARGB_R 2 @@ -186,7 +193,13 @@ static GstStaticPadTemplate src_template_factory = GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";" + GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_xRGB ";" + GST_VIDEO_CAPS_xBGR ";" + GST_VIDEO_CAPS_RGBA ";" + GST_VIDEO_CAPS_BGRA ";" + GST_VIDEO_CAPS_ARGB ";" + GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_YUV ("{AYUV, I420, UYVY, NV12, NV21}")) ); @@ -195,7 +208,13 @@ static GstStaticPadTemplate video_sink_template_factory = GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";" + GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_xRGB ";" + GST_VIDEO_CAPS_xBGR ";" + GST_VIDEO_CAPS_RGBA ";" + GST_VIDEO_CAPS_BGRA ";" + GST_VIDEO_CAPS_ARGB ";" + GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_YUV ("{AYUV, I420, UYVY, NV12, NV21}")) ); @@ -1439,6 +1458,8 @@ gst_text_overlay_shade_packed_Y (GstTextOverlay * overlay, guchar * dest, } #define gst_text_overlay_shade_BGRx gst_text_overlay_shade_xRGB +#define gst_text_overlay_shade_RGBx gst_text_overlay_shade_xRGB +#define gst_text_overlay_shade_xBGR gst_text_overlay_shade_xRGB static inline void gst_text_overlay_shade_xRGB (GstTextOverlay * overlay, guchar * dest, gint x0, gint x1, gint y0, gint y1) @@ -1464,6 +1485,36 @@ gst_text_overlay_shade_xRGB (GstTextOverlay * overlay, guchar * dest, } } +#define ARGB_SHADE_FUNCTION(name, OFFSET) \ +static inline void \ +gst_text_overlay_shade_##name (GstTextOverlay * overlay, guchar * dest, \ +gint x0, gint x1, gint y0, gint y1) \ +{ \ + gint i, j;\ + \ + x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\ + x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\ + \ + y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\ + y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\ + \ + for (i = y0; i < y1; i++) {\ + for (j = x0; j < x1; j++) {\ + gint y, y_pos, k;\ + y_pos = (i * 4 * overlay->width) + j * 4;\ + for (k = OFFSET; k < 3+OFFSET; k++) {\ + y = dest[y_pos + k] + overlay->shading_value;\ + dest[y_pos + k] = CLAMP (y, 0, 255);\ + }\ + }\ + }\ +} +ARGB_SHADE_FUNCTION (ARGB, 1); +ARGB_SHADE_FUNCTION (ABGR, 1); +ARGB_SHADE_FUNCTION (RGBA, 0); +ARGB_SHADE_FUNCTION (BGRA, 0); + + /* FIXME: * - use proper strides and offset for I420 * - don't draw over the edge of the picture (try a longer @@ -1617,7 +1668,7 @@ static inline void gst_text_overlay_blit_AYUV (GstTextOverlay * overlay, guint8 * rgb_pixels, gint xpos, gint ypos) { - int a, r, g, b; + int a, r, g, b, a1; int y, u, v; int i, j; int h, w; @@ -1649,14 +1700,17 @@ gst_text_overlay_blit_AYUV (GstTextOverlay * overlay, CAIRO_UNPREMULTIPLY (a, r, g, b); + // convert background to yuv COMP_Y (y, r, g, b); COMP_U (u, r, g, b); COMP_V (v, r, g, b); - a = (a * dest[0] + 128) >> 8; - BLEND (dest[1], a, y, dest[1]); - BLEND (dest[2], a, u, dest[2]); - BLEND (dest[3], a, v, dest[3]); + // preform text "OVER" background alpha compositing + a1 = a + (dest[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0 + OVER (dest[1], a, y, dest[0], dest[1], a1); + OVER (dest[2], a, u, dest[0], dest[2], a1); + OVER (dest[3], a, v, dest[0], dest[3], a1); + dest[0] = a1 - 1; // remove the temporary 1 we added pimage += 4; dest += 4; @@ -1712,6 +1766,57 @@ gst_text_overlay_blit_##name (GstTextOverlay * overlay, \ } xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3); xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0); +xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1); +xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2); + +#define ARGB_BLIT_FUNCTION(name, A, R, G, B) \ +static inline void \ +gst_text_overlay_blit_##name (GstTextOverlay * overlay, \ + guint8 * rgb_pixels, gint xpos, gint ypos) \ +{ \ + int a, r, g, b, a1; \ + int i, j; \ + int h, w; \ + guchar *pimage, *dest; \ + \ + w = overlay->image_width; \ + h = overlay->image_height; \ + \ + if (xpos < 0) { \ + xpos = 0; \ + } \ + \ + if (xpos + w > overlay->width) { \ + w = overlay->width - xpos; \ + } \ + \ + if (ypos + h > overlay->height) { \ + h = overlay->height - ypos; \ + } \ + \ + for (i = 0; i < h; i++) { \ + pimage = overlay->text_image + i * overlay->image_width * 4; \ + dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \ + for (j = 0; j < w; j++) { \ + a = pimage[CAIRO_ARGB_A]; \ + b = pimage[CAIRO_ARGB_B]; \ + g = pimage[CAIRO_ARGB_G]; \ + r = pimage[CAIRO_ARGB_R]; \ + CAIRO_UNPREMULTIPLY (a, r, g, b); \ + a1 = a + (dest[A] * (255 - a)) / 255 + 1; \ + OVER (dest[R], a, r, dest[0], dest[R], a1); \ + OVER (dest[G], a, g, dest[0], dest[G], a1); \ + OVER (dest[B], a, b, dest[0], dest[B], a1); \ + dest[A] = a1 - 1; \ + pimage += 4; \ + dest += 4; \ + } \ + } \ +} +ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2); +ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0); +ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3); +ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1); static void gst_text_overlay_render_text (GstTextOverlay * overlay, @@ -1839,11 +1944,41 @@ gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame) xpos, xpos + overlay->image_width, ypos, ypos + overlay->image_height); break; + case GST_VIDEO_FORMAT_xBGR: + gst_text_overlay_shade_xBGR (overlay, + GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, + ypos, ypos + overlay->image_height); + break; case GST_VIDEO_FORMAT_BGRx: gst_text_overlay_shade_BGRx (overlay, data, xpos, xpos + overlay->image_width, ypos, ypos + overlay->image_height); break; + case GST_VIDEO_FORMAT_RGBx: + gst_text_overlay_shade_RGBx (overlay, + GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, + ypos, ypos + overlay->image_height); + break; + case GST_VIDEO_FORMAT_ARGB: + gst_text_overlay_shade_ARGB (overlay, + GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, + ypos, ypos + overlay->image_height); + break; + case GST_VIDEO_FORMAT_ABGR: + gst_text_overlay_shade_ABGR (overlay, + GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, + ypos, ypos + overlay->image_height); + break; + case GST_VIDEO_FORMAT_RGBA: + gst_text_overlay_shade_RGBA (overlay, + GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, + ypos, ypos + overlay->image_height); + break; + case GST_VIDEO_FORMAT_BGRA: + gst_text_overlay_shade_BGRA (overlay, + GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, + ypos, ypos + overlay->image_height); + break; default: g_assert_not_reached (); } @@ -1873,6 +2008,30 @@ gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame) case GST_VIDEO_FORMAT_xRGB: gst_text_overlay_blit_xRGB (overlay, data, xpos, ypos); break; + case GST_VIDEO_FORMAT_RGBx: + gst_text_overlay_blit_RGBx (overlay, + GST_BUFFER_DATA (video_frame), xpos, ypos); + break; + case GST_VIDEO_FORMAT_xBGR: + gst_text_overlay_blit_xBGR (overlay, + GST_BUFFER_DATA (video_frame), xpos, ypos); + break; + case GST_VIDEO_FORMAT_ARGB: + gst_text_overlay_blit_ARGB (overlay, + GST_BUFFER_DATA (video_frame), xpos, ypos); + break; + case GST_VIDEO_FORMAT_ABGR: + gst_text_overlay_blit_ABGR (overlay, + GST_BUFFER_DATA (video_frame), xpos, ypos); + break; + case GST_VIDEO_FORMAT_RGBA: + gst_text_overlay_blit_RGBA (overlay, + GST_BUFFER_DATA (video_frame), xpos, ypos); + break; + case GST_VIDEO_FORMAT_BGRA: + gst_text_overlay_blit_BGRA (overlay, + GST_BUFFER_DATA (video_frame), xpos, ypos); + break; default: g_assert_not_reached (); } diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index 1912263da0..da1bff03c4 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -1326,6 +1326,57 @@ flushing: } } +static gint64 +gst_base_audio_sink_get_alignment (GstBaseAudioSink * sink, + GstClockTime sample_offset) +{ + GstRingBuffer *ringbuf = sink->ringbuffer; + gint64 align; + gint64 diff; + gint64 maxdrift; + gint segdone = g_atomic_int_get (&ringbuf->segdone) - ringbuf->segbase; + gint64 samples_done = segdone * ringbuf->samples_per_seg; + gint64 headroom = sample_offset - samples_done; + gboolean allow_align = TRUE; + + /* now try to align the sample to the previous one, first see how big the + * difference is. */ + if (sample_offset >= sink->next_sample) + diff = sample_offset - sink->next_sample; + else + diff = sink->next_sample - sample_offset; + + /* calculate the max allowed drift in units of samples. By default this is + * 20ms and should be anough to compensate for timestamp rounding errors. */ + maxdrift = (ringbuf->spec.rate * sink->priv->drift_tolerance) / GST_MSECOND; + + /* calc align with previous sample */ + align = sink->next_sample - sample_offset; + + /* don't align if it means writing behind the read-segment */ + if (diff > headroom && align < 0) + allow_align = FALSE; + + if (G_LIKELY (diff < maxdrift && allow_align)) { + GST_DEBUG_OBJECT (sink, + "align with prev sample, ABS (%" G_GINT64_FORMAT ") < %" + G_GINT64_FORMAT, align, maxdrift); + } else { + /* calculate sample diff in seconds for error message */ + gint64 diff_s = + gst_util_uint64_scale_int (diff, GST_SECOND, ringbuf->spec.rate); + /* timestamps drifted apart from previous samples too much, we need to + * resync. We log this as an element warning. */ + GST_WARNING_OBJECT (sink, + "Unexpected discontinuity in audio timestamps of " + "%s%" GST_TIME_FORMAT ", resyncing", + sample_offset > sink->next_sample ? "+" : "-", GST_TIME_ARGS (diff_s)); + align = 0; + } + + return align; +} + static GstFlowReturn gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) { @@ -1348,7 +1399,6 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) GstFlowReturn ret; GstSegment clip_seg; gint64 time_offset; - gint64 maxdrift; sink = GST_BASE_AUDIO_SINK (bsink); @@ -1582,29 +1632,7 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) goto no_align; } - /* now try to align the sample to the previous one, first see how big the - * difference is. */ - if (sample_offset >= sink->next_sample) - diff = sample_offset - sink->next_sample; - else - diff = sink->next_sample - sample_offset; - - /* calculate the max allowed drift in units of samples. By default this is - * 20ms and should be anough to compensate for timestamp rounding errors. */ - maxdrift = (ringbuf->spec.rate * sink->priv->drift_tolerance) / GST_MSECOND; - - if (G_LIKELY (diff < maxdrift)) { - /* calc align with previous sample */ - align = sink->next_sample - sample_offset; - GST_DEBUG_OBJECT (sink, - "align with prev sample, ABS (%" G_GINT64_FORMAT ") < %" - G_GINT64_FORMAT, align, maxdrift); - } else { - GST_DEBUG_OBJECT (sink, - "discont timestamp (%" G_GINT64_FORMAT ") >= %" G_GINT64_FORMAT, diff, - maxdrift); - align = 0; - } + align = gst_base_audio_sink_get_alignment (sink, sample_offset); sink->priv->last_align = align; /* apply alignment */ diff --git a/gst-libs/gst/audio/gstbaseaudiosrc.c b/gst-libs/gst/audio/gstbaseaudiosrc.c index 4d4418ecb9..8135e432c7 100644 --- a/gst-libs/gst/audio/gstbaseaudiosrc.c +++ b/gst-libs/gst/audio/gstbaseaudiosrc.c @@ -560,14 +560,19 @@ gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps) spec->buffer_time = src->buffer_time; spec->latency_time = src->latency_time; - if (!gst_ring_buffer_parse_caps (spec, caps)) + GST_OBJECT_LOCK (src); + if (!gst_ring_buffer_parse_caps (spec, caps)) { + GST_OBJECT_UNLOCK (src); goto parse_error; + } /* calculate suggested segsize and segtotal */ spec->segsize = spec->rate * spec->bytes_per_sample * spec->latency_time / GST_MSECOND; spec->segtotal = spec->buffer_time / spec->latency_time; + GST_OBJECT_UNLOCK (src); + GST_DEBUG ("release old ringbuffer"); gst_ring_buffer_release (src->ringbuffer); @@ -628,9 +633,12 @@ gst_base_audio_src_query (GstBaseSrc * bsrc, GstQuery * query) GstClockTime min_latency, max_latency; GstRingBufferSpec *spec; + GST_OBJECT_LOCK (src); if (G_UNLIKELY (src->ringbuffer == NULL - || src->ringbuffer->spec.rate == 0)) + || src->ringbuffer->spec.rate == 0)) { + GST_OBJECT_UNLOCK (src); goto done; + } spec = &src->ringbuffer->spec; @@ -642,6 +650,7 @@ gst_base_audio_src_query (GstBaseSrc * bsrc, GstQuery * query) max_latency = gst_util_uint64_scale_int (spec->segtotal * spec->segsize, GST_SECOND, spec->rate * spec->bytes_per_sample); + GST_OBJECT_UNLOCK (src); GST_DEBUG_OBJECT (src, "report latency min %" GST_TIME_FORMAT " max %" GST_TIME_FORMAT, diff --git a/gst-libs/gst/rtp/gstrtcpbuffer.c b/gst-libs/gst/rtp/gstrtcpbuffer.c index 07a1b79fba..1ba9a6a961 100644 --- a/gst-libs/gst/rtp/gstrtcpbuffer.c +++ b/gst-libs/gst/rtp/gstrtcpbuffer.c @@ -161,7 +161,7 @@ gst_rtcp_buffer_validate_data (guint8 * data, guint len) goto wrong_length; /* get padding */ - pad_bytes = data[len - 1]; + pad_bytes = data[data_len - 1]; if (data_len != pad_bytes) goto wrong_padding; } diff --git a/gst-libs/gst/tag/gstvorbistag.c b/gst-libs/gst/tag/gstvorbistag.c index 2700ae4b02..9d86ee7244 100644 --- a/gst-libs/gst/tag/gstvorbistag.c +++ b/gst-libs/gst/tag/gstvorbistag.c @@ -37,6 +37,8 @@ #include "config.h" #endif #include +#include +#include #include "gsttageditingprivate.h" #include #include @@ -313,28 +315,20 @@ gst_vorbis_tag_add_coverart (GstTagList * tags, gchar * img_data_base64, { GstBuffer *img; gsize img_len; - guchar *out; - guint save = 0; - gint state = 0; if (base64_len < 2) goto not_enough_data; /* img_data_base64 points to a temporary copy of the base64 encoded data, so * it's safe to do inpace decoding here - * TODO: glib 2.20 and later provides g_base64_decode_inplace, so change this - * to use glib's API instead once it's in wider use: - * http://bugzilla.gnome.org/show_bug.cgi?id=564728 - * http://svn.gnome.org/viewvc/glib?view=revision&revision=7807 */ - out = (guchar *) img_data_base64; - img_len = g_base64_decode_step (img_data_base64, base64_len, - out, &state, &save); - + */ + g_base64_decode_inplace (img_data_base64, &img_len); if (img_len == 0) goto decode_failed; - img = gst_tag_image_data_to_image_buffer (out, img_len, - GST_TAG_IMAGE_TYPE_NONE); + img = + gst_tag_image_data_to_image_buffer ((const guint8 *) img_data_base64, + img_len, GST_TAG_IMAGE_TYPE_NONE); if (img == NULL) goto convert_failed; @@ -363,6 +357,65 @@ convert_failed: } } +/* Standardized way of adding pictures to vorbiscomments: + * http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE + */ +static void +gst_vorbis_tag_add_metadata_block_picture (GstTagList * tags, + gchar * value, gint value_len) +{ + GstByteReader reader; + guint32 img_len = 0, img_type = 0; + guint32 img_mimetype_len = 0, img_description_len = 0; + gsize decoded_len; + const guint8 *data; + + /* img_data_base64 points to a temporary copy of the base64 encoded data, so + * it's safe to do inpace decoding here + */ + g_base64_decode_inplace (value, &decoded_len); + if (decoded_len == 0) + goto decode_failed; + + gst_byte_reader_init (&reader, (guint8 *) value, decoded_len); + + if (!gst_byte_reader_get_uint32_be (&reader, &img_type)) + goto error; + + if (!gst_byte_reader_get_uint32_be (&reader, &img_mimetype_len)) + goto error; + if (!gst_byte_reader_skip (&reader, img_mimetype_len)) + goto error; + + if (!gst_byte_reader_get_uint32_be (&reader, &img_description_len)) + goto error; + if (!gst_byte_reader_skip (&reader, img_description_len)) + goto error; + + /* Skip width, height, color depth and number of colors for + * indexed formats */ + if (!gst_byte_reader_skip (&reader, 4 * 4)) + goto error; + + if (!gst_byte_reader_get_uint32_be (&reader, &img_len)) + goto error; + + if (!gst_byte_reader_get_data (&reader, img_len, &data)) + goto error; + + gst_tag_list_add_id3_image (tags, data, img_len, img_type); + + return; + +error: + GST_WARNING + ("Couldn't extract image or image type from METADATA_BLOCK_PICTURE tag"); + return; +decode_failed: + GST_WARNING ("Failed to decode Base64 data from METADATA_BLOCK_PICTURE tag"); + return; +} + /** * gst_tag_list_from_vorbiscomment: * @data: data to convert @@ -439,6 +492,8 @@ gst_tag_list_from_vorbiscomment (const guint8 * data, gsize size, continue; } else if (g_ascii_strcasecmp (cur, "COVERART") == 0) { gst_vorbis_tag_add_coverart (list, value, value_len); + } else if (g_ascii_strcasecmp (cur, "METADATA_BLOCK_PICTURE") == 0) { + gst_vorbis_tag_add_metadata_block_picture (list, value, value_len); } else { gst_vorbis_tag_add (list, cur, value); } @@ -493,38 +548,79 @@ typedef struct MyForEach; static GList * -gst_tag_to_coverart (const GValue * image_value) +gst_tag_to_metadata_block_picture (const gchar * tag, + const GValue * image_value) { - gchar *coverart_data, *data_result, *mime_result; + gchar *comment_data, *data_result; const gchar *mime_type; + guint mime_type_len; GstStructure *mime_struct; GstBuffer *buffer; GList *l = NULL; guint8 *data; gsize size; + GstByteWriter writer; + GstTagImageType image_type = GST_TAG_IMAGE_TYPE_NONE; + gint width = 0, height = 0; + guint8 *metadata_block; + guint metadata_block_len; g_return_val_if_fail (image_value != NULL, NULL); buffer = gst_value_get_buffer (image_value); g_return_val_if_fail (gst_caps_is_fixed (buffer->caps), NULL); mime_struct = gst_caps_get_structure (buffer->caps, 0); + mime_type = gst_structure_get_name (mime_struct); + if (strcmp (mime_type, "text/uri-list") == 0) + mime_type = "-->"; + mime_type_len = strlen (mime_type); + + gst_structure_get (mime_struct, "image-type", GST_TYPE_TAG_IMAGE_TYPE, + &image_type, "width", G_TYPE_INT, &width, "height", G_TYPE_INT, &height, + NULL); + + metadata_block_len = 32 + mime_type_len + GST_BUFFER_SIZE (buffer); + gst_byte_writer_init_with_size (&writer, metadata_block_len, TRUE); + + if (image_type == GST_TAG_IMAGE_TYPE_NONE + && strcmp (tag, GST_TAG_PREVIEW_IMAGE) == 0) { + gst_byte_writer_put_uint32_be_unchecked (&writer, 0x01); + } else { + /* Convert to ID3v2 APIC image type */ + if (image_type == GST_TAG_IMAGE_TYPE_NONE) + image_type = GST_TAG_IMAGE_TYPE_UNDEFINED; + else + image_type = image_type + 2; + gst_byte_writer_put_uint32_be_unchecked (&writer, image_type); + } + + gst_byte_writer_put_uint32_be_unchecked (&writer, mime_type_len); + gst_byte_writer_put_data_unchecked (&writer, (guint8 *) mime_type, + mime_type_len); + /* description length */ + gst_byte_writer_put_uint32_be_unchecked (&writer, 0); + gst_byte_writer_put_uint32_be_unchecked (&writer, width); + gst_byte_writer_put_uint32_be_unchecked (&writer, height); + /* color depth */ + gst_byte_writer_put_uint32_be_unchecked (&writer, 0); + /* for indexed formats the number of colors */ + gst_byte_writer_put_uint32_be_unchecked (&writer, 0); data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ); - if (strcmp (mime_type, "text/uri-list") == 0) { - /* URI reference */ - coverart_data = g_strndup ((gchar *) data, size); - } else { - coverart_data = g_base64_encode (data, size); - } + gst_byte_writer_put_uint32_be_unchecked (&writer, size); + gst_byte_writer_put_data_unchecked (&writer, data, size); gst_buffer_unmap (buffer, data, size); - data_result = g_strdup_printf ("COVERART=%s", coverart_data); - mime_result = g_strdup_printf ("COVERARTMIME=%s", mime_type); - g_free (coverart_data); + g_assert (gst_byte_writer_get_pos (&writer) == metadata_block_len); + + metadata_block = gst_byte_writer_reset_and_get_data (&writer); + comment_data = g_base64_encode (metadata_block, metadata_block_len); + g_free (metadata_block); + data_result = g_strdup_printf ("METADATA_BLOCK_PICTURE=%s", comment_data); + g_free (comment_data); l = g_list_append (l, data_result); - l = g_list_append (l, mime_result); return l; } @@ -559,7 +655,8 @@ gst_tag_to_vorbis_comments (const GstTagList * list, const gchar * tag) if ((strcmp (tag, GST_TAG_PREVIEW_IMAGE) == 0 && gst_tag_list_get_tag_size (list, GST_TAG_IMAGE) == 0) || strcmp (tag, GST_TAG_IMAGE) == 0) { - return gst_tag_to_coverart (gst_tag_list_get_value_index (list, tag, 0)); + return gst_tag_to_metadata_block_picture (tag, + gst_tag_list_get_value_index (list, tag, 0)); } if (strcmp (tag, GST_TAG_EXTENDED_COMMENT) != 0) { diff --git a/gst-libs/gst/video/video.c b/gst-libs/gst/video/video.c index fd265a13e6..efb2e9f051 100644 --- a/gst-libs/gst/video/video.c +++ b/gst-libs/gst/video/video.c @@ -1926,9 +1926,9 @@ gst_video_format_get_component_offset (GstVideoFormat format, if (component == 0) return 0; if (component == 1) - return GST_ROUND_UP_4 (width) * GST_ROUND_UP_2 (height); + return GST_ROUND_UP_4 (width) * height; if (component == 2) { - return GST_ROUND_UP_4 (width) * GST_ROUND_UP_2 (height) + + return GST_ROUND_UP_4 (width) * height + GST_ROUND_UP_4 (GST_ROUND_UP_4 (width) / 4) * (GST_ROUND_UP_4 (height) / 4); } diff --git a/gst/encoding/Makefile.am b/gst/encoding/Makefile.am index 0e287fad13..2bd7cddfa8 100644 --- a/gst/encoding/Makefile.am +++ b/gst/encoding/Makefile.am @@ -6,8 +6,6 @@ glib_gen_basename = gstencode built_sources = gstencode-marshal.c built_headers = gstencode-marshal.h -plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ - plugin_LTLIBRARIES = libgstencodebin.la libgstencodebin_la_SOURCES = \ diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am index 6e7130e4ce..b78e2e2823 100644 --- a/gst/playback/Makefile.am +++ b/gst/playback/Makefile.am @@ -6,8 +6,6 @@ glib_gen_basename = gstplay built_sources = gstplay-marshal.c built_headers = gstplay-marshal.h -plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ - plugin_LTLIBRARIES = libgstplaybin.la libgstdecodebin.la libgstdecodebin2.la libgstplaybin_la_SOURCES = \ diff --git a/tests/check/libs/video.c b/tests/check/libs/video.c index 57de1be046..8af3c1da87 100644 --- a/tests/check/libs/video.c +++ b/tests/check/libs/video.c @@ -424,14 +424,6 @@ GST_START_TEST (test_video_formats) off1 = gst_video_format_get_component_offset (fmt, 1, w, h); off2 = gst_video_format_get_component_offset (fmt, 2, w, h); - /* FIXME: for YUV9/YVU9 old videotestsrc code disagrees with new code - * - figure out which is right */ - if (fmt == GST_VIDEO_FORMAT_YUV9 || fmt == GST_VIDEO_FORMAT_YVU9) { - if (w == 1 && h == 1) - GST_ERROR ("FIXME: fix GST_VIDEO_FORMAT_YUV9/YVU9 size checks"); - goto skip_check; - } - fail_unless_equals_int (size, (unsigned long) paintinfo.endptr); fail_unless_equals_int (off0, (unsigned long) paintinfo.yp); fail_unless_equals_int (off1, (unsigned long) paintinfo.up);