GStreamer第一阶段的简单总结

如何使用Gstreamer实现RTMP推流功能? 朋友小王需要在jetson平台上将相机输出的图像转发到RTMP流媒体服务器,虽然基于成熟的FFmpeg架构能够实现,但是由于前者在jetson平台上的硬件加速功能支持不是很好,导致程序跑起来后CPU占用率异常高,直接影响到了主要业务,因此希望有另外一种方法来实现。本文将基于Gstreamer来实现RTMP推流功能,顺便讲解下RTSP推流服务如何搭建。GStreamer 是一个开源的多媒体框架,用户可以在其中构建图形(管道),处理各种媒体用例:视频播放、流媒体、图像处理、视频编辑等。 阅读详情

前言

因为涉及很多细节的GStreamer官方论坛有详细解链接: GStreamer官网,这里不做说明,以下只是涉及到个人的理解和认知,方便后续的查阅。


个人的总结

1)了解pipeline的使用,简单说就是通道的概念, 因为涉及到数据的复用和拓展, 里面有一个filter(tee插件), 通过tee和后续拓展
的绑定关系复用数据,很像许多Soc编解码芯片中的vpss通道的概念。 数据复用为视频编码通道, 数据jpeg通道以及对数据处理的通道。

2)重点是去了解Pad Templates 中的Availability,这个关系到link的方式,另外关注Element Properties的属性设置。

3)工作使用最多的不是作为播放器,而是将采集数据获取后做后续的图像处理,因此以下是从v4l2src插件中获取到数据的方式:
4)GStreamer复用的拓展性导致其复杂性, 还涉及到glib的学习,因此学习成本还是比较高的, 但现主流的流媒体芯片或边缘化芯片大部分都用到GStreamer,
比如NVIDIA和瑞芯微,特别是瑞芯微直接将GStreamer作为框架,增加自己的硬编解码。
5) GStreamer的许多控件非常适合拓展, 比如推流rtp/rtsp/rtmp/webrtc开发非常快,但走API这套的调试以及对API的理解和学习周期比较长。
这里只花了3天时间学习, 做一个记录。

v4l2src插件的简单使用

/**
 *			
	@brief  实现从v4l2src中拿到数据,方便后续数据的操作,比如用作重新硬编解码用

	appsink关键属性: emit-signals默认是关闭
		
 emit-signals        : Emit new-preroll and new-sample signals
                        flags: readable, writable
                        Boolean. Default: false

   
 Element Signals:
   "eos" :	void user_function (GstElement* object,
								gpointer user_data);
   "new-preroll" :	GstFlowReturn user_function (GstElement* object,
												 gpointer user_data);
   "new-sample" :  GstFlowReturn user_function (GstElement* object,
												gpointer user_data);
 
 Element Actions:
   "pull-preroll" :  GstSample * user_function (GstElement* object);
   "pull-sample" :	GstSample * user_function (GstElement* object);
   "try-pull-preroll" :  GstSample * user_function (GstElement* object,
													guint64 arg0);
   "try-pull-sample" :	GstSample * user_function (GstElement* object,
												   guint64 arg0);
 */
#include <unistd.h>
#include <gst/gst.h>
#include <pthread.h>

#include <time.h>
#include <sys/time.h>

#define		TEST_MODULE

//#define VIDEO_X_RAW

typedef struct ExtraCtrl_
{
	int s32brightness;
	int s32Contrast;
	int s32Saturation;
	int s32Hue;
	int s32Sharpness;
	
}Extra_Ctrl_S;

