当前位置: 首页 > news >正文

【音视频】视频解码实战

FFmpeg流程

在这里插入图片描述

  • 从本地读取YUV数据编码为h264格式的数据,然后再存⼊到本地,编码后的数据有带startcode
  • 与FFmpeg 示例⾳频编码的流程基本⼀致。

函数说明

  • avcodec_find_encoder_by_name:根据指定的编码器名称查找注册的编码器。
  • avcodec_alloc_context3:为AVCodecContext分配内存。
  • avcodec_open2:打开编解码器。
  • avcodec_send_frame:将AVFrame⾮压缩数据给编码器。。
  • avcodec_receive_packet:获取到编码后的AVPacket数据。
  • av_frame_get_buffer: 为⾳频或视频数据分配新的buffer。在调⽤这个函数之前,必须在AVFame上设置好以下属性:format(视频为像素格式,⾳频为样本格式)、nb_samples(样本个数,针对⾳频)、channel_layout(通道类型,针对⾳频)、width/height(宽⾼,针对视频)。
  • av_frame_make_writable:确保AVFrame是可写的,尽可能避免数据的复制。如果AVFrame不是是可写的,将分配新的buffer和复制数据。
  • av_image_fill_arrays: 存储⼀帧像素数据存储到AVFrame对应的data buffer。

编码出来的h264数据可以直接使⽤ffplay播放,也可以使⽤VLC播放。

av_image_get_buffer_size

int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);

函数的作⽤是通过指定像素格式、图像宽、图像⾼来计算所需的内存⼤⼩,重点说明⼀个参数align:此参数是设定内存对⻬的对⻬数,也就是按多⼤的字节进⾏内存对⻬:

  • ⽐如设置为1,表示按1字节对⻬,那么得到的结果就是与实际的内存⼤⼩⼀样。3
  • 再⽐如设置为4,表示按4字节对⻬。也就是内存的起始地址必须是4的整倍数。

av_image_alloc

int av_image_alloc(uint8_t *pointers[4], int linesizes[4], int w, int h, enum AVPixelFormat pix_fmt,int align);

av_image_alloc()是这样定义的。此函数的功能是按照指定的宽、⾼、像素格式来分配图像内存。

  • pointers[4]:保存图像通道的地址。如果是RGB,则前三个指针分别指向R,G,B的内存地址。第四个指针保留不⽤ linesizes[4]:保存图像每个通道的内存对⻬的步⻓,即⼀⾏的对⻬内存的宽度,此值⼤⼩等于图像宽度。
  • w: 要申请内存的图像宽度。
  • h: 要申请内存的图像⾼度。
  • pix_fmt: 要申请内存的图像的像素格式。
  • align: ⽤于内存对⻬的值。
  • 返回值:所申请的内存空间的总⼤⼩。如果是负值,表示申请失败。

av_image_fill_arrays

int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4], const uint8_t *src, enum AVPixelFormat pix_fmt, int width, int height, int align);

av_image_fill_arrays()函数⾃身不具备内存申请的功能,此函数类似于格式化已经申请的内存,即通过av_malloc()函数申请的内存空间,或者av_frame_get_buffer()函数申请的内存空间。

再者,av_image_fill_arrays()中参数具体说明:

  • dst_data[4][out]对申请的内存格式化为三个通道后,分别保存其地址
  • dst_linesize[4]:[out]格式化的内存的步⻓(即内存对⻬后的宽度)
  • *src: [in]av_alloc()函数申请的内存地址。
  • pix_fmt: [in] 申请 src内存时的像素格式
  • width: [in]申请src内存时指定的宽度
  • height: [in]申请scr内存时指定的⾼度
  • align: [in]申请src内存时指定的对⻬字节数。

H.264 码率设置

1 什么是视频码率

视频码率是视频数据(包含视频⾊彩量、亮度量、像素量)每秒输出的位数。⼀般⽤的单位是kbps。

2 设置视频码率的必要性
  • 在⽹络视频应⽤中,视频质量和⽹络带宽占⽤是相⽭盾的。通常情况下,视频流占⽤的带宽越⾼则视频质量也越⾼,需要的⽹络带宽也越⼤,解决这⼀⽭盾的钥匙当然是视频编解码技术。评判⼀种视频编解码技术的优劣,是⽐较在相同的带宽条件下,哪个视频质量更好;在相同的视频质量条件下,哪个占⽤的⽹络带宽更少(⽂件体积⼩)。

  • 是不是视频码率越⾼,质量越好呢?理论上是这样的。然⽽在我们⾁眼分辨的范围内,当码率⾼到⼀定程度时,就没有什么差别了。所以码率设置有它的最优值,H.264(也叫AVC或X264)的⽂件中,视频的建议码率如下:

视频大小分辨率推荐码率
480P720x4801800Kbps
720P1280x7203500Kbps
1080P1920x10808500Kbps
3 ⼿机设置码率建议
项目计算公式192X144320X240480X360640X4801280X7201920X1080
极低码率(宽X高X3)/430kbps60kbps120kbps250kbps500kbps1000kbps
低码率(宽X高X3)/260kbps120kbps250kbps500kbps1000kbps2000kbps
中码率(宽X高X3)120kbps250kbps500kbps1000kbps2000kbps4000kbps
高码率(宽X高X3)X2250kbps500kbps1000kbps2000kbps4000kbps8000kbps
极高码率(宽X高X3)X4500kbps1000kbps2000kbps4000kbps8000kbps16000kbps

FFmpeg与H264编码指南

鉴于x264的参数众多,各种参数的配合复杂,为了使⽤者⽅便,x264建议如⽆特别需要可使⽤preset和tune设置。这套开发者推荐的参数较为合理,可在此基础上在调整⼀些具体参数以
符合⾃⼰需要,⼿动设定的参数会覆盖preset和tune⾥的参数。

使⽤ffmpeg -h encoder=libx264 命令查询相关⽀持的参数

