Files
2026-08-24 10:03:24 +08:00

449 lines
18 KiB
C

/*
* Copyright (C) 2014-2019 Amlogic, Inc. All rights reserved.
*
* All information contained herein is Amlogic confidential.
*
* This software is provided to you pursuant to Software License Agreement
* (SLA) with Amlogic Inc ("Amlogic"). This software may be used
* only in accordance with the terms of this agreement.
*
* Redistribution and use in source and binary forms, with or without
* modification is strictly prohibited without prior written permission from
* Amlogic.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <gst/gst.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>
#include "video_transcoding.h"
GST_DEBUG_CATEGORY_STATIC(video_transcoding);
#define GST_CAT_DEFAULT video_transcoding
//GstFlowReturn video_transcoding_pulldata_callback(GstElement *sink, CustomData *data);
static void *video_transcoding_workloop(HANDLE *handle);
static GstFlowReturn appsink_pull_data_callback(GstElement *sink, CustomData *data );
void send_eos_event_to_app (GstElement* sink, CustomData *data);
HANDLE video_transcoding_init( video_transcoding_param *param, int argc, char **argv){
GstCaps* src_caps = NULL;
GstCaps* sink_caps = NULL;
CustomData *data = (CustomData *)malloc(sizeof(CustomData));
if (NULL == data)
{
GST_ERROR("%s malloc CustomData error\n", __func__);
return NULL;
}
/* init the logsystem */
GST_DEBUG_CATEGORY_EXTERN(video_transcoding);
GST_DEBUG_CATEGORY_INIT(video_transcoding, "video_transcoding", 0, "libtranscoding log");
GST_DEBUG("%s src_width=%d src_height=%d src_codec=%d src_framerate=%d/1\n \
dst_width=%d dst_height=%d dst_codec=%d dst_framerate=%d/1\n \
bitrate_kb=%d gop_size=%d\n",
__func__, param->src_size.width, param->src_size.height, param->src_codec, param->src_framerate,
param->dst_size.width, param->dst_size.height, param->dst_codec, param->dst_framerate,
param->bitrate_kb, param->gop_size);
printf("%s src_width=%d src_height=%d src_codec=%d src_framerate=%d/1\n \
dst_width=%d dst_height=%d dst_codec=%d dst_framerate=%d/1\n \
bitrate_kb=%d gop_size=%d\n",
__func__, param->src_size.width, param->src_size.height, param->src_codec, param->src_framerate,
param->dst_size.width, param->dst_size.height, param->dst_codec, param->dst_framerate,
param->bitrate_kb, param->gop_size);
/* Initialize cumstom data structure */
memset (data, 0, sizeof (CustomData));
/* Initialize GStreamer */
gst_init (&argc, &argv);
/* switch the decoder codec */
switch (param->src_codec)
{
case AV1:
data->v4l2_dec = gst_element_factory_make ("amlv4l2av1dec", "transcoding-v4l2_av1_dec");
break;
case AVS2:
data->v4l2_dec = gst_element_factory_make ("amlv4l2avs2dec", "transcoding-v4l2_avs2_dec");
break;
case AVS3:
data->v4l2_dec = gst_element_factory_make ("amlv4l2avs3dec", "transcoding-v4l2_avs3_dec");
break;
case AVS:
data->v4l2_dec = gst_element_factory_make ("amlv4l2avsdec", "transcoding-v4l2_avs_dec");
break;
case H264:
data->v4l2_dec = gst_element_factory_make ("amlv4l2h264dec", "transcoding-v4l2_h264_dec");
src_caps = gst_caps_new_simple("video/x-h264",
"width", G_TYPE_INT, param->src_size.width,
"height", G_TYPE_INT, param->src_size.height,
//"framerate", GST_TYPE_FRACTION, param->src_framerate,1,
NULL);
break;
case H265:
data->v4l2_dec = gst_element_factory_make ("amlv4l2h265dec", "transcoding-v4l2_h265_dec");
src_caps = gst_caps_new_simple("video/x-h265",
"width", G_TYPE_INT, param->src_size.width,
"height", G_TYPE_INT, param->src_size.height,
//"framerate", GST_TYPE_FRACTION, param->src_framerate,1,
NULL);
break;
case JPEG:
data->v4l2_dec = gst_element_factory_make ("amlv4l2jpegdec", "transcoding-v4l2_jpeg_dec");
break;
case MPEG4:
data->v4l2_dec = gst_element_factory_make ("amlv4l2mpeg4dec", "transcoding-v4l2_mpeg4_dec");
src_caps = gst_caps_new_simple("video/mpeg",
"mpegversion", G_TYPE_INT, 2,
"systemstream", G_TYPE_BOOLEAN, FALSE,
// "width", G_TYPE_INT, param->src_size.width,
// "height", G_TYPE_INT, param->src_size.height,
//"framerate", GST_TYPE_FRACTION, param->src_framerate,1,
NULL);
//src_caps = gst_caps_new_simple("video/mpeg",NULL);
//src_caps = gst_caps_new_any();
break;
case VC1:
data->v4l2_dec = gst_element_factory_make ("amlv4l2vc1dec", "transcoding-v4l2_vc1_dec");
break;
case VP9:
data->v4l2_dec = gst_element_factory_make ("amlv4l2vp9dec", "transcoding-v4l2_vp9_dec");
break;
default:
GST_ERROR("%s No match dec codec : %d\n", __func__, param->src_codec);
break;
}
//const gchar *dec_name = gst_element_get_name(data->v4l2_dec);
GST_DEBUG("%s dec plugin name : %s\n", __func__, gst_element_get_name(data->v4l2_dec));
/* switch the encoder codec */
switch (param->dst_codec)
{
case JPEG:
data->video_enc = gst_element_factory_make ("amljpegenc", "transcoding-video_jpeg_enc");
break;
case H264:
data->video_enc = gst_element_factory_make ("amlvenc", "transcoding-video_h264_enc");
//sink_caps = gst_caps_new_simple("video/x-raw",
sink_caps = gst_caps_new_simple("video/x-h264",
"width", G_TYPE_INT, param->dst_size.width,
"height", G_TYPE_INT, param->dst_size.height,
"framerate", GST_TYPE_FRACTION, param->dst_framerate,1,
NULL);
break;
case H265:
data->video_enc = gst_element_factory_make ("amlvenc", "transcoding-video_h265_enc");
sink_caps = gst_caps_new_simple("video/x-h265",
"width", G_TYPE_INT, param->dst_size.width,
"height", G_TYPE_INT, param->dst_size.height,
"framerate", GST_TYPE_FRACTION, param->dst_framerate,1,
NULL);
break;
default:
GST_ERROR("No match enc codec : %d\n", param->dst_codec);
break;
}
//const gchar *enc_name = gst_element_get_name(data->video_enc);
GST_DEBUG("%s encoder plugin name : %s\n", __func__, gst_element_get_name(data->video_enc));
/* Create the elements */
// data->app_queue = gst_element_factory_make ("queue", "app_queue");
data->app_sink = gst_element_factory_make ("appsink", "transcoding-app_sink");
data->app_source = gst_element_factory_make ("appsrc", "transcoding-app_source");
// data->ts_demux = gst_element_factory_make ("tsdemux", "ts_demux");
data->video_convert = gst_element_factory_make ("amlvconv", "transcoding-video_convert");
// data->src_parse = gst_element_factory_make ("mpegvideoparse", "src_parse");
// data->video_queue = gst_element_factory_make ("queue", "video_queue");
//data->video_rate = gst_element_factory_make ("videorate", "video_rate");
/* Create the empty pipeline */
data->pipeline = gst_pipeline_new ("transcoding-pipeline");
if (!data->pipeline || !data->app_sink || !data->app_source || !data->v4l2_dec || !data->video_convert || !data->video_enc) {
GST_ERROR("%s Not all elements could be created.\n", __func__);
goto cleanup;
}
/* configure the appsrc */
g_object_set(data->app_source, "caps", src_caps, "block", TRUE, NULL);
GST_DEBUG("%s sink_caps we set : %s\n",__func__, gst_caps_to_string(src_caps));
gst_caps_unref(src_caps);
//g_signal_connect (data->app_source, "need-data", G_CALLBACK (start_feed), &data);
//g_signal_connect (data->app_source, "enough-data", G_CALLBACK (stop_feed), &data);
/* configure the amlvenc */
g_object_set(data->video_enc,"bitrate", param->bitrate_kb, "gop", param->gop_size, NULL);
/*configure the app sink */
g_object_set (data->app_sink, "emit-signals", TRUE, "caps", sink_caps, "sync", FALSE, NULL);
g_signal_connect (data->app_sink, "eos", G_CALLBACK (send_eos_event_to_app), data);
//"async", FALSE,
GST_DEBUG("%s sink_caps we set : %s\n",__func__, gst_caps_to_string(sink_caps));
gst_caps_unref(sink_caps);
//g_signal_connect (data->app_sink, "new-sample", G_CALLBACK (appsink_pull_data_callback), data);
/* Add all plugins to the pipeline */
gst_bin_add_many (GST_BIN (data->pipeline), data->app_source, data->v4l2_dec, data->video_convert, data->video_enc,data->app_sink ,NULL);
if (gst_element_link_many (data->app_source, data->v4l2_dec, data->video_convert, data->video_enc, data->app_sink, NULL) != TRUE ) {
GST_ERROR("%s Elements could not be linked.\n", __func__);
goto cleanup;
}
/* Create a GLib Main Loop and set it to run */
data->main_loop = g_main_loop_new (NULL, FALSE);
data->init = TRUE;
data->playing = FALSE;
data->cus_pull_data_callback = NULL;
// start thread
pthread_create(&data->tid, NULL, video_transcoding_workloop, (HANDLE)data);
return (HANDLE)data;
cleanup:
if (data->pipeline) gst_object_unref(data->pipeline);
if (data->app_source) gst_object_unref(data->app_source);
if (data->v4l2_dec) gst_object_unref(data->v4l2_dec);
if (data->video_convert) gst_object_unref(data->video_convert);
if (data->video_enc) gst_object_unref(data->video_enc);
if (data->app_sink) gst_object_unref(data->app_sink);
free(data);
return NULL;
}
void video_transcoding_deinit(HANDLE *handle){
/* chech input param */
if (NULL == handle) {
GST_ERROR("%s invalid param!\n", __func__);
return;
}
CustomData *data = (CustomData *)handle;
/* free resources */
data->init = FALSE;
data->playing = FALSE;
g_main_loop_quit(data->main_loop);
if (pthread_join(data->tid, NULL) ==0) {
printf("workloop thread exited successful\n");
} else {
printf("workloop thread exited fail\n");
}
g_main_loop_unref(data->main_loop);
gst_object_unref(data->pipeline);
free(data);
return;
}
/* This function is called when an error message is posted on the bus */
static gboolean handle_message (GstBus *bus, GstMessage *msg, CustomData *data)
{
GError *err;
gchar *debug_info;
switch (GST_MESSAGE_TYPE(msg))
{
case GST_MESSAGE_ERROR:
gst_message_parse_error(msg, &err, &debug_info);
GST_ERROR("%s Error received from element %s: %s\n",__func__, GST_OBJECT_NAME(msg->src), err->message);
GST_ERROR("Debugging information: %s\n", debug_info ? debug_info : "none");
g_clear_error(&err);
g_free(debug_info);
g_main_loop_quit(data->main_loop);
break;
case GST_MESSAGE_EOS:
GST_DEBUG("%s End-Of-Stream reached.\n", __func__);
g_main_loop_quit(data->main_loop);
break;
case GST_MESSAGE_STATE_CHANGED:
{
GstState old_state, new_state, pending_state;
gst_message_parse_state_changed(msg, &old_state, &new_state, &pending_state);
GST_DEBUG("%s: state changed from %s to %s\n", GST_MESSAGE_SRC_NAME(msg), gst_element_state_get_name(old_state),
gst_element_state_get_name(new_state));
if (GST_MESSAGE_SRC(msg) == GST_OBJECT(data->pipeline))
{
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(data->pipeline), GST_DEBUG_GRAPH_SHOW_ALL, \
g_strdup_printf("libtranscoding_dot.%s_%s", gst_element_state_get_name(old_state), gst_element_state_get_name(new_state)));
data->playing = FALSE;
}
break;
}
default:
break;
}
/* We want to keep receiving messages */
return TRUE;
}
static void *video_transcoding_workloop(HANDLE *handle) {
/* chech input param */
if (NULL == handle) {
GST_ERROR("%s invalid param!\n", __func__);
return NULL;
}
CustomData *data = (CustomData *)handle;
/* Instruct the bus to emit signals for each received message, and connect to the interesting signals */
// GstBus *bus = gst_element_get_bus (data->pipeline);
// gst_bus_add_signal_watch (bus);
// g_signal_connect (G_OBJECT (bus), "message::error", (GCallback)error_callback, &data);
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(data->pipeline));
gst_bus_add_watch(bus, (GstBusFunc)handle_message, data);
gst_object_unref(bus);
g_main_loop_run(data->main_loop);
GST_DEBUG("%s work loop start work\n", __func__);
return NULL;
}
int video_transcoding_writeData(HANDLE handle, void* buffer, gint buff_size){
GstFlowReturn ret;
/* chech input param */
if (NULL == handle) {
GST_ERROR("%s invalid param!\n", __func__);
return -1;
}
CustomData *data = (CustomData *)handle;
/* push sample to appsrc */
//g_signal_emit_by_name(data->app_source, "push-sample", &sample);
/* for not size set */
//gst_buffer = gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY, buffer, buff_size, 0 ,buff_size, NULL, NULL);
GstBuffer *gst_buffer = gst_buffer_copy((GstBuffer *)buffer);
ret = gst_app_src_push_buffer(data->app_source, gst_buffer);
if (ret != GST_FLOW_OK) {
/* We got some error, stop sending data */
GST_ERROR("%s write data error\n", __func__);
return -1;
}
GST_DEBUG("%s write data addr: %p wirite data size: %d\n", __func__, buffer, buff_size);
return 0;
}
static GstFlowReturn appsink_pull_data_callback(GstElement *sink, CustomData *data ){
GstSample *sample;
GstBuffer *out_buffer;
gint out_size;
//HANDLE *handle = (HANDLE *)data;
/* chech input param */
if (NULL == data) {
GST_ERROR("%s invalid param!\n", __func__);
return GST_FLOW_ERROR;
}
/* Retrieve the buffer */
g_signal_emit_by_name (sink, "pull-sample", &sample);
if (NULL != sample) {
out_buffer = gst_sample_get_buffer(sample);
out_size = gst_buffer_get_size(out_buffer);
/* The only thing we do in this example is print a * to indicate a received buffer */
GST_DEBUG ("%s out_buffer addr: %p out_buffer size: %d\n", __func__, out_buffer, out_size);
//video_transcoding_pulldata_callback(handle, buffer, out_size);
/* app pull data callback */
data->cus_pull_data_callback(out_buffer, out_size);
gst_sample_unref (sample);
return GST_FLOW_OK;
}
return GST_FLOW_ERROR;
}
int video_transcoding_setSinkCallback(HANDLE *handle, video_transcoding_callback video_transcoding_pulldata_callback) {
//GstSample* sample = NULL;
/* chech input param */
if (NULL == handle) {
GST_ERROR("%s invalid param!\n", __func__);
return -1;
}
CustomData *data = (CustomData *)handle;
/* get sample from appsink */
//g_signal_emit_by_name (data->app_sink, "pull-sample", &sample);
data->cus_pull_data_callback = video_transcoding_pulldata_callback;
//printf("video_transcoding_pulldata_callback address : %p\n", data->cus_pull_data_callback)
g_signal_connect (data->app_sink, "new-sample", G_CALLBACK (appsink_pull_data_callback), data);
GST_DEBUG("%s get data done\n", __func__);
return 0;
}
int video_transcoding_start(HANDLE *handle) {
GstStateChangeReturn ret;
/* check input param */
if (NULL == handle)
{
GST_ERROR("%s input param error\n", __func__ );
return -1;
}
CustomData *data = (CustomData *)handle;
/* start playing */
ret = gst_element_set_state(data->pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
GST_ERROR("%s Unable to set the pipeline to the PLAYING state.\n", __func__);
gst_object_unref(data->pipeline);
return -1;
}
return 0;
}
int video_transcoding_stop(HANDLE *handle) {
GstStateChangeReturn ret;
/* check input param */
if (NULL == handle)
{
GST_ERROR("%s input param error\n", __func__ );
return -1;
}
CustomData *data = (CustomData *)handle;
ret = gst_element_set_state(data->pipeline, GST_STATE_NULL);
if (ret == GST_STATE_CHANGE_FAILURE) {
GST_ERROR("%s Unable to set the pipeline to the NULL state.\n", __func__);
gst_object_unref(data->pipeline);
return -1;
}
return 0;
}
void send_eos_event_to_app (GstElement* sink, CustomData *data){
printf("%s we got eos event\n",__func__);
data->cus_pull_data_callback(NULL, -1);
return;
}