mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-03 16:09:39 +00:00
fd588a50e4
This element convert vp8 frame header into human readable json data. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3734>
371 lines
12 KiB
C
371 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);
|
|
}
|