/* * gstvp82json.c - VP8 parsed bistream to json * * Copyright (C) 2023 Collabora * Author: Benjamin Gaignard * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:element-vp82json * @title: vp82json * * Convert VP8 bitstream parameters to JSON formated text. * * ## Example launch line * ``` * gst-launch-1.0 filesrc location=/path/to/vp8/file ! parsebin ! vp82json ! filesink location=/path/to/json/file * ``` * * Since: 1.24 */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "gstvp82json.h" GST_DEBUG_CATEGORY (gst_vp8_2_json_debug); #define GST_CAT_DEFAULT gst_vp8_2_json_debug struct _GstVp82json { GstElement parent; GstPad *sinkpad, *srcpad; guint frame_counter; GstVp8Parser parser; JsonObject *json; }; static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-vp8") ); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("text/x-json,format=vp8")); #define gst_vp8_2_json_parent_class parent_class G_DEFINE_TYPE_WITH_CODE (GstVp82json, gst_vp8_2_json, GST_TYPE_ELEMENT, GST_DEBUG_CATEGORY_INIT (gst_vp8_2_json_debug, "vp82json", 0, "VP8 to json")); static void gst_vp8_2_json_finalize (GObject * object) { GstVp82json *self = GST_VP8_2_JSON (object); json_object_unref (self->json); } static gchar * get_string_from_json_object (JsonObject * object) { JsonNode *root; JsonGenerator *generator; gchar *text; /* Make it the root node */ root = json_node_init_object (json_node_alloc (), object); generator = json_generator_new (); json_generator_set_indent (generator, 2); json_generator_set_indent_char (generator, ' '); json_generator_set_pretty (generator, TRUE); json_generator_set_root (generator, root); text = json_generator_to_data (generator, NULL); /* Release everything */ g_object_unref (generator); json_node_free (root); return text; } static GstFlowReturn gst_vp8_2_json_chain (GstPad * sinkpad, GstObject * object, GstBuffer * in_buf) { GstVp82json *self = GST_VP8_2_JSON (object); JsonObject *json = self->json; GstVp8ParserResult pres; GstVp8FrameHdr frame_hdr; JsonObject *range; GstBuffer *out_buf; gchar *json_string; guint length; GstMapInfo in_map, out_map; GstFlowReturn ret = GST_FLOW_OK; if (!gst_buffer_map (in_buf, &in_map, GST_MAP_READ)) { GST_ERROR_OBJECT (self, "Cannot map buffer"); return GST_FLOW_ERROR; } pres = gst_vp8_parser_parse_frame_header (&self->parser, &frame_hdr, in_map.data, in_map.size); if (pres != GST_VP8_PARSER_OK) { GST_WARNING_OBJECT (self, "Cannot parse frame header"); goto unmap; } json_object_set_int_member (json, "frame number", self->frame_counter++); if (frame_hdr.key_frame) json_object_set_string_member (json, "frame type", "keyframe"); else json_object_set_string_member (json, "frame type", "interframe"); json_object_set_int_member (json, "version", frame_hdr.version); json_object_set_int_member (json, "show frame", frame_hdr.show_frame); json_object_set_int_member (json, "data chunk size", frame_hdr.data_chunk_size); json_object_set_int_member (json, "first part size", frame_hdr.first_part_size); if (frame_hdr.key_frame) { JsonArray *array, *token_probs, *mv_probs, *y_prob, *uv_prob; JsonObject *quant_indices, *mode_probs; guint i, j, k, l; json_object_set_int_member (json, "width", frame_hdr.width); json_object_set_int_member (json, "height", frame_hdr.height); json_object_set_int_member (json, "horizontal scale code", frame_hdr.horiz_scale_code); json_object_set_int_member (json, "vertical scale code", frame_hdr.vert_scale_code); json_object_set_int_member (json, "color space", frame_hdr.color_space); json_object_set_int_member (json, "clamping type", frame_hdr.clamping_type); json_object_set_int_member (json, "filter type", frame_hdr.filter_type); json_object_set_int_member (json, "loop filter level", frame_hdr.loop_filter_level); json_object_set_int_member (json, "sharpness level", frame_hdr.sharpness_level); json_object_set_int_member (json, "log2 nbr of dct partitions", frame_hdr.log2_nbr_of_dct_partitions); array = json_array_new (); for (i = 0; i < 8; i++) json_array_add_int_element (array, frame_hdr.partition_size[i]); json_object_set_array_member (json, "partition size", array); quant_indices = json_object_new (); json_object_set_int_member (quant_indices, "y ac qi", frame_hdr.quant_indices.y_ac_qi); json_object_set_int_member (quant_indices, "y dc delta", frame_hdr.quant_indices.y_dc_delta); json_object_set_int_member (quant_indices, "y2 dc delta", frame_hdr.quant_indices.y2_dc_delta); json_object_set_int_member (quant_indices, "y2 ac delta", frame_hdr.quant_indices.y2_ac_delta); json_object_set_int_member (quant_indices, "uv dc delta", frame_hdr.quant_indices.uv_dc_delta); json_object_set_int_member (quant_indices, "uv ac delta", frame_hdr.quant_indices.uv_ac_delta); json_object_set_object_member (json, "quant indices", quant_indices); token_probs = json_array_new (); for (i = 0; i < 4; i++) for (j = 0; j < 8; j++) for (k = 0; k < 3; k++) for (l = 0; l < 11; l++) json_array_add_int_element (token_probs, frame_hdr.token_probs.prob[i][j][k][l]); json_object_set_array_member (json, "token probabilities", token_probs); mv_probs = json_array_new (); for (i = 0; i < 2; i++) for (j = 0; j < 19; j++) json_array_add_int_element (mv_probs, frame_hdr.mv_probs.prob[i][j]); json_object_set_array_member (json, "motion vector probabilities", mv_probs); mode_probs = json_object_new (); y_prob = json_array_new (); for (i = 0; i < 4; i++) json_array_add_int_element (y_prob, frame_hdr.mode_probs.y_prob[i]); json_object_set_array_member (mode_probs, "y probabilities", y_prob); uv_prob = json_array_new (); for (i = 0; i < 3; i++) json_array_add_int_element (uv_prob, frame_hdr.mode_probs.uv_prob[i]); json_object_set_array_member (mode_probs, "uv probabilities", uv_prob); json_object_set_object_member (json, "mode probabilities", mode_probs); json_object_set_int_member (json, "refresh entropy probs", frame_hdr.refresh_entropy_probs); json_object_set_int_member (json, "refresh last", frame_hdr.refresh_last); } else { json_object_set_int_member (json, "refresh golden frame", frame_hdr.refresh_golden_frame); json_object_set_int_member (json, "refresh alternate frame", frame_hdr.refresh_alternate_frame); json_object_set_int_member (json, "copy buffer to golden", frame_hdr.copy_buffer_to_golden); json_object_set_int_member (json, "copy buffer to alternate", frame_hdr.copy_buffer_to_alternate); json_object_set_int_member (json, "sign bias golden", frame_hdr.sign_bias_golden); json_object_set_int_member (json, "sign bias alternate", frame_hdr.sign_bias_alternate); json_object_set_int_member (json, "mb no skip coeff", frame_hdr.mb_no_skip_coeff); json_object_set_int_member (json, "prob skip false", frame_hdr.prob_skip_false); json_object_set_int_member (json, "prob intra", frame_hdr.prob_intra); json_object_set_int_member (json, "prob last", frame_hdr.prob_last); json_object_set_int_member (json, "prob gf", frame_hdr.prob_gf); } range = json_object_new (); json_object_set_int_member (range, "rd range", frame_hdr.rd_range); json_object_set_int_member (range, "rd value", frame_hdr.rd_value); json_object_set_int_member (range, "rd count", frame_hdr.rd_count); json_object_set_object_member (json, "range decoder", range); json_object_set_int_member (json, "header size", frame_hdr.header_size); json_string = get_string_from_json_object (json); length = strlen (json_string); out_buf = gst_buffer_new_allocate (NULL, length, NULL); gst_buffer_map (out_buf, &out_map, GST_MAP_WRITE); if (length) memcpy (&out_map.data[0], json_string, length); gst_buffer_unmap (out_buf, &out_map); g_free (json_string); gst_buffer_copy_into (out_buf, in_buf, GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_METADATA, 0, -1); gst_pad_push (self->srcpad, out_buf); unmap: gst_buffer_unmap (in_buf, &in_map); gst_buffer_unref (in_buf); return ret; } static gboolean gst_vp8_2_json_set_caps (GstVp82json * self, GstCaps * caps) { GstCaps *src_caps = gst_caps_new_simple ("text/x-json", "format", G_TYPE_STRING, "vp8", NULL); GstEvent *event; event = gst_event_new_caps (src_caps); gst_caps_unref (src_caps); return gst_pad_push_event (self->srcpad, event); } static gboolean gst_vp8_2_json_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstVp82json *self = GST_VP8_2_JSON (parent); gboolean res = TRUE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: { GstCaps *caps; gst_event_parse_caps (event, &caps); res = gst_vp8_2_json_set_caps (self, caps); gst_event_unref (event); break; } default: res = gst_pad_event_default (pad, parent, event); break; } return res; } static GstStateChangeReturn gst_vp8_2_json_change_state (GstElement * element, GstStateChange transition) { GstVp82json *self = GST_VP8_2_JSON (element); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: self->frame_counter = 0; break; default: break; } return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); } static void gst_vp8_2_json_class_init (GstVp82jsonClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gstelement_class->change_state = gst_vp8_2_json_change_state; gobject_class->finalize = gst_vp8_2_json_finalize; gst_element_class_add_static_pad_template (gstelement_class, &src_factory); gst_element_class_add_static_pad_template (gstelement_class, &sink_factory); gst_element_class_set_static_metadata (gstelement_class, "Vp82json", "Transform", "VP8 to json element", "Benjamin Gaignard "); } static void gst_vp8_2_json_init (GstVp82json * self) { self->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); gst_pad_set_chain_function (self->sinkpad, gst_vp8_2_json_chain); gst_element_add_pad (GST_ELEMENT (self), self->sinkpad); gst_pad_set_event_function (self->sinkpad, GST_DEBUG_FUNCPTR (gst_vp8_2_json_sink_event)); self->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); gst_element_add_pad (GST_ELEMENT (self), self->srcpad); self->json = json_object_new (); self->frame_counter = 0; gst_vp8_parser_init (&self->parser); }