gstreamer/subprojects/gst-plugins-bad/ext/codec2json/gstvp82json.c
Benjamin Gaignard fd588a50e4 codec2json: Add vp82json element
This element convert vp8 frame header into human readable
json data.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3734>
2023-01-30 19:46:55 +00:00

372 lines
12 KiB
C

/*
* gstvp82json.c - VP8 parsed bistream to json
*
* Copyright (C) 2023 Collabora
* Author: Benjamin Gaignard <benjamin.gaignard@collabora.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., 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 <config.h>
#endif
#include <gst/base/base.h>
#include <json-glib/json-glib.h>
#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 <benjamin.gaignard@collabora.com>");
}
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);
}