【音视频 | opus】opus编解码库(opus-1.4)详细介绍以及使用——附带解码示例代码

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍opus编解码库(opus-1.4)的使用🍭
😎金句分享😎:🍭🍭

opus和Ogg相关系列文章:
1、RFC3533 :Ogg封装格式版本 0(The Ogg Encapsulation Format Version 0)
2、Ogg封装格式详解——包含Ogg封装过程、数据包(packet)、页(page)、段(segment)等
3、libogg库详解介绍以及使用——附带libogg库解析.opus文件的C源码
4、RFC7845:Opus音频编解码器的Ogg封装(Ogg Encapsulation for the Opus Audio Codec)
5、opus编解码库(opus-1.4)详细介绍以及使用——附带解码示例代码
6、opus编码的Ogg封装文件详解

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
  • 🎄二、opus 库编译
    • ✨2.1 编译环境
    • ✨2.2 opus-1.4 库编译
    • ✨2.3 opus_demo 使用
  • 🎄三、opus-1.4 库函数介绍
    • ✨3.1 opus 编码相关库函数
    • ✨3.2 opus 解码相关库函数
  • 🎄四、opus 解码流程及源码
  • 🎄五、总结


在这里插入图片描述

🎄一、概述

Opus是一款完全开放、免版税、高度通用的音频编解码器。Opus在互联网上的交互式语音和音乐传输方面无与伦比,但也适用于存储和流媒体应用。它被互联网工程任务组(IETF)标准化为 RFC 6716,它结合了Skype的SILK编解码器和Xiph.Org的CELT编解码器的技术。

Opus可以处理广泛的音频应用程序,包括IP语音、视频会议、游戏内聊天,甚至远程现场音乐表演。它可以从低比特率的窄带语音扩展到非常高质量的立体声音乐。支持的功能包括:

  • 比特率从6 kb/s到510 kb/s

  • 采样率从8kHz(窄带)到48kHz(全频带)

  • 帧大小从2.5毫秒到60毫秒

  • 支持恒定比特率(CBR)和可变比特率(VBR)

  • 从窄带到全频带的音频带宽

  • 支持语音和音乐

  • 支持单声道和立体声

  • 最多支持255个通道(多流帧)

  • 可动态调整的比特率、音频带宽和帧大小

  • 良好的丢失鲁棒性和丢包隐藏性(PLC)

  • 浮点和定点实现

需要了解更多opus的内容,可以到opus官网:https://www.opus-codec.org 。

API手册:Opus audio codec (RFC 6716): API and operations manual 1.1.3

在这里插入图片描述

🎄二、opus 库编译

下载 opus 库,点击链接:https://www.opus-codec.org/downloads/,本文下载的是opus-1.4.tar.gz

✨2.1 编译环境

$ uname -a
Linux ubuntu 4.4.0-128-generic #154~14.04.1-Ubuntu SMP Fri May 25 14:58:51 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.5 LTS
Release:        14.04
Codename:       trusty$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04.4' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4) 

✨2.2 opus-1.4 库编译

tar zxvf opus-1.4.tar.gz
cd opus-1.4/
./configure --prefix=`pwd`/result_gcc
make && make install

✨2.3 opus_demo 使用

opus-1.4 库编译之后,在opus-1.4/.libs目录会出现一个名为 opus_demo 的可执行程序,该程序的源码在opus-1.4/src/opus_demo.c 路径。

opus_demo 是演示 opus-1.4 库 API 函数的一个demo,功能是将PCM音频文件编码成opus数据,或者将opus数据解码成PCM文件。注意,它编码出来的opus并不能直接使用VLC播放,因为只是一些数据包,并没有封装成VLC可识别的音频文件。

使用下面命令进行opus编码,这里将48000Hz-16bit-2ch-ChengDu.pcm作为输入文件,输入采样率48000、通道数2、510000的比特流,最后编码输出 opus_demo.out 文件:

./opus_demo -e audio 48000 2 510000 48000Hz-16bit-2ch-ChengDu.pcm opus_demo.out

在这里插入图片描述

使用下面命令可以将上面的输出文件opus_demo.out还原回PCM音频文件opus_demo_dec.pcm

./opus_demo -d 48000 2 opus_demo.out opus_demo_dec.pcm

在这里插入图片描述