libx264 AVOptions:-preset            <string>     E..V..... Set the encoding preset (cf. x264 --fullhelp) (default "medium")-tune              <string>     E..V..... Tune the encoding params (cf. x264 --fullhelp)-profile           <string>     E..V..... Set profile restrictions (cf. x264 --fullhelp)-fastfirstpass     <boolean>    E..V..... Use fast settings when encoding first pass (default true)-level             <string>     E..V..... Specify level (as defined by Annex A)-passlogfile       <string>     E..V..... Filename for 2 pass stats-wpredp            <string>     E..V..... Weighted prediction for P-frames-a53cc             <boolean>    E..V..... Use A53 Closed Captions (if available) (default true)-x264opts          <string>     E..V..... x264 options-crf               <float>      E..V..... Select the quality for constant quality mode (from -1 to FLT_MAX) (default -1)-crf_max           <float>      E..V..... In CRF mode, prevents VBV from lowering quality beyond this point. (from -1 to FLT_MAX) (default -1)-qp                <int>        E..V..... Constant quantization parameter rate control method (from -1 to INT_MAX) (default -1)-aq-mode           <int>        E..V..... AQ method (from -1 to INT_MAX) (default -1)none                         E..V.....variance                     E..V..... Variance AQ (complexity mask)autovariance                 E..V..... Auto-variance AQautovariance-biased              E..V..... Auto-variance AQ with bias to dark scenes-aq-strength       <float>      E..V..... AQ strength. Reduces blocking and blurring in flat and textured areas. (from -1 to FLT_MAX) (default -1)-psy               <boolean>    E..V..... Use psychovisual optimizations. (default auto)-psy-rd            <string>     E..V..... Strength of psychovisual optimization, in <psy-rd>:<psy-trellis> format.-rc-lookahead      <int>        E..V..... Number of frames to look ahead for frametype and ratecontrol (from -1 to INT_MAX) (default -1)-weightb           <boolean>    E..V..... Weighted prediction for B-frames. (default auto)-weightp           <int>        E..V..... Weighted prediction analysis method. (from -1 to INT_MAX) (default -1)none                         E..V.....simple                       E..V.....smart                        E..V.....-ssim              <boolean>    E..V..... Calculate and print SSIM stats. (default auto)-intra-refresh     <boolean>    E..V..... Use Periodic Intra Refresh instead of IDR frames. (default auto)-bluray-compat     <boolean>    E..V..... Bluray compatibility workarounds. (default auto)-b-bias            <int>        E..V..... Influences how often B-frames are used (from INT_MIN to INT_MAX) (default INT_MIN)-b-pyramid         <int>        E..V..... Keep some B-frames as references. (from -1 to INT_MAX) (default -1)none                         E..V.....strict                       E..V..... Strictly hierarchical pyramidnormal                       E..V..... Non-strict (not Blu-ray compatible)-mixed-refs        <boolean>    E..V..... One reference per partition, as opposed to one reference per macroblock (default auto)-8x8dct            <boolean>    E..V..... High profile 8x8 transform. (default auto)-fast-pskip        <boolean>    E..V..... (default auto)-aud               <boolean>    E..V..... Use access unit delimiters. (default auto)-mbtree            <boolean>    E..V..... Use macroblock tree ratecontrol. (default auto)-deblock           <string>     E..V..... Loop filter parameters, in <alpha:beta> form.-cplxblur          <float>      E..V..... Reduce fluctuations in QP (before curve compression) (from -1 to FLT_MAX) (default -1)-partitions        <string>     E..V..... A comma-separated list of partitions to consider. Possible values: p8x8, p4x4, b8x8, i8x8, i4x4, none, all-direct-pred       <int>        E..V..... Direct MV prediction mode (from -1 to INT_MAX) (default -1)none                         E..V.....spatial                      E..V.....temporal                     E..V.....auto                         E..V.....-slice-max-size    <int>        E..V..... Limit the size of each slice in bytes (from -1 to INT_MAX) (default -1)-stats             <string>     E..V..... Filename for 2 pass stats-nal-hrd           <int>        E..V..... Signal HRD information (requires vbv-bufsize; cbr not allowed in .mp4) (from -1 to INT_MAX) (default -1)none                         E..V.....vbr                          E..V.....cbr                          E..V.....-avcintra-class    <int>        E..V..... AVC-Intra class 50/100/200 (from -1 to 200) (default -1)-me_method         <int>        E..V..... Set motion estimation method (from -1 to 4) (default -1)dia                          E..V.....hex                          E..V.....umh                          E..V.....esa                          E..V.....tesa                         E..V.....-motion-est        <int>        E..V..... Set motion estimation method (from -1 to 4) (default -1)dia                          E..V.....hex                          E..V.....umh                          E..V.....esa                          E..V.....tesa                         E..V.....-forced-idr        <boolean>    E..V..... If forcing keyframes, force them as IDR frames. (default false)-coder             <int>        E..V..... Coder type (from -1 to 1) (default default)default                      E..V.....cavlc                        E..V.....cabac                        E..V.....vlc                          E..V.....ac                           E..V.....-b_strategy        <int>        E..V..... Strategy to choose between I/P/B-frames (from -1 to 2) (default -1)-chromaoffset      <int>        E..V..... QP difference between chroma and luma (from INT_MIN to INT_MAX) (default -1)-sc_threshold      <int>        E..V..... Scene change threshold (from INT_MIN to INT_MAX) (default -1)-noise_reduction   <int>        E..V..... Noise reduction (from INT_MIN to INT_MAX) (default -1)-x264-params       <string>     E..V..... Override the x264 configuration using a :-separated list of key=value parameters

x264是⼀个 H.264/MPEG4 AVC 编码器,本指南将指导新⼿如何创建⾼质量的H.264视频。

对于普通⽤户通常有两种码率控制模式:CRF(Constant Rate Factor)和Two pass ABR。码率控制是⼀种决定为每⼀个视频帧分配多少⽐特数的⽅法,它将决定⽂件的⼤⼩和质量的分配。

CRF(Constant Rate Factor):
1 选择⼀个CRF值
  • 量化⽐例的范围为051,其中0为⽆损模式,**23为缺省值**,51可能是最差的。该数字越⼩,图像质量越好。从主观上讲,1828是⼀个合理的范围。18往往被认为从视觉上看是⽆损的,它的输出视频质量和输⼊视频⼀模⼀样或者说相差⽆⼏。但从技术的⻆度来讲,它依然是有损压缩。

  • 若CRF值加6,输出码率⼤概减少⼀半;若CRF值减6,输出码率翻倍。通常是在保证可接受视频质量的前提下选择⼀个最⼤的CRF值,如果输出视频质量很好,那就尝试⼀个更⼤的值,如果看起来很糟,那就尝试⼀个⼩⼀点值。

注释:本⽂所提到的量化⽐例只适⽤于8-bit x264(10-bit x264的量化⽐例 为0~63),你可以使⽤x264 --help命令在Output bit depth选项查看输出位深,在各种版本中,8bit是最常⻅的。

2 选择⼀个preset和tune

preset

  • 预设是⼀系列参数的集合,这个集合能够在编码速度和压缩率之间做出⼀个权衡。⼀个编码速度稍慢的预设会提供更⾼的压缩效率(压缩效率是以⽂件⼤⼩来衡量的)。这就是说,假如你想得到⼀个指定⼤⼩的⽂件或者采⽤恒定⽐特率编码模式,你可以采⽤⼀个较慢的预设来获得更好的质量。同样的,对于恒定质量编码模式,你可以通过选择⼀个较慢的预设轻松地节省⽐特率。

  • 如果你很有耐⼼,通常的建议是使⽤最慢的预设。⽬前所有的预设按照编码速度降序排列为:

ultrafast
superfast
veryfast
faster
fast
medium – default preset
slow
slower
veryslow
placebo - ignore this as it is not useful (see FAQ)

默认为medium级别。

你可以使⽤–preset来查看预设列表,也可以通过x264 --fullhelp来查看预设所采⽤的参数配置。

针对 libx264做过简单的各选项对⽐测试,结果如下图:

在这里插入图片描述

从图中可以看出,当其他参数固定时,选择不同的preset,对应的码率和编码时间都不⼀样