/*
*		@brief 设置ISP图像质量,主要是图像增强类
		@prams[in] source  - SRC plugin			extraCtrl - 外部的图像喜好
		@return  null

		@test:
			1. 通过inspect查看插件支持的图像属性有哪些
*/
void setCameraQuality(GstElement *source, const Extra_Ctrl_S *extraCtrl)
{
		
		g_object_set(source, "do-timestamp", TRUE, NULL);
#if 0
	  GstStructure* extraCtrls = gst_structure_new("logitech_controls",
	  											   "brightness", extraCtrl->s32brightness,
	  											   "contrast", G_TYPE_INT, extraCtrl->s32Contrast,
	  											   "saturation", G_TYPE_INT, extraCtrl->s32Saturation,
	  											   "hue", G_TYPE_INT, extraCtrl->s32Hue,
	  											   "sharpness", G_TYPE_INT, extraCtrl->s32Sharpness,
												   
												 //  "focus_auto", G_TYPE_BOOLEAN, FALSE,
												 //  "white_balance_temperature_auto", G_TYPE_BOOLEAN, FALSE,
												 //  "white_balance_temperature", G_TYPE_INT, 3500,
												   NULL);
#else 

	/*gst-1.0 无sharpness
		默认值 brightness 0, hue 0 contrast 36 saturation:54

	*/
	int brightness = 1, hue = 1, contrast = 1, saturation = 1;
	g_object_get(source , "brightness", &brightness, "hue", &hue, "contrast", &contrast, "saturation", &saturation, NULL);

	g_print("get: brightness:%d, hue:%d, contrast:%d, saturation:%d\n", brightness, hue, contrast, saturation);

	
#if 1
	int setBrightness = 0;
	GstStructure* extraCtrls = gst_structure_new("logitech_controls",
												"brightness", G_TYPE_INT, brightness ,
												"hue", G_TYPE_INT, hue,
                                                 "contrast", G_TYPE_INT, contrast,
                                                 "saturation", G_TYPE_INT, saturation,//160,
                                                 NULL);
  g_object_set(source, "extra-controls" , extraCtrls, NULL);
 #endif      
#endif 
	
	g_object_get(source , "brightness", &brightness, "hue", &hue, "contrast", &contrast, "saturation", &saturation, NULL);

	g_print("get: brightness:%d, hue:%d, contrast:%d, saturation:%d\n", brightness, hue, contrast, saturation);
}

TEST_MODULE void *ispCtlProc(void *arg)
{
	while(1)
	{
		GstElement * element = (GstElement*)arg;
		if(element)
		{
			static int rec = 0;
			g_print("rec:%d\n", rec++);
			Extra_Ctrl_S s;memset(&s, 0, sizeof(s));
			static int count = 0;
			s.s32brightness = count++ % 255;
			s.s32Contrast = 128;
			s.s32Saturation = 128;
			s.s32Hue = 128;
			s.s32Sharpness = 128;
			setCameraQuality(element, &s);
		}

		usleep(1000*100);
	}

	return NULL;
}

/*
		@brief  原始数据回调
*/
GstFlowReturn cb_appsink_new_sample(GstElement* appsink, gpointer user_data)

{
     // LOG_INFO_MSG ("cb_appsink_new_sample called, user data: %p", user_data);
#if 0
     SinkPipeline* sp = reinterpret_cast<SinkPipeline*> (user_data);
#endif
     GstSample* sample = NULL;
     GstBuffer* buffer = NULL;
     GstMapInfo map;
     const GstStructure* info = NULL;
     GstCaps* caps = NULL;
     GstFlowReturn ret = GST_FLOW_OK;
     int sample_width = 0;
     int sample_height = 0;
     // equals to gst_app_sink_pull_sample (GST_APP_SINK_CAST (appsink), sample);
     g_signal_emit_by_name (appsink, "pull-sample", &sample, &ret);
     if (ret != GST_FLOW_OK) {
         g_print ("can't pull GstSample.\n");
         return ret;
     }
     if (sample) 
     {
         buffer = gst_sample_get_buffer (sample);
         if ( buffer == NULL ) {
             g_print ("get buffer is null\n");
             goto exit;
         }
        gboolean mapret= gst_buffer_map (buffer, &map, GST_MAP_READ);
		if(mapret == FALSE)
		{
			goto exit;
		}
         /**/
         caps = gst_sample_get_caps (sample);
         if ( caps == NULL ) {
             g_print ("get caps is null\n");
             goto exit;
         }
         info = gst_caps_get_structure (caps, 0);
         if ( info == NULL ) {
             g_print ("get info is null\n");
             goto exit;
         }
         // -------- Read frame and convert to opencv format --------
         // convert gstreamer data to OpenCV Mat, you could actually
         // resolve height / width from caps...
         gst_structure_get_int (info, "width", &sample_width);
         gst_structure_get_int (info, "height", &sample_height);
        
         g_print("format:%s, width:%d, height:%d\n",  gst_structure_get_string(info, "format"), sample_width, sample_height);

         if(NULL == map.data)
         {
				g_print("appsink buffer data empty\n");
				goto exit;
         }	
		static struct timeval s, e;
         static int flag = 1;
         if(flag)
         {
         	
         	 gettimeofday(&s, 0); e = s;
			flag = 0;

         }
         gettimeofday(&e, 0);
         static int frameNo = 0;
         frameNo++;
         unsigned int timespan = (e.tv_sec - s.tv_sec )*1000*1000 + e.tv_usec - s.tv_usec;
         if(timespan >= 1000000)
         {
         
		 	g_print("frame siz:%d, max_siz:%d, %02d FPS\n", map.size, map.maxsize, frameNo);
			frameNo = 0;
			gettimeofday(&s, 0);
		 }
         //FILE *fp = 
#if 0
         // customized user action
         {
             // init a cv::Mat with gst buffer address: deep copy
             // sometime you may got a empty buffer
             if (map.data == NULL) {
                 LOG_ERROR_MSG("appsink buffer data empty\n");
                 return GST_FLOW_OK;
             }
             cv::Mat img (sample_height, sample_width, CV_8UC3,
                             (unsigned char*)map.data, cv::Mat::AUTO_STEP);
             img = img.clone();
             // redirection outside operation: for decoupling use
             if (sp->m_putDataFunc) {
                 sp->m_putDataFunc(std::make_shared<cv::Mat> (img),
                     sp->m_putDataArgs);
             } else {
                 goto exit;
             }
         }
 #endif
 exit:
     if (buffer) {
         gst_buffer_unmap (buffer, &map);
     }
     if (sample) {
         gst_sample_unref (sample);
     }
     return GST_FLOW_OK;
 	}

}
/*
	@brief 创造线程设置图像数据, TEST 模块
*/
TEST_MODULE int ispInit(GstElement *source)
{
	int ret = 0;
	pthread_t ispControlThread;
  	pthread_create(&ispControlThread, 0, ispCtlProc, (void *)source);
}