在这里插入图片描述

🎄三、opus-1.4 库函数介绍

opus-1.4 的库函数都在 opus.h 头文件中,库函数总共分为下面6个部分,在头文件搜索defgroup可以依次跳到各个部分开头,现在只有前三个部分。:

  • opus_encoder :编码相关
  • opus_decoder :解码相关
  • opus_repacketizer :重新打包器相关
  • opus_multistream
  • opus_libinfo
  • opus_custom

每个API函数在 opus.h 头文件中都要详细的英文解释,部分函数还有实例代码,如果还有不懂,就看opus_demo.c的代码,也有助于清楚怎么使用。

下面摘取个别常用的库函数:

✨3.1 opus 编码相关库函数

int opus_encoder_get_size(int channels);
功能:获取一个 OpusEncoder 结构体的大小
参数:channels:通道数,必须12;
返回值:以字节为单位的大小//---------------------------------------
OpusEncoder *opus_encoder_create(opus_int32 Fs, int channels, int application, int *error);
功能:分配并初始化编码器状态
参数:Fs:采样率,必须是 8000, 12000, 16000, 24000, 48000 之一;channels:声道数,12;application:OPUS_APPLICATION_VOIP 或 OPUS_APPLICATION_AUDIO 或 PUS_APPLICATION_RESTRICTED_LOWDELAYerror:输出参数,错误码;
返回值:成功返回编码器指针,失败返回NULL//---------------------------------------
int opus_encoder_init(OpusEncoder *st, opus_int32 Fs, int channels, int application);
功能:初始化之前分配的编码器状态;//---------------------------------------
opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes);
功能:编码一个Opus帧;//---------------------------------------
opus_int32 opus_encode_float(OpusEncoder *st, const float *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes);
功能:从浮点输入编码一个Opus帧//---------------------------------------
void opus_encoder_destroy(OpusEncoder *st);
功能:释放 opus_encoder_create 分配的 OpusEncoder 指针;//---------------------------------------
int opus_encoder_ctl(OpusEncoder *st, int request, ...);
功能:在Opus编码器上执行控制功能。

✨3.2 opus 解码相关库函数