tune
tune是x264中重要性仅次于preset的选项,它是视觉优化的参数,tune可以理解为视频偏好(或者视频类型),tune不是⼀个单⼀的参数,⽽是由⼀组参数构成-tune来改变参数设置。当前的 tune包括:

  • film:电影类型,对视频的质量⾮常严格时使⽤该选项

  • animation:动画⽚,压缩的视频是动画⽚时使⽤该选项

  • grain:颗粒物很重,该选项适⽤于颗粒感很重的视频

  • stillimage:静态图像,该选项主要⽤于静⽌画⾯⽐较多的视频

  • psnr:提⾼psnr,该选项编码出来的视频psnr⽐较⾼

  • ssim:提⾼ssim,该选项编码出来的视频ssim⽐较⾼

  • fastdecode:快速解码,该选项有利于快速解码

  • zerolatency:零延迟,该选项主要⽤于视频直播

  • 如果你不确定使⽤哪个选项或者说你的输⼊与所有的tune皆不匹配,你可以忽略–tune 选项。

  • 你可以使⽤-tune来查看tune列表,也可以通过x264 --fullhelp来查看tune所采⽤的参数配置。

profile

另外⼀个可选的参数是-profile:v,它可以将你的输出限制到⼀个特定的 H.264 profile。⼀些⾮常⽼的或者要被淘汰的设备仅⽀持有限的选项,⽐如只⽀持baseline或者main。

所有的profile 包括:

  1. baseline profile:基本画质。⽀持I/P 帧,只⽀持⽆交错(Progressive)和CAVLC;
  2. extended profile:进阶画质。⽀持I/P/B/SP/SI 帧,只⽀持⽆交错(Progressive)和CAVLC;
  3. main profile:主流画质。提供I/P/B 帧,⽀持⽆交错(Progressive)和交错(Interlaced),也⽀持CAVLC 和CABAC 的⽀持;
  4. high profile:⾼级画质。在main Profile 的基础上增加了8x8内部预测、⾃定义量化、 ⽆损视频编码和更多的YUV 格式;

在相同配置情况下,High profile(HP)可以⽐Main profile(MP)节省10%的码流量,⽐MPEG-2MP节省60%的码流量,具有更好的编码性能。根据应⽤领域的不同:

  • baseline profile多应⽤于实时通信领域;
  • main profile多应⽤于流媒体领域;
  • high profile则多应⽤于⼴电和存储领域。
低延迟

x264提供了⼀个 -tune zerolatency 选项。

兼容性

如果你想让你的视频最⼤化的和⽬标播放设备兼容(⽐如⽼版本的的ios或者所有的android 设备),那么你可以这做:

-profile:v baseline
  • 这将会关闭很多⾼级特性,但是它会提供很好的兼容性。也许你可能不需要这些设置,因为⼀旦你⽤了这些设置,在同样的视频质量下与更⾼的编码档次相⽐会使⽐特率稍有增加。
  • 关于profile列表和关于它们的描述,你可以运⾏x264 --fullhelp
  • 要牢记apple quick time 对于x264编码的视频只⽀持 YUV 420颜⾊空间,⽽且不⽀持任何⾼于 main profile编码档次。这样对于quick time 只留下了两个兼容选项baseline和 main。其他的编码档次qucik time均不⽀持,虽然它们均可以在其它的播放设备上回放。
X264参数之zerolatency的分析

我们都知道,加⼊zerolatency的⽬的是为了降低在线转码的编码延迟,那么,该参数是如何影响到x264的转码性能了呢?

⾸先,先来看看代码中编码延迟的影响因素:

h->frames.i_delay = max(h->param.i_bframe, h->param.rc.i_lookahead)
h->i_thread_frames - 1
h->param.i_sync_lookahead
h->param.b_vfr_input

设置zerolatency后,相应的参数配置如下:

if(!strncasecmp(s, "zerolatency", 11 ))
{param->rc.i_lookahead = 0;param->i_sync_lookahead = 0;param->i_bframe = 0;param->b_sliced_threads = 1;param->b_vfr_input = 0;param->rc.b_mb_tree = 0;
}

下⾯我们来看⼀下zerolatency设置中各个参数的意义:

  • rc_lookahead: Set number of frames to look ahead for frametype and ratecontrol

  • 该参数为mb-tree码率控制和vbv-lookahead设置可⽤的帧数量,最⼤值为250。对于mbi-tree来说,rc_lookahead值越⼤,会得到更准确的结果,但编码速度也会更慢,因为编码器需要缓存慢rc_lookahead帧数据后,才会开始第⼀帧编码,增加编码延时,因此在实时视频通信中将其设置为0。

  • sync_lookahead: 设置⽤于线程预测的帧缓存⼤⼩,最⼤值为250。在第⼆遍及更多遍编码或基于分⽚线程时⾃动关闭。sync_lookahead = 0为关闭线程预测,可减⼩延迟,但也会降低性能。

  • bframes: I帧和P帧或者两个P帧之间可⽤的最⼤连续B帧数量,默认值为3。B帧可使⽤双向预测,从⽽显著提⾼压缩率,但由于需要缓存更多的帧数以及重排序的原因,会降低编码速度,增加编码延迟,因此在实时编码时也建议将该值设置为0。

  • sliced_threads: 基于分⽚的线程,默认值为off,开启该⽅法在压缩率和编码效率上都略低于默认⽅法,但没有编码延时。除⾮在编码实时流或者对低延迟要求较⾼的场合开启该⽅法,⼀般情况下建议设为off。

  • vfr_input: 与force-cfr选项相对应:

OPT("force-cfr")
p->b_vfr_input = !atobool(value);
  • vfr_input= 1时,为可变帧率,使⽤timebase和timestamps做码率控制;vfr_input = 0时,为固定帧率,使⽤fps做码率控制。

mb_tree: 基于宏块树的码率控制。对于每个MB,向前预测⼀定数量的帧(该数量由rc_lookahead和keyint中的较⼩值决定),计算该MB被引⽤的次数,根据被引⽤次数的多少决定为该MB分配的量化QP值。该⽅法会⽣成⼀个临时stats⽂件,记录了每个P帧中每个MB被参考的情况。使⽤mb_tree的⽅法能够节约⼤概30%的码率,但同时也会增加编码延迟,因此实时流编码时也将其关闭。

实现流程

准备文件

build路径下准备yuv420p的文件,用于编码为h264格式的文件

在这里插入图片描述

main函数参数中传入对应的输入文件、输出文件,以及对应的编码器

在这里插入图片描述

初始化解码器
  • 根据解码器名字查找对应的解码器
  • 初始化解码器上下文
codec_name = argv[3];
codec = avcodec_find_encoder_by_name(codec_name);
codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);
}
  • 设置编码器基本参数,比如分辨率,帧率,时间基、视频格式
codec_ctx->width = 1280;
codec_ctx->height = 720;
/* 设置time base ,注意timebase的正确设置,会影响码率的输出, 即是AVFrame的pts的timebase需要和codec_ctx->time_base一致*/
codec_ctx->time_base = (AVRational){1, 25}; // 和AVFrame的pts相同,这样不需要做时间戳的转换
codec_ctx->framerate = (AVRational){25, 1};
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
  • 设置编码器GOP参数,比如GOP长度(I帧间隔),B帧间隔