int main(int argc, char *argv[]) 
{
	
  GstElement *pipeline, *source;
  GstElement  *converter,*appsink;											
  
  GstBus *bus;
  GstMessage *msg;

 
 
  /* Initialize GStreamer */
  gst_init (&argc, &argv);

  /* Create the elements */
  /*src(always)*/
  source = gst_element_factory_make ("v4l2src", "v4l2src");
  
  converter= 	gst_element_factory_make("videoconvert","converter01");
  //sink
  appsink = gst_element_factory_make("appsink", "appsink");

  /* Create the empty pipeline */
  pipeline = gst_pipeline_new ("test-pipeline");

  if ( !pipeline || !source || !appsink ) {
    g_printerr ("Not all elements could be created.\n");
    return -1;
  }

//	set appsink 
g_object_set(appsink, "emit-signals", TRUE, NULL);

  g_signal_connect (appsink, "new-sample", G_CALLBACK (cb_appsink_new_sample), NULL);

 

  /* Link all elements that can be automatically linked because they have "Always" pads */
  gst_bin_add_many (GST_BIN (pipeline), source, converter, appsink, NULL);

  
//  TEST_MODULE ispInit(source); 

/*
	@note:
		1. 需要在gst_bin_add_many之后调用
		先需要知道摄像头支持的Pixel Format,通过命令可查,
		v4l2-ctl --list-formats-ext --device /dev/video0可以查看支持的格式只有YUYV和MJPG,另外可以查看到具体的分辨率和帧率
*/
#if defined(VIDEO_X_RAW)
 GstCaps *caps = gst_caps_new_simple ("video/x-raw", 
       //  "format", G_TYPE_STRING, "YV12", //<<< IF THIS IS SET TO ARGB (THE FORMAT I WANT IT FAILS ON LINKING) 
      // 	"format", G_TYPE_STRING, "NV21",
         "framerate", GST_TYPE_FRACTION, 25, 1, 
        // "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, 
         "width", G_TYPE_INT, 320, 
         "height", G_TYPE_INT, 240, 
         NULL); 
#else
		 GstCaps *caps = gst_caps_new_simple ("video/mpeg", 
      	 "mpegversion", G_TYPE_INT, 2,
      	 "systemstream", G_TYPE_BOOLEAN, TRUE,
         NULL); 

#endif 
	
	gst_element_link_filtered(source, converter, caps);
	gst_caps_unref(caps);

	

	/*这里做了转换,需要逐一连接*/
	gst_element_link(source, converter);
	gst_element_link(converter, appsink);

	

  /* Start playing the pipeline */
  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  /* Wait until error or EOS */
  bus = gst_element_get_bus (pipeline);
  msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);