int opus_decoder_get_size(int channels);
功能:获取一个 OpusDecoder 结构体的大小
参数:channels:通道数,必须12;
返回值:以字节为单位的大小//---------------------------------------
OpusDecoder *opus_decoder_create(opus_int32 Fs,int channels,int *error);
功能:分配并初始化解码器状态
参数:Fs:采样率,必须是 8000, 12000, 16000, 24000, 48000 之一;channels:声道数,12;error:输出参数,错误码;
返回值:成功返回解码器指针,失败返回NULL//---------------------------------------
int opus_decoder_init(OpusDecoder *st, opus_int32 Fs, int channels);
功能:初始化之前分配的解码器状态;//---------------------------------------
int opus_decode(OpusDecoder *st, const unsigned char *data, opus_int32 len,opus_int16 *pcm, int frame_size, int decode_fec);
功能:解码一个Opus数据包。//---------------------------------------
int opus_decode_float(OpusDecoder *st, const unsigned char *data, opus_int32 len,float *pcm, int frame_size, int decode_fec);
功能:用浮点输出解码一个Opus数据包。//---------------------------------------
int opus_decoder_ctl(OpusDecoder *st, int request, ...);
功能:在Opus解码器上执行控制功能。//---------------------------------------
void opus_decoder_destroy(OpusDecoder *st);
功能:释放 opus_decoder_create分配的 OpusDecoder 指针;//---------------------------------------
int opus_packet_parse(const unsigned char *data,opus_int32 len, unsigned char *out_toc,const unsigned char *frames[48],opus_int16 size[48],int *payload_offset);//---------------------------------------  
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_bandwidth(const unsigned char *data) OPUS_ARG_NONNULL(1);/** Gets the number of samples per frame from an Opus packet.* @param [in] data <tt>char*</tt>: Opus packet.*                                  This must contain at least one byte of*                                  data.* @param [in] Fs <tt>opus_int32</tt>: Sampling rate in Hz.*                                     This must be a multiple of 400, or*                                     inaccurate results will be returned.* @returns Number of samples per frame.*/
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_samples_per_frame(const unsigned char *data, opus_int32 Fs) OPUS_ARG_NONNULL(1);/** Gets the number of channels from an Opus packet.* @param [in] data <tt>char*</tt>: Opus packet* @returns Number of channels* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type*/
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_channels(const unsigned char *data) OPUS_ARG_NONNULL(1);/** Gets the number of frames in an Opus packet.* @param [in] packet <tt>char*</tt>: Opus packet* @param [in] len <tt>opus_int32</tt>: Length of packet* @returns Number of frames* @retval OPUS_BAD_ARG Insufficient data was passed to the function* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type*/
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_frames(const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1);/** Gets the number of samples of an Opus packet.* @param [in] packet <tt>char*</tt>: Opus packet* @param [in] len <tt>opus_int32</tt>: Length of packet* @param [in] Fs <tt>opus_int32</tt>: Sampling rate in Hz.*                                     This must be a multiple of 400, or*                                     inaccurate results will be returned.* @returns Number of samples* @retval OPUS_BAD_ARG Insufficient data was passed to the function* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type*/
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_samples(const unsigned char packet[], opus_int32 len, opus_int32 Fs) OPUS_ARG_NONNULL(1);/** Gets the number of samples of an Opus packet.* @param [in] dec <tt>OpusDecoder*</tt>: Decoder state* @param [in] packet <tt>char*</tt>: Opus packet* @param [in] len <tt>opus_int32</tt>: Length of packet* @returns Number of samples* @retval OPUS_BAD_ARG Insufficient data was passed to the function* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type*/
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_nb_samples(const OpusDecoder *dec, const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);/** Applies soft-clipping to bring a float signal within the [-1,1] range. If* the signal is already in that range, nothing is done. If there are values* outside of [-1,1], then the signal is clipped as smoothly as possible to* both fit in the range and avoid creating excessive distortion in the* process.* @param [in,out] pcm <tt>float*</tt>: Input PCM and modified PCM* @param [in] frame_size <tt>int</tt> Number of samples per channel to process* @param [in] channels <tt>int</tt>: Number of channels* @param [in,out] softclip_mem <tt>float*</tt>: State memory for the soft clipping process (one float per channel, initialized to zero)*/
OPUS_EXPORT void opus_pcm_soft_clip(float *pcm, int frame_size, int channels, float *softclip_mem);

在这里插入图片描述

🎄四、opus 解码流程及源码

opus 解码流程:
1、创建 opus 解码器:opus_decoder_create;
2、配置 opus 解码器:opus_decoder_ctl;
3、取出 opus 包;
4、使用 opus_decode 函数对 opus 包解码;

示例代码:

/*** @file opusDec.h* @author wkd_007* @date 2023-10-27 15:47:38*/
#ifndef	__OPUS_DEC_H__
#define __OPUS_DEC_H__#include "opus/opus.h"#define MAX_OPUS_DEC_FRAME  48000   // opus解码最大采样点个数,如果个数时间小于120ms,可能停止解码,这里设置1000ms的个数
#define OPUS_DEC_CHANNELS   2
class COpusDec
{
public:COpusDec();~COpusDec();int CreateOpusDecoder(int sampleRate, int channels);int OpusDecode(unsigned char* in_data, int in_len, short *out_buf);private:OpusDecoder *decoder;   // opus 解码器指针int sample_rate;        // 采样率int channel_num;        // 通道数
};
#endif// __OPUS_DEC_H__
/*** @file 	opusDec.cpp* @author 	wkd_007* @brief 	opus 解码* @date 	2023-10-27 15:38:43*/#include <stdio.h>
#include "opusDec.h"COpusDec::COpusDec()
{decoder = NULL;sample_rate = 0;channel_num = 0;
}COpusDec::~COpusDec()
{sample_rate = 0;channel_num = 0;if(decoder){opus_decoder_destroy(decoder);decoder = NULL;}
}int COpusDec::CreateOpusDecoder(int sampleRate, int channels)
{int err = 0;decoder = opus_decoder_create(sampleRate, channels, &err);if(err != OPUS_OK || decoder == NULL){printf("[%s %d]err=%d decoderIsNULL=%d\n",__FILE__,__LINE__,err, decoder == NULL);return -1;}opus_decoder_ctl(decoder, OPUS_SET_LSB_DEPTH(16));sample_rate = sampleRate;channel_num = channels;return err;
}int COpusDec::OpusDecode(unsigned char* in_data, int in_len, short *out_buf)
{if(decoder == NULL)return -1;int frame_size = opus_decode(decoder, in_data, in_len, out_buf, MAX_OPUS_DEC_FRAME, 0);if (frame_size < 0){printf("[%s %d] frame_size=%d in_len=%d\n",__FILE__,__LINE__, frame_size,in_len);return frame_size;}return frame_size;
}

在这里插入图片描述

🎄五、总结

本文介绍了opus-1.4 库的下载、编译,demo程序的使用,以及简单介绍常用的opus编解码库函数 ,最后给出opus解码示例代码;

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:
从RTP包中分析OPUS码流:https://blog.csdn.net/hclbeloved/article/details/115528990
libopus 实现pcm 编码到opus:https://blog.csdn.net/sinat_27720649/article/details/126530085

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/131312.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第G7周:Semi-Supervised GAN 理论与实战

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f366; 参考文章&#xff1a;365天深度学习训练营-第G7周&#xff1a;Semi-Supervised GAN 理论与实战&#xff08;训练营内部成员可读&#xff09; &#x1f356; 原作者&#xff1a;K同学啊|接…

Centralized Feature Pyramid for Object Detection解读

Centralized Feature Pyramid for Object Detection 问题 主流的特征金字塔集中于层间特征交互&#xff0c;而忽略了层内特征规则。尽管一些方法试图在注意力机制或视觉变换器的帮助下学习紧凑的层内特征表示&#xff0c;但它们忽略了对密集预测任务非常重要的被忽略的角点区…

【论文精读】PlanT: Explainable Planning Transformers via Object-Level Representations

1 基本信息 院校&#xff1a;德国的图宾根大学 网站&#xff1a;https://www.katrinrenz.de/plant 2 论文背景 2.1 现有问题 现在的基于学习的方法使用高精地图和BEV&#xff0c;认为准确的&#xff08;达到像素级的pixel-level&#xff09;场景理解是鲁棒的输出的关键。re…

Java自学第1课:安装JDK+Eclipse

1 引言 在学习前&#xff0c;我想说一句&#xff0c;那就是为什么要学习Java。 每个人的出发点都不同&#xff0c;对于做信息化的工程技术人员来说&#xff0c;java不懂&#xff0c;就没法干项目。 尽管有c和matlab等基础&#xff0c;但java看起来与这些语言都不太一样。 做…

基于单片机的智能饮水机系统

收藏和点赞&#xff0c;您的关注是我创作的动力 文章目录 概要 一、系统设计方案分析2.1 设计功能及性能分析2.2设计方案分析 二、系统的硬件设计3.1 系统设计框图系统软件设计4.1 总体介绍原理图 四、 结论 概要 现在很多学校以及家庭使用的饮水机的功能都是比较单一的&#…

MySQL InnoDB数据存储结构

1. 数据库的存储结构&#xff1a;页 索引结构给我们提供了高效的索引方式&#xff0c;不过索引信息以及数据记录都是保存在文件上的&#xff0c;确切说是存储在页结构中。另一方面&#xff0c;索引是在存储引擎中实现的&#xff0c;MySQL服务器上的存储引擎负责对表中数据的读…

分享68个工作总结PPT,总有一款适合您

分享68个工作总结PPT&#xff0c;总有一款适合您 PPT下载链接&#xff1a;https://pan.baidu.com/s/1juus0gmesBFxJ-5KZgSMdQ?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易。知识付…

C语言----每日五道选择题Day1

1.第一题 1、指出下列代码的缺陷&#xff08; &#xff09;【多选】 float f[10]; // 假设这里有对f进行初始化的代码 for(int i 0; i < 10;) {if(f[i] 0)break; } A: for(int i 0; i < 10;)这一行写错了 B: f是float型数据直接做相等判断有风险 C: f[i]应该是…

[MICROSAR Adaptive] --- autosar官方文档阅读建议

目前互联网上没有太多的 Adaptive AUTOSAR 的学习资料,官方文档是一个很不错的途径。看过官方文档才发现,目前很多关于 Adaptive AUTOSAR 的文章都是官方文档的简化翻译,不如直接看官方文档更全面深入。 1 Adaptive AUTOSAR 文档官方下载地址 https://www.autosar.org/sta…