codec_ctx->gop_size = 25;   // I帧间隔
codec_ctx->max_b_frames = 2; // 如果不想包含B帧则设置为0
  • 设置解码器高级参数,比如设置pretestprofiletune等参数
  • 设置的这些参数影响输出的视频质量与速度
ret = av_opt_set(codec_ctx->priv_data, "preset", "medium", 0);
ret = av_opt_set(codec_ctx->priv_data, "profile", "main", 0); // 默认是high
ret = av_opt_set(codec_ctx->priv_data, "tune","film",0); //  画质film
  • 设置输出比特率,可以有波动(最小值+最大值),也可以设置成固定的
  • 可以设置缓存大小,波动的时候可以存在缓存中
codec_ctx->bit_rate = 3000000; //固定codec_ctx->rc_max_rate = 3000000; //最大值
codec_ctx->rc_min_rate = 1000000;// 最小值codec_ctx->rc_buffer_size = 2000000;//缓存大小
  • 如果有需要,还可以设置多线程编码,不过多线程会带来延迟
codec_ctx->thread_count = 4;  // 开了多线程后也会导致帧输出延迟, 需要缓存thread_count帧后再编程。
codec_ctx->thread_type = FF_THREAD_FRAME; // 并 设置为FF_THREAD_FRAME
  • libx264编码器默认自动写入NALU头部,如(SPS、PPS、StartCode)
  • 如果需要设置将这些信息将写入在extra_data中,则需要设置编码器标志或上AV_CODEC_FLAG_GLOBAL_HEADER
  • 设置后不会每一个I帧前都写入SPS/PPS信息
codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; // 存本地文件时不要去设置
  • 将编码器和编码器上下文关联起来
ret = avcodec_open2(codec_ctx, codec, NULL);
if (ret < 0) {fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));exit(1);
}
打开文件
  • 打开输入输出文件,进行循环写入数据
infile = fopen(in_yuv_file, "rb");
if (!infile) {fprintf(stderr, "Could not open %s\n", in_yuv_file);exit(1);
}
outfile = fopen(out_h264_file, "wb");
if (!outfile) {fprintf(stderr, "Could not open %s\n", out_h264_file);exit(1);
}
分配frame和``packet`
  • frame用于存储没有编码的yuv裸数据
  • packet用于存储编码过的h264数据
// 分配pkt和frame
pkt = av_packet_alloc();
if (!pkt) {fprintf(stderr, "Could not allocate video frame\n");exit(1);
}
frame = av_frame_alloc();
if (!frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);
}
设置frame属性
  • frame中的参数需要从解码器上下文中拷贝过来
  • 根据frame中的参数分配对应的data内存
frame->format = codec_ctx->pix_fmt;
frame->width  = codec_ctx->width;
frame->height = codec_ctx->height;ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {fprintf(stderr, "Could not allocate the video frame data\n");exit(1);
}
  • 根据frame的参数,计算出缓存区需要设置的内存大小
  • 分配对应大小的字节内存
int frame_bytes = av_image_get_buffer_size(frame->format, frame->width,frame->height, 1);
uint8_t *yuv_buf = (uint8_t *)malloc(frame_bytes);
循环编码
  • 编码逻辑和音频类似,只是这里是将yuv数据根据相应参数拷贝到frame
int need_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,frame->format,frame->width, frame->height,1);
  • 循环解码代码如下
 for (;;) {memset(yuv_buf, 0, frame_bytes);size_t read_bytes = fread(yuv_buf, 1, frame_bytes, infile);if(read_bytes <= 0) {printf("read file finish\n");break;}/* 确保该frame可写, 如果编码器内部保持了内存参考计数,则需要重新拷贝一个备份目的是新写入的数据和编码器保存的数据不能产生冲突*/int frame_is_writable = 1;if(av_frame_is_writable(frame) == 0) { // 这里只是用来测试printf("the frame can't write, buf:%p\n", frame->buf[0]);if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针printf("ref_count1(frame) = %d\n", av_buffer_get_ref_count(frame->buf[0]));frame_is_writable = 0;}ret = av_frame_make_writable(frame);if(frame_is_writable == 0) {  // 这里只是用来测试printf("av_frame_make_writable, buf:%p\n", frame->buf[0]);if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针printf("ref_count2(frame) = %d\n", av_buffer_get_ref_count(frame->buf[0]));}if(ret != 0) {printf("av_frame_make_writable failed, ret = %d\n", ret);break;}int need_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,frame->format,frame->width, frame->height, 1);if(need_size != frame_bytes) {printf("av_image_fill_arrays failed, need_size:%d, frame_bytes:%d\n",need_size, frame_bytes);break;}pts += 40;// 设置ptsframe->pts = pts;       // 使用采样率作为pts的单位,具体换算成秒 pts*1/采样率begin_time = get_time();ret = encode(codec_ctx, frame, pkt, outfile);end_time = get_time();printf("encode time:%lldms\n", end_time - begin_time);if(ret < 0) {printf("encode failed\n");break;}}
  • encode函数用于将frame编码为packet,并写入到输出的h264文件中
  • 同样注意send一次frame,可能生成多个package,因此需要循环接收
static int encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,FILE *outfile)
{int ret;/* send the frame to the encoder */if (frame)printf("Send frame %3"PRId64"\n", frame->pts);/* 通过查阅代码,使用x264进行编码时,具体缓存帧是在x264源码进行,* 不会增加avframe对应buffer的reference*/ret = avcodec_send_frame(enc_ctx, frame);if (ret < 0){fprintf(stderr, "Error sending a frame for encoding\n");return -1;}while (ret >= 0){ret = avcodec_receive_packet(enc_ctx, pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return 0;} else if (ret < 0) {fprintf(stderr, "Error encoding audio frame\n");return -1;}if(pkt->flags & AV_PKT_FLAG_KEY)printf("Write packet flags:%d pts:%3"PRId64" dts:%3"PRId64" (size:%5d)\n",pkt->flags, pkt->pts, pkt->dts, pkt->size);if(!pkt->flags)printf("Write packet flags:%d pts:%3"PRId64" dts:%3"PRId64" (size:%5d)\n",pkt->flags, pkt->pts, pkt->dts, pkt->size);fwrite(pkt->data, 1, pkt->size, outfile);}return 0;
}
冲刷编码器
  • 编码结束后还需要冲刷一下编码器,写入剩余数据
encode(codec_ctx, NULL, pkt, outfile);
结束工作
  • 结束后要关闭文件并释放相关内存
fclose(infile);
fclose(outfile);// 释放内存
if(yuv_buf) {free(yuv_buf);
}av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&codec_ctx);

整体代码

main.c

/**
* @projectName   08-02-encode_video
* @brief         视频编码,从本地读取YUV数据进行H264编码
* @author        Liao Qingfu
* @date          2020-04-16
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libavcodec/avcodec.h>
#include <libavutil/time.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>int64_t get_time()
{return av_gettime_relative() / 1000;  // 换算成毫秒
}
static int encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,FILE *outfile)
{int ret;/* send the frame to the encoder */if (frame)printf("Send frame %3"PRId64"\n", frame->pts);/* 通过查阅代码,使用x264进行编码时,具体缓存帧是在x264源码进行,* 不会增加avframe对应buffer的reference*/ret = avcodec_send_frame(enc_ctx, frame);if (ret < 0){fprintf(stderr, "Error sending a frame for encoding\n");return -1;}while (ret >= 0){ret = avcodec_receive_packet(enc_ctx, pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return 0;} else if (ret < 0) {fprintf(stderr, "Error encoding audio frame\n");return -1;}if(pkt->flags & AV_PKT_FLAG_KEY)printf("Write packet flags:%d pts:%3"PRId64" dts:%3"PRId64" (size:%5d)\n",pkt->flags, pkt->pts, pkt->dts, pkt->size);if(!pkt->flags)printf("Write packet flags:%d pts:%3"PRId64" dts:%3"PRId64" (size:%5d)\n",pkt->flags, pkt->pts, pkt->dts, pkt->size);fwrite(pkt->data, 1, pkt->size, outfile);}return 0;
}
/*** @brief 提取测试文件:ffmpeg -i test_1280x720.flv -t 5 -r 25 -pix_fmt yuv420p yuv420p_1280x720.yuv*           参数输入: yuv420p_1280x720.yuv yuv420p_1280x720.h264 libx264* @param argc* @param argv* @return*/
int main(int argc, char **argv)
{char *in_yuv_file = NULL;char *out_h264_file = NULL;FILE *infile = NULL;FILE *outfile = NULL;const char *codec_name = NULL;const AVCodec *codec = NULL;AVCodecContext *codec_ctx= NULL;AVFrame *frame = NULL;AVPacket *pkt = NULL;int ret = 0;if (argc < 4) {fprintf(stderr, "Usage: %s <input_file out_file codec_name >, argc:%d\n",argv[0], argc);return 0;}in_yuv_file = argv[1];      // 输入YUV文件out_h264_file = argv[2];codec_name = argv[3];/* 查找指定的编码器 */codec = avcodec_find_encoder_by_name(codec_name);if (!codec) {fprintf(stderr, "Codec '%s' not found\n", codec_name);exit(1);}codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}/* 设置分辨率*/codec_ctx->width = 1280;codec_ctx->height = 720;/* 设置time base ,注意timebase的正确设置,会影响码率的输出, 即是AVFrame的pts的timebase需要和codec_ctx->time_base一致*/codec_ctx->time_base = (AVRational){1, 25}; // 和AVFrame的pts相同,这样不需要做时间戳的转换codec_ctx->framerate = (AVRational){25, 1};/* 设置I帧间隔* 如果frame->pict_type设置为AV_PICTURE_TYPE_I, 则忽略gop_size的设置,一直当做I帧进行编码*/codec_ctx->gop_size = 25;   // I帧间隔codec_ctx->max_b_frames = 2; // 如果不想包含B帧则设置为0codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;//if (codec->id == AV_CODEC_ID_H264) {// 相关的参数可以参考libx264.c的 AVOption options// ultrafast all encode time:2270ms// medium all encode time:5815ms// veryslow all encode time:19836msret = av_opt_set(codec_ctx->priv_data, "preset", "medium", 0);if(ret != 0) {printf("av_opt_set preset failed\n");}ret = av_opt_set(codec_ctx->priv_data, "profile", "main", 0); // 默认是highif(ret != 0) {printf("av_opt_set profile failed\n");}ret = av_opt_set(codec_ctx->priv_data, "tune","zerolatency",0); // 直播是才使用该设置
//        ret = av_opt_set(codec_ctx->priv_data, "tune","film",0); //  画质filmif(ret != 0) {printf("av_opt_set tune failed\n");}}/** 设置编码器参数*//* 设置bitrate */codec_ctx->bit_rate = 3000000;// codec_ctx->rc_max_rate = 3000000;// codec_ctx->rc_min_rate = 1000000;// codec_ctx->rc_buffer_size = 20000AA00;// codec_ctx->thread_count = 4;  // 开了多线程后也会导致帧输出延迟, 需要缓存thread_count帧后再编程。codec_ctx->thread_type = FF_THREAD_FRAME; // 并 设置为FF_THREAD_FRAME/* 对于H264 AV_CODEC_FLAG_GLOBAL_HEADER  设置则只包含I帧,此时sps pps需要从codec_ctx->extradata读取*  不设置则每个I帧都带 sps pps sei*/// codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; // 存本地文件时不要去设置/* 将codec_ctx和codec进行绑定 */ret = avcodec_open2(codec_ctx, codec, NULL);if (ret < 0) {fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));exit(1);}printf("thread_count: %d, thread_type:%d\n", codec_ctx->thread_count, codec_ctx->thread_type);// 打开输入和输出文件infile = fopen(in_yuv_file, "rb");if (!infile) {fprintf(stderr, "Could not open %s\n", in_yuv_file);exit(1);}outfile = fopen(out_h264_file, "wb");if (!outfile) {fprintf(stderr, "Could not open %s\n", out_h264_file);exit(1);}// 分配pkt和framepkt = av_packet_alloc();if (!pkt) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}// 为frame分配bufferframe->format = codec_ctx->pix_fmt;frame->width  = codec_ctx->width;frame->height = codec_ctx->height;ret = av_frame_get_buffer(frame, 0);if (ret < 0) {fprintf(stderr, "Could not allocate the video frame data\n");exit(1);}// 计算出每一帧的数据 像素格式 * 宽 * 高// 1382400int frame_bytes = av_image_get_buffer_size(frame->format, frame->width,frame->height, 1);printf("frame_bytes %d\n", frame_bytes);uint8_t *yuv_buf = (uint8_t *)malloc(frame_bytes);if(!yuv_buf) {printf("yuv_buf malloc failed\n");return 1;}int64_t begin_time = get_time();int64_t end_time = begin_time;int64_t all_begin_time = get_time();int64_t all_end_time = all_begin_time;int64_t pts = 0;printf("start enode\n");for (;;) {memset(yuv_buf, 0, frame_bytes);size_t read_bytes = fread(yuv_buf, 1, frame_bytes, infile);if(read_bytes <= 0) {printf("read file finish\n");break;}/* 确保该frame可写, 如果编码器内部保持了内存参考计数,则需要重新拷贝一个备份目的是新写入的数据和编码器保存的数据不能产生冲突*/int frame_is_writable = 1;if(av_frame_is_writable(frame) == 0) { // 这里只是用来测试printf("the frame can't write, buf:%p\n", frame->buf[0]);if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针printf("ref_count1(frame) = %d\n", av_buffer_get_ref_count(frame->buf[0]));frame_is_writable = 0;}ret = av_frame_make_writable(frame);if(frame_is_writable == 0) {  // 这里只是用来测试printf("av_frame_make_writable, buf:%p\n", frame->buf[0]);if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针printf("ref_count2(frame) = %d\n", av_buffer_get_ref_count(frame->buf[0]));}if(ret != 0) {printf("av_frame_make_writable failed, ret = %d\n", ret);break;}int need_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,frame->format,frame->width, frame->height, 1);if(need_size != frame_bytes) {printf("av_image_fill_arrays failed, need_size:%d, frame_bytes:%d\n",need_size, frame_bytes);break;}pts += 40;// 设置ptsframe->pts = pts;       // 使用采样率作为pts的单位,具体换算成秒 pts*1/采样率begin_time = get_time();ret = encode(codec_ctx, frame, pkt, outfile);end_time = get_time();printf("encode time:%lldms\n", end_time - begin_time);if(ret < 0) {printf("encode failed\n");break;}}/* 冲刷编码器 */encode(codec_ctx, NULL, pkt, outfile);all_end_time = get_time();printf("all encode time:%lldms\n", all_end_time - all_begin_time);// 关闭文件fclose(infile);fclose(outfile);// 释放内存if(yuv_buf) {free(yuv_buf);}av_frame_free(&frame);av_packet_free(&pkt);avcodec_free_context(&codec_ctx);printf("main finish, please enter Enter and exit\n");getchar();return 0;
}

更多资料:https://github.com/0voice

相关文章:

【音视频】视频解码实战

FFmpeg流程 从本地读取YUV数据编码为h264格式的数据&#xff0c;然后再存⼊到本地&#xff0c;编码后的数据有带startcode与FFmpeg 示例⾳频编码的流程基本⼀致。 函数说明 avcodec_find_encoder_by_name&#xff1a;根据指定的编码器名称查找注册的编码器。avcodec_alloc_co…...

计算机网络学习笔记 4-6章

第 4 章 网络层 【考纲内容】 &#xff08;一&#xff09;网络层的功能 异构网络互连&#xff1b;路由与转发&#xff1b;SDN 基本概念&#xff1b;拥塞控制 &#xff08;二&#xff09;路由算法 静态路由与动态路由&#xff1b;距离 - 向量路由算法&#xff1…...

游戏哪些接口会暴露源IP?_深度解析服务器通信安全隐患

一、用户认证体系中的IP泄露陷阱 在游戏登录验证环节&#xff0c;采用明文传输的HTTP协议接口会将客户端IP直接暴露在TCP握手阶段。某头部MOBA游戏曾因使用HTTP Basic认证方式&#xff0c;导致黑客通过抓取三次握手数据包获取服务器真实IP。游戏行业权威测试显示&#xff0c;使…...

树莓派学习专题<11>:使用V4L2驱动获取摄像头数据--启动/停止数据流,数据捕获,缓存释放

树莓派学习专题&#xff1c;11&#xff1e;&#xff1a;使用V4L2驱动获取摄像头数据--启动/停止数据流&#xff0c;数据捕获&#xff0c;缓存释放 1. 启动和停止数据流2. 捕获数据3. 释放缓存 1. 启动和停止数据流 使用命令 VIDIOC_STREAMON 启动摄像头数据流&#xff0c;使用…...

adb push 报错:CreateProcess failure, error 123

客户使用adb push 可执行程序的时候报错如下所示 原因&#xff1a;文件目录里边带中文导致 解决方法&#xff1a;将文件目录里中文改成英文就好了...

【实战篇】数字化打印——打印格式设计器的功能说明

前言 myBuilder内置了覆盖丰富场景的打印格式设计器&#xff0c;效果统一&#xff0c;功能完善。 设计器一&#xff1a;小票 用于设计小票、水单等滚筒纸张的场景&#xff0c;例如&#xff1a;超市购物小票 主要功能 打印格式的保存、下载、上传设计时功能&#xff1a;撤销…...

【数据挖掘】时间序列预测-时间序列预测策略

时间序列预测策略 &#xff08;1&#xff09;单步预测与多步预测&#xff08;2&#xff09;直接多步预测&#xff08;3&#xff09;递归多步预测&#xff08;4&#xff09;直接递归的混合预测&#xff08;5&#xff09;多输入多输出预测 &#xff08;1&#xff09;单步预测与多…...

京东商品详情数据爬取难度分析与解决方案

在当今数字化商业时代&#xff0c;电商数据对于市场分析、竞品研究、价格监控等诸多领域有着不可估量的价值。京东&#xff0c;作为国内首屈一指的电商巨头&#xff0c;其商品详情页蕴含着海量且极具价值的数据&#xff0c;涵盖商品价格、库存、规格、用户评价等关键信息。然而…...

【Linux】线程

一.线程概念 我们在学习进程的时候已经知道了&#xff0c;进程内核数据结构pcb自己的代码和数据。那么单单一个task_struct是什么呢&#xff1f; 我们将单个的task_struct叫做轻量级进程&#xff0c;而这个轻量级进程也叫做线程。以往我们在了解进程的时候&#xff0c;一个进…...

WPF-遵循MVVM框架创建图表的显示【保姆级】

文章速览 1、技术栈实现步骤1、创建WPF工程项目2、引入框架 Caliburn.Micro、数据可视化库ScottPlot.WPF3、创建文件夹&#xff0c;并创建相应的View & ViewModel4、创建启动类5、将启动类设置为启动项6、编写View7、编写VM8、将VM和View中的图表进行绑定9、备注 示例效果 …...

深入详解人工智能数学基础—概率论-KL散度在变分自编码器(VAE)中的应用

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用…...

《代码整洁之道》第9章 单元测试 - 笔记

测试驱动开发 (TDD) 是一种编写整洁代码的“规程”或“方法论”&#xff0c;而不仅仅是测试技术。 JaCoCo 在运行测试后生成详细的覆盖率报告的工具&#xff0c; maven 引用。 测试驱动开发 测试驱动开发&#xff08;TDD&#xff09;是什么&#xff1f; TDD 不是说写完代码…...

每日c/c++题 备战蓝桥杯(P2392 kkksc03考前临时抱佛脚)

【题解】期末考试抱佛脚最短时间&#xff08;动态规划 | 二进制背包&#xff09; 题目链接 题目背景 kkksc03 的大学生活非常颓废&#xff0c;临近期末考试才开始疯狂复习。他有 4 门科目需要复习&#xff0c;每一科都有若干道题目&#xff0c;每道题目需要一定的时间完成。…...

徽客松S1 | 合肥首场 AI 黑客松招募

越来越多的黑客松在各个城市出现&#xff01;5 月 10 日&#xff0c;合肥&#xff0c;12 小时极速挑战。 我们和本次「徽客松」发起人 SDL 也是在一个黑客松上相识。当你的城市还没有黑客松可参加&#xff0c;与其等待&#xff0c;不如学习 SDL&#xff0c;自己发起一个&#…...

单片机-89C51部分:6、按键

飞书文档https://x509p6c8to.feishu.cn/wiki/EtkHw8MG0ipz3NkHlZEcwpEnn4g 一、应用场景&#xff1a; 轻触开关、按键、电容开关、光栅传感器、微动、关电开关 二、原理&#xff1a; 轻触按键可以理解为两根导线&#xff0c;按下时导线连接&#xff0c;松开时导线断开。我们可…...

小结: DHCP

交换机的物理接口分批地址池、全局分配地址池 分批地址池&#xff08;接口地址池/局部分配&#xff09; 按物理接口&#xff08;如 VLAN 接口、SVI、物理端口&#xff09;划分&#xff0c;每个接口单独配置一个小型地址池。适合规模较小、子网划分清晰的场景。配置方法示例&…...

matlab simulink中理想变压激磁电流容易有直流偏置的原因分析。

simulink把线性变压器模块拉出来&#xff0c;设置没有绕线电阻的变压器&#xff0c;激磁电感和Rm都有&#xff0c;然后给一个50%占空比的方波&#xff0c;幅值正负10V&#xff0c;线路中设置一个电阻&#xff0c;模拟导线阻抗。通过示波器观察激磁电流&#xff0c;发现电阻越小…...

国产三维CAD皇冠CAD在「通用设备制造业」建模教程:台式起重机

在制造业数字化转型的浪潮中&#xff0c;三维CAD软件已成为装备设计的核心工具&#xff0c;而国产软件的崛起正悄然改变行业格局。皇冠CAD&#xff08;CrownCAD&#xff09;作为中国自主研发的云端三维CAD平台&#xff0c;凭借全栈可控的底层架构、高效协同的设计流程及复杂场景…...

Day 12

文件操作 文件文件操作文件函数课堂笔记 文件 1&#xff09;概述 FILE 所有平台的名字都一样&#xff0c;FILE 是一个结构体类型,里面的成员功能一样,不同平台成员的名字不一样。 FILE *fp 1、fp指针&#xff0c;只用调用了fopen().在堆区分配空间,把地址返回给fp 2、fp指针…...

Lua 第11部分 小插曲:出现频率最高的单词

在本章中&#xff0c;我们要开发一个读取并输出一段文本中出现频率最高的单词的程序。像之前的小插曲一样&#xff0c;本章的程序也十分简单但是也使用了诸如迭代器和匿名函数这样的高级特性。 该程序的主要数据结构是一个记录文本中出现的每一个单词及其出现次数之间关系的表。…...

自然语言处理之机器翻译:注意力机制在低资源翻译中的突破与哲思

## 被忽视的7000种语言 在人工智能翻译技术突飞猛进的今天,一个残酷的事实被刻意掩盖:全球7000种语言中,超过95%缺乏构建现代机器翻译系统所需的基础资源。当我们在庆贺Transformer模型将英德翻译BLEU值推高至40%时,那些承载着人类文明基因的少数民族语言,正在经历着前所未…...

SQL 处理重复数据之技巧(Techniques for Handling Duplicate Data with SQL)

SQL 处理重复数据之技巧 ❝ 在日常数据库操作中&#xff0c;我们经常会遇到重复数据的问题。重复数据不仅会占用存储空间&#xff0c;还可能导致数据分析结果不准确。本文将详细讲解 SQL 中处理重复数据的常用方法&#xff0c;帮助你更高效地管理数据库中的数据。 一、为什么会…...

Redis01-基础-入门

零、文章目录 Redis01-基础-入门 1、认识 NoSQL NoSQL 知识请参考&#xff1a;https://blog.csdn.net/liyou123456789/article/details/132612444 2、认识 Redis &#xff08;1&#xff09;简介 Redis&#xff08;Remote Dictionary Server&#xff0c;远程字典服务&…...

辞九门回忆

2025年月日&#xff0c;13~30℃&#xff0c;挺好的 待办&#xff1a; 《高等数学2》期末试卷 高数重修电子版材料 冶金《物理》期末试卷 《物理[2]》期末试卷 批阅冶金《物理》作业→→统计平时成绩 遇见&#xff1a;遇见一位小姐姐。 感受或反思&#xff1a;不主动推动关系&a…...

全球城市范围30米分辨率土地覆盖数据(1985-2020)

Global urban area 30 meter resolution land cover data (1985-2020) 时间分辨率年空间分辨率10m - 100m共享方式保护期 277 天 5 时 42 分 9 秒数据大小&#xff1a;8.98 GB数据时间范围&#xff1a;1985-2020元数据更新时间2024-01-11 数据集摘要 1985~2020全球城市土地覆…...

java编程式、声明式事务简单介绍

大家吼鸭&#xff01;今天学习新项目的时候&#xff0c;项目中运用了编程式项目&#xff0c;有点不理解什么叫编程式事务&#xff0c;于是我去查询了一些资料&#xff0c;大概了解了一下。现在做一个简单的介绍。 编程式事务和声明式事务的区别 现在想象一个场景&#xff0c;…...

Golang 遇见 Kubernetes:云原生开发的完美结合

Golang 和 Kubernetes 简介 Golang 概述 Golang&#xff0c;也称为 Go&#xff0c;是由 Google 开发的一种开源编程语言。Go 由 Robert Griesemer、Rob Pike 和 Ken Thompson 设计&#xff0c;于 2009 年首次发布&#xff0c;此后在各个领域都获得了广泛的关注&#xff0c;尤其…...

第三章,GRE和MGRE

VPN---虚拟专用网络 VPN的核心技术----隧道技术---封装 GRE---通用路由封装 配置 GRE的配置&#xff1a; R1&#xff1a; [r1]interface Tunnel 0/0/0 ---创建一个虚拟的隧道接口 [r1-Tunnel0/0/0]ip address 192.168.3.1 24 ---给隧道接口分配一个IP地址 [r1-Tunnel0/0/0]t…...

redis常用集合操作命令

在 Redis 的命令行界面&#xff08;redis-cli&#xff09;中&#xff0c; Redis 的集合&#xff08;Set&#xff09;是无序的&#xff0c;且集合中的元素是唯一的。Redis 本身没有直接提供获取集合中某个特定属性的命令&#xff0c;因为集合中的元素是简单的值&#xff0c;而不…...

vue3中ref在js中为什么需要.value才能获取/修改值?

文章目录 [TOC](文章目录) 一、ref定义值什么情况下需要.value1. 情况1:在js中需要使用.value2. 情况2:在html模版中不需要使用.value3. 情况31.代码2.效果3. 二、重新了解一下vue2和vue3的响应式1.vue2&#xff08;Object.defineProperty&#xff09;2.vue3&#xff08;proxy&…...

使用vue2 开发一个纯静态的校园二手交易平台-前端项目练习

这篇文章给大家分享一个适合练习学习前端技术的项目&#xff1a;校园二手交易平台系统。 因为最近在学习vue相关的技术&#xff0c;所以就根据学习的前端技术&#xff0c;来写一些纯前端的项目来练习&#xff0c;这篇文章主要是分享一下 我做的这个项目的一些功能&#xff0c;如…...

使用wavesurferJs实现录音音波效果

效果图展示 插件安装 npm i wavesurfer实现过程 <!-- author: weileiming date: 2025-04-26 14:04:08 description: 悬浮音波层 props:isRecord: 录制状态waveOptions: 音波基础配置overlayStyle: 基础蒙层配置 methods:togglePlay: 切换录制状态 --> <template>…...

Golang 类型方法

在 Go 语言中&#xff0c;方法绑定到任意类型的特性可以称为 “类型方法&#xff08;Type Methods&#xff09;” 或 “接收者方法&#xff08;Receiver Methods&#xff09;”&#xff0c;它体现了以下几种核心编程思想&#xff1a; 1. 官方术语&#xff1a;接收者方法&#x…...

多模态常见面试题

多模态常见面试 一、最近关注的论文&#xff0c;多模态视觉大模型(CLIP,DALLE)&#xff1f;二、blip2的架构&#xff0c;优势和之前多模态模型的区别&#xff1f;三、多模态融合后&#xff0c;怎样知道最终结果受哪种模态影响更大&#xff1f;四、多模态中常见的SOTA模型有哪些…...

LangChain构建大模型应用之RAG

RAG&#xff08;Retrieval-augmented Generation 检索增强生成&#xff09;是一种结合信息检索与生成模型的技术&#xff0c;通过动态整合外部知识库提升大模型输出的准确性和时效性。其核心思想是在生成答案前&#xff0c;先检索外部知识库中的相关信息作为上下文依据&#xf…...

Git 全面解析:从核心概念到生态应用

Git 一、Git 起源与定位 诞生背景&#xff1a;2005 年由 Linus Torvalds 为管理 Linux 内核开发而设计&#xff0c;因 BitKeeper 许可证争议&#xff0c;急需分布式版本控制系统&#xff08;DVCS&#xff09;替代集中式工具&#xff08;如 SVN&#xff09;。核心优势&#x…...

国产免费工作流引擎star 5.9k,Warm-Flow版本升级1.7.0(新增大量好用功能)

国产免费工作流引擎star 5.9k&#xff0c;Warm-Flow版本升级1.7.0&#xff08;新增大量好用功能&#xff09; 主要更新内容项目介绍功能思维导图设计器流程图演示地址官网Warm-Flow视频 之前大家一直吐槽没有撤销、驳回到上一个任务和拿回等功能&#xff0c;此次版本全都带给大…...

camera知识学习

1、DSP DSP&#xff08;数字信号处理器&#xff09;&#xff0c;这个是介于sensor和ISP处理的一个处理阶段&#xff0c;会进行一些传感器方面的偏硬件处理&#xff0c;再进行数据格式的转换&#xff0c;将raw数据转换成RGB数据或者YUV数据...

Java高频常用工具包汇总

Java高频常用工具包汇总 Java生态系统中有许多广泛使用的工具包&#xff0c;以下是一些高频常用的工具包分类汇总&#xff1a; 1. 核心工具包 Apache Commons系列 Commons Lang - 提供各种基础工具类Commons IO - 文件/IO操作工具Commons Collections - 集合扩展工具Commons …...

蓝桥杯 16. 密文搜索

密文搜索 原题目链接 题目描述 福尔摩斯从 X 星收到一份资料&#xff0c;全部是小写字母组成。 他的助手提供了另一份资料&#xff1a;许多长度为 8 的密码列表。 福尔摩斯发现&#xff0c;这些密码是被打乱后隐藏在先前那份资料中的。 请你编写一个程序&#xff0c;从第…...

Spring Boot 中多线程的基础使用

1. 核心机制 Spring Boot 通过 TaskExecutor 和 Async 注解支持多线程编程&#xff0c;结合线程池管理&#xff0c;有效提升应用性能。核心组件包括&#xff1a; EnableAsync&#xff1a;启用异步任务支持。 Async&#xff1a;标记方法为异步执行。 ThreadPoolTaskExecutor&…...

660SJBH企业信息管理系统

第一章 问题来源 1.1 课题提出背景和意义 由于企业规模进一步扩大&#xff0c;企业信息的管理也变得越来越复杂。为此&#xff0c;切实有效的把企业信息管理系统引入企业管理领域中&#xff0c;对于促进企业管理制度和提高企业质量有着显着意义。 Internet的发展使我们的企业…...

OpenCV实验室工具的使用

OpenCV实验室工具是一个调用OpenCV常见函数&#xff0c;让用户调整参数&#xff0c;快速得到试验结果的工具软件。 软件界面中包含三列&#xff0c;第一列是功能菜单&#xff0c;第二列是实现某一功能时需要输入的参数&#xff0c;第三列是图像处理历史。 OpenCV实验室包含了常…...

月之暗面开源-音频理解、生成和对话生成模型:Kimi-Audio-7B-Instruct

一、Kimi - Audio 简介 Kimi - Audio 是一个开源的音频基础模型&#xff0c;在音频理解、生成和对话等方面表现出色。其设计旨在作为一个通用的音频基础模型&#xff0c;能够在单一统一的框架内处理各种音频处理任务&#xff0c;如语音识别&#xff08;ASR&#xff09;、音频问…...

依赖于切片级标签,结合信息瓶颈理论,对弱监督病理切片分类模型进行微调

小罗碎碎念 在医学AI领域&#xff0c;病理全切片图像&#xff08;WSI&#xff09;分析意义重大&#xff0c;但面临诸多难题。 高分辨率的WSI使得获取精确注释极为困难&#xff0c;且计算成本高昂。 多实例学习&#xff08;MIL&#xff09;虽能利用WSI级弱监督缓解注释压力&…...

UE5 NDisplay 单主机打包运行

前言 最近在做UE的左右眼双屏输出&#xff0c;找了半天只有近年来比较火的NDispaly可以做这件事了&#xff0c;看了一下官方的教程写的很全面&#xff0c;但是相对笼统了一些&#xff0c;发现B站和一些博客了也写了有&#xff0c;但是我建议还是好好过一遍官方文档吧&#xff0…...

Kubernetes/KubeSphere 安装踩坑记:从 context deadline exceeded 到成功部署的完整排障笔记

目录 Kubernetes/KubeSphere 安装踩坑记&#xff1a;从 context deadline exceeded 到成功部署的完整排障笔记 一、问题现象 二、第一手日志采集 三、定位思路 四、分步解决 4-1 处理 pause:3.8 4-2 处理 kube-apiserver:v1.31.0 五、再次安装并验证 六、经验总结 七…...

SpringMVC 静态资源处理 mvc:default-servlet-handler

我们先来看看效果,当我把这一行注释掉的时候&#xff1a; 我们来看看页面&#xff1a; 现在我把注释去掉&#xff1a; 、 可以看到的是&#xff0c;这个时候又可以访问了 那么我们就可以想&#xff0c;这个 <mvc:default-servlet-handler />它控制着我们页面的访问…...

2、Linux操作系统下,ubuntu22.04版本安装搜狗输入法

1.添加中文语言支持&#xff0c;打开此窗口的步骤如下&#xff1a; system setting>language and region>language>install/remove language&#xff0c;之后弹出下面的窗口&#xff0c;点击“reminder me later勾选Chinese&#xff08;simplified&#xff09;&#…...

go语言八股文(四)

1.go语言中defer的变量快照在什么情况下会生效 1. 变量在 defer 被注册时的值被捕获 当 defer 被注册时&#xff0c;它会捕获变量在那一刻的值。如果变量是值类型&#xff08;如基本类型、结构体等&#xff09;&#xff0c;defer 会捕获该值的副本&#xff1b;如果变量是指针类…...