if(msg != NULL)
{
	GError *err;
	gchar *debug_info;

	switch (GST_MESSAGE_TYPE (msg)) {
	case GST_MESSAGE_ERROR:
	gst_message_parse_error (msg, &err, &debug_info);
	g_printerr ("Error received from element %s: %s\n",
	GST_OBJECT_NAME (msg->src), err->message);
	g_printerr ("Debugging information: %s\n",
	debug_info ? debug_info : "none");
	g_clear_error (&err);
	g_free (debug_info);
	break;
	
	case GST_MESSAGE_EOS:
	g_print ("End-Of-Stream reached.\n");
	break;
	
	default:
	/* We should not reach here because we only asked for ERRORs and EOS */
	g_printerr ("Unexpected message received.\n");
	break;
	}
	gst_message_unref (msg);
}else
{
	g_printerr("error.");
}
	

  /* Release the request pads from the Tee, and unref them */
 
  
  /* Free resources */
  if (msg != NULL)
    gst_message_unref (msg);
  gst_object_unref (bus);
  gst_element_set_state (pipeline, GST_STATE_NULL);

  gst_object_unref (pipeline);
  return 0;
}


编译:

cmake_minimum_required(VERSION 3.0)
project(test_project)

find_package(PkgConfig REQUIRED)
pkg_search_module(GST1 REQUIRED gstreamer-1.0)#查找到gstreamer-1.0 并用GST1代表,GST1自己定义

add_executable(a.out v4l2src_appsink.c)

target_include_directories(a.out PRIVATE ${GST1_INCLUDE_DIRS})
target_link_libraries(a.out ${GST1_LIBRARIES} -lpthread)

message("GST1_LIBRARIES
${GST1_LIBRARIES}
")

运行结果:

format:YUY2, width:1920, height:1080
format:YUY2, width:1920, height:1080
format:YUY2, width:1920, height:1080
format:YUY2, width:1920, height:1080
format:YUY2, width:1920, height:1080
format:YUY2, width:1920, height:1080
format:YUY2, width:1920, height:1080
frame siz:4147200, max_siz:4147200, 07 FPS
format:YUY2, width:1920, height:1080
format:YUY2, width:1920, height:1080
format:YUY2, width:1920, height:1080
format:YUY2, width:1920, height:1080

gstreamer推流SRT协议视频,VLC播放SRT视频(一) 阅读详情

相关推荐

使用Gstreamer进行RTSP推流

使用vlc打开需要配置sdp文件,前面有提到过:https://notes.z-dd.online/2024/06/08/RTP%E8%A7%86%E9%A2%91%E6%B5%81%E7%9A%84SDP%E6%96%87%E4%BB%B6/链接: https://notes.z-dd.online/2024/06/11/%E4%BD%BF%E7%94%A8Gstreamer%E8%BF%9B%E8%A1%8CRTSP%E6%8E%A8%E6%B5%81/使用Gstreamer进行RTSP推流。

DD'Notes 4672

基于gstreamer的rtsp推流 (USB摄像头)

​一、配置gstreamer环境 二、安装gstreamer-rtsp-server 三、读取usb摄像头并推rtsp流 ​

zhangyue123的博客 1万+

【GStreamer】gstreamer在linux下的简单使用

对于Ubuntu系统,使用如下命令可以下载gstreamer([Installing on Linux](https://gstreamer.freedesktop.org/documentation/installing/on-linux.html?gi-language=c))

weixin_42877471的博客 4339

Gstreamer中pad的链接

pad相当与element的接口,那么element间的连接,实质上就是pad间的连接,所以这里总结下pad的链接。

技术笔记 1859

GStreamer 简单的播放控制实例

本文介绍了使用GStreamer框架实现简单视频播放器的方法。通过命令行工具ffplay和gst-launch-1.0演示了网络视频播放效果,重点展示了用GStreamer API编写的4行核心代码实现播放器功能。代码使用playbin元素构建播放管道,通过总线获取播放状态消息,实现了基本的播放控制。文章解释了GStreamer的关键概念如管道、总线和消息机制,并对比了不同播放方式的优缺点。该示例为GStreamer编程提供了简洁的入门参考,展示了如何通过简单代码实现多媒体播放功能。

hjjdebug的专栏 1330

opencv+gstreamer拉流

使用OpenCV结合Gstreamer实现RTSP流硬件解码

hello_dear_you的博客 1万+

jetson tx2 使用opencv的gstreamer

原因:由于刷机jetson tx2的jetpack安装的openCV4Tegra不支持gstreamer所以需要卸载jetpack安装的opencv安装支持gstreamer的opencv版本 安装步骤: 删除所有由JetPack(或OpenCV4Tegra)安装的旧opencv内容 sudo apt-get purge libopencv* 2.删除其他未使用的apt包(可选操作项) s...

xinxin_Tian的博客 3441

从零开始成为GStreamer专家——基于Windows的GStreamer从源码下载、编译到开发

写一个自己的Hello,Word! 代码下载 一、GStreamer下载 到Sign up · GitLabfreedesktop.org GitLab loginhttps://gitlab.freedesktop.org/users/sign_up上注册一个用户。 在PC机上用ssh-keygen -t rsa -C "yourname@yourhost.com"生成id_rsa.pub值,把这个值贴到下面Key框里,如果这一步不会,请搜索参考ssh-keygen的用法。 git clo...

intel1985的专栏 1万+

GStreamer 1 简单看看

GStreamer的核心是pipeline,这块很强,很适合现在对视频流进行连续处理。下面的sources,sinks,containers,decoder就是一个个的插件,应该一个插件就是一个so动态链接库。我在想可能它家的机制在商业上也有一定好处,比如说插件之间可以比较独立,而且可以闭源发大财,所以现在很多大厂都基于它在搞。Comprehensive Core Library:核心库,提供插件API框架,应该涉及到线程管理,任务调度,同步,调试系统等等。从它家的主页来看,整个GS大概有以下4个部分。

fanged的专栏 708

【GStreamer】GstElement的简单分析

GstElement是GStreamer中最核心的构建块,代表一个功能单元,用于处理多媒体数据流。每个元素都有特定的功能,例如source elements用于生成数据(摄像头读取视频)、filter elements用于处理数据(编解码、格式转换等)、sink elements用于接收数据(视频渲染到屏幕),GStreamer通过组合不同的GstElement来构建复杂的多媒体处理流程

weixin_42877471的博客 919

【GStreamer】GstPad的简单分析

GstPad用于在pipeline中连接不同的元素(GstElement),可以将pad理解为一个元素的连接点或者端口,数据通过这些连接点在元素之间流动。pad的核心作用是,数据流动的接口,可以是某一个元素的数据接收端口,也可以是某一个元素的数据发送端口。使用capabilities(Caps)描述pad可以处理的数据类型(如视频、音频、具体的编码格式等),必须确保两个pad的caps可以兼容。

weixin_42877471的博客 1033

Gstreamer概述

Gstreamer概述

jinking01的专栏 4258

【GStreamer】基于gstreamer的简单h264-rtmp推流器

基于gstreamer的简单h264-rtmp推流器

weixin_42877471的博客 916

GStreamer安装——Android

您的设备上现在应该可以使用名为“Android教程1”的新应用程序,并带有GStreamer徽标。项目现在可以运行了。使用的Gradle构建系统中的NDK支持 Android Studio仍处于测试阶段,因此推荐的构建方式 使用GStreamer SDK仍然是使用“ndk-build”。开发机器是您开发Android应用程序的地方,然后您将其部署在目标机器上,目标机器显然应该是Android设备。该项目应该自动构建,一旦成功完成,应该可以运行运行>运行“教程X”的教程,前提是连接了设备并启用了USB调试。

FREEDOM_X的专栏 3321

[linux]gstreamer笔记

gstreamer的编程知识,gstreamer作为linux下的多媒体应用以其优良的构思得到了极大的关注,这种编程思想和方法是非常优秀的. 对于gstreamer编程,我们一般的做法如下:  1.用gst_element_factory_find找到一个工厂对象(factory object),比如说"mad"是用于 mp3的工厂对象,工厂对象主要用于生成元件. 我们这里用mad工厂主要是要mp

gundomcz的专栏 5149

从零到一:使用阿里云百炼搭建企业级 AI Agent 完全实战手册(2026超详细版)

本文是一份企业级AI Agent构建手册,主要面向企业技术负责人、AI产品经理和后端开发工程师。手册详细介绍了如何在阿里云百炼平台上搭建企业智能体应用的全流程,包括环境准备、知识库构建、智能体创建、工具集成、工作流编排等核心环节。重点内容包括: 准备工作:阿里云账号注册、百炼服务开通、免费额度领取、API Key管理 知识库构建:文档格式要求、内容优化原则、RAG系统搭建 智能体开发:Agent创建、工具集成、复杂业务工作流设计 生产部署:多渠道接入、监控运维、安全治理 成本控制:Token预算管理、选型决

u010323587的专栏 1075

GStreamer安装——Windows

你 将需要DVD刻录软件),将文件挂载到虚拟DVD设备中 (您将需要DVD虚拟化软件)或解压文件 是一个常规的压缩文件(您将需要解压软件 了解ISO格式)。在Windows 8和Windows 10上,可能需要在安装后注销并重新登录到您的帐户,以便Visual Studio获取新定义的环境变量。每个版本的Visual Studio都会发生这种情况,而运行时DLL是 每个版本的Visual Studio都不同。此属性表包含标头和库所在的目录,以及编译器和链接器的必要选项,因此您无需更改项目中的任何其他内容。

FREEDOM_X的专栏 4689

(NOI)打表骗分过样例,暴力枚举出奇迹1.8.22题解

                                                                                                                                                           48818 B题解 #include &lt;iostream&gt; u...

Owen20071026的博客 3127

GStreamer——教程——Android教程1:Link against GStreamer

它展示了,除了Java和C之间的互连(遵循标准的JNI程序)之外,将GStreamer支持添加到Android应用程序并不比将其添加到桌面应用程序复杂。它加载libgstreamer_android.so,其中包含所有GStreamer方法,以及libtutorial-1.so,其中包含本教程的C部分,如下所述。此目录包含通常的Android NDK结构:一个用于Java代码的src文件夹,一个用于C代码的jni文件夹和一个用于UI资源的res文件夹。确保在运行时此方法是可访问的是你的责任。

FREEDOM_X的专栏 1409

基于Codex平台构建AI技能:抖音爆款分析与带货视频生成实战

这次我们来看一个基于 Codex 平台构建 AI 技能(Skill)的实战项目。核心目标很直接:利用 AI 能力,自动化分析抖音平台的爆款内容,并生成可用于带货的视频。这听起来像是内容创作领域的“自动化流水线”,它解决的核心痛点是:人工分析爆款、撰写脚本、制作视频的效率瓶颈。 对于技术开发者和内容创作者而言,这个项目的价值在于将“内容策略分析”和“视频内容生成”两个环节通过 Codex 的 Skill 机制串联起来,形成一个可定制、可批量执行的自动化工作流。我们最关心的几个问题包括:这个方案的技术门槛高不高

weixin_29054399的博客 484

PE启动盘结合360杀毒离线版:深度清除顽固病毒实战指南

在系统安全维护领域,离线查杀是一种在独立于宿主操作系统的环境下进行病毒扫描和清除的核心技术。其原理是通过一个预启动环境(如PE)加载杀毒软件,从而绕过可能已被感染或受保护的系统核心,直接访问和扫描磁盘文件。这项技术的核心价值在于能够彻底处理那些在正常系统运行时难以清除的深度隐藏病毒、Rootkit和驱动级木马。典型的应用场景包括应对勒索软件、顽固感染型病毒以及系统启动项被劫持等情况。本文以“PE启动盘”和“360杀毒离线版”这一经典组合为例,详细解析了从工具选型、环境部署到深度查杀策略的全流程,为系统维护人

weixin_29741447的博客 408

gstreamer的安装和简单的mp3编写

document.body.oncopy = function() { if (window.clipboardData) { set

Oanna的专栏 1490

百度新闻评论内容抓取

通过抓包分析发现,百家号手机app端文章评论内容接口为:https://ext.baidu.com/api/comment/v1/comment/getlist?appid=101&sid=1008524_2-1010050_1-1007549_23033-1007550_23035&cuid=01B5EAF73E8A83BB842BE04E4FB6C656|23200201047...

codingforhaifeng的博客 2420
上一篇: c++回调
下一篇: GStreamer porting-list-0.11.txt
帘下有白绿
博客等级 码龄12年 165粉丝 44原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值