微信小程序:实现多个按钮提交表单

效果 核心步骤 通过data-type给不同按钮进行设置&#xff0c;便于很好的区分不同按钮执行不同功能 data-type"" 完整代码 wxml <form action"" bindsubmit"formSubmit"><button style"margin-bottom:5%" data-type"pa…

[黑马程序员SpringBoot2]——运维实用篇

目录&#xff1a; 工程打包与运行打包插件Boot工程快速启动&#xff08;Linux版本&#xff09;临时属性配置文件4级分类自定义配置文件多环境开发(yaml版)多环境开发多文件版&#xff08;yaml版&#xff09;多环境开发多文件版&#xff08;properties版&#xff09;多环境分组…

难题来了:分库分表后,查询太慢了,如何优化?

说在前面&#xff1a; 尼恩社群中&#xff0c;很多小伙伴反馈&#xff0c; Sharding-JDBC 分页查询的速度超级慢&#xff0c; 怎么处理&#xff1f; 反馈这个问题的小伙伴&#xff0c;很多很多。 而且这个问题&#xff0c;也是面试的核心难题。前段时间&#xff0c;有小伙伴…

windows 用vs创建cmake工程并编译opencv应用项目生成exe流程简述

目录 前言一、安装opencv&#xff08;1&#xff09;下载&#xff08;2&#xff09;双击安装&#xff08;3&#xff09;环境变量和system文件夹设置 二、打开vs创建项目三、编辑cpp&#xff0c;.h&#xff0c;cmakelist.txt文件&#xff08;1&#xff09;h文件&#xff08;2&…

【Python从入门到进阶】41、有关requests代理的使用

接上篇《40、requests的基本使用》 上一篇我们介绍了requests库的基本使用&#xff0c;本篇我们来学习requests的代理。 一、引言 在网络爬虫和数据抓取的过程中&#xff0c;我们经常需要发送HTTP请求来获取网页内容或与远程服务器进行通信。然而&#xff0c;在某些情况下&…

通过在Z平面放置零极点的来设计数字滤波器

文章来源地址&#xff1a;https://www.yii666.com/blog/393376.html 通过在Z平面放置零极点的来设计数字滤波器 要求&#xff1a;设计一款高通滤波器&#xff0c;用在音频信号处理过程中&#xff0c;滤掉100Hz以下的信号。 实现方法&#xff1a;通过在Z平面放置零极点的来设…

数据结构与算法【02】—线性表

CSDN系列专栏&#xff1a;数据结构与算法专栏 针对以前写的数据结构与算法系列重写(针对文字描述、图片、错误修复)&#xff0c;改动会比较大&#xff0c;一直到更新完为止 前言 通过前面数据结构与算法基础知识我们知道了数据结构的一些概念和重要性&#xff0c;那么本章总结…

【UE 材质】简单的闪闪发光材质

效果 节点 参考视频&#xff1a; https://www.bilibili.com/video/BV1uK411y737/?vd_source36a3e35639c44bb339f59760641390a8

MySQL(8):聚合函数

聚合函数介绍 聚合函数&#xff1a; 对一组数据进行汇总的函数&#xff0c;输入的是一组数据的集合&#xff0c;输出的是单个值。 聚合函数类型&#xff1a;AVG(),SUM(),MAX(),MIN(),COUNT() AVG / SUM 只适用于数值类型的字段&#xff08;或变量&#xff09; SELECT AVG(…

【LeetCode】每日一题 2023_11_4 数组中两个数的最大异或值

文章目录 刷题前唠嗑题目&#xff1a;数组中两个数的最大异或值题目描述代码与解题思路 结语 刷题前唠嗑 LeetCode? 启动&#xff01;&#xff01;&#xff01; 题目&#xff1a;数组中两个数的最大异或值 题目链接&#xff1a;421. 数组中两个数的最大异或值 题目描述 代…

前端埋点方式

前言&#xff1a; 想要了解用户在系统中所做的操作&#xff0c;从而得出用户在本系统中最常用的模块、在系统中停留的时间。对于了解用户的行为、分析用户的需求有很大的帮助&#xff0c;想实现这种需求可以通过前端埋点的方式。 埋点方式&#xff1a; 1.什么是埋点&#xff1f…