【音視頻】FFmpeg解封裝

解封裝

復用器,比如MP4/FLV

在這里插入圖片描述

解復用器,MP4/FLV

在這里插入圖片描述

封裝格式相關函數

  • avformat_alloc_context(); 負責申請一個AVFormatContext結構的內存,并進行簡單初始化
  • avformat_free_context(); 釋放該結構里的所有東西以及該結構本身
  • avformat_close_input();關閉解復用器。關閉后就不再需要使用avformat_free_context 進行釋放。
  • avformat_open_input(); 打開輸入視頻文件
  • avformat_find_stream_info(): 獲取視頻文件信息
  • av_read_frame(); 讀取音視頻包
  • avformat_seek_file(); 定位文件
  • av_seek_frame(): 定位文件

解封裝流程

在這里插入圖片描述

FFmpeg數據結構之間的關系

區分不同的碼流
  • AVMEDIA_TYPE_VIDEO視頻流
video_index = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,-1,-1, NULL, 0)
  • AVMEDIA_TYPE_AUDIO 音頻流
video_index = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,-1,-1, NULL, 0)

AVPacket 里面也有一個index的字段,這個字段存儲對應的一個流

重點

  • avformat_open_inputavformat_find_stream_info分別用于打開一個流和分析流信息。
  • 在初始信息不足的情況下(比如FLVH264文件),avformat_find_stream_info接口需要在內部調用read_frame_internal接口讀取流數據(音視頻幀),然后再分析后,設置核心數據結構AVFormatContext
  • 由于需要讀取數據包,avformat_find_stream_info接口會帶來很大的延遲

實現流程

添加視頻文件
  • build路徑下添加相關mp4flvts文件
    在這里插入圖片描述

  • 設置參數輸入

![[Pasted image 20250330141825.png|600]]、

avformat_open_input
int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);

這個函數主要是用于打開輸入的媒體文件或流,并對相關上下文進行初始化:

  • AVFormatContext結構體分配內存,然后將其地址存儲在 *ps 中,后續對媒體文件的操作,如讀取數據包、查找流信息等,都要基于這個上下文。

    • 輸入媒體文件的文件名或 URL。可以是本地文件的路徑,例如 "/path/to/your/file.mp4";也可以是網絡流的 URL,比如 "http://example.com/stream.m3u8"
  • 第三個參數用于指定輸入文件的格式。通常情況下設置為 NULL,讓 FFmpeg 自動探測文件格式。不過,在某些特殊情況下,如果你明確知道文件的格式,也可以指定具體的 AVInputFormat 類型。

  • 最后一個參數是指向 AVDictionary 指針的指針,用于傳遞額外的選項,例如編解碼器選項、協議選項等。如果不需要傳遞額外選項,可以將其設置為 NULL

  • 輸入為mp4文件的時候,這一步可以獲取很多信息,如編解碼ID、媒體持續時間等

![[Pasted image 20250330142428.png|200]]

  • 輸入文件為flv,則無法獲取較多信息,因為flv頭部信息不足

比如這里的duration沒有被設置,是隨機值

在這里插入圖片描述

  • 輸入為ts,效果和flv類似,但信息比flv多一點

在這里插入圖片描述

avformat_find_stream_info

這個函數主要是用于分析輸入媒體的流信息,為上下文結構體補充信息,比如比特率等

  • 在調用 avformat_open_input 打開媒體文件后,AVFormatContext 中僅包含了一些基本的文件信息,而各個流(如音頻流、視頻流、字幕流等)的詳細信息,像編碼格式、幀率、采樣率、分辨率等,往往還未被解析出來。avformat_find_stream_info 函數的主要功能就是讀取媒體文件的一定數量的數據包,對這些數據包進行分析,從而填充 AVFormatContext 中各流的詳細信息。
ret = avformat_find_stream_info(ifmt_ctx, NULL);

參考下圖,執行后將bite_rateduration等信息補充到了上下文中

在這里插入圖片描述

如果一開始頭部信息不足,調用這個函數比較耗費時間,因為需要在內部讀入視頻幀進行分析

  • ts文件
    在這里插入圖片描述

  • flv文件

請添加圖片描述

mp4文件

在這里插入圖片描述

可以發現,是存在延遲的,如果文件容量更大的話,延遲可能會更大

av_dump_format

這個函數將上下文信息打印出來

av_dump_format(ifmt_ctx, 0, in_filename, 0);
  • 使用avformat_open_input后打印上下文信息

請添加圖片描述

  • 使用avformat_find_stream_info后打印信息

請添加圖片描述

這里還可以將上下文結構體的內容打印出來:
請添加圖片描述

 // url: 調用avformat_open_input讀取到的媒體文件的路徑/名字printf("media name:%s\n", ifmt_ctx->url);// nb_streams: nb_streams媒體流數量printf("stream number:%d\n", ifmt_ctx->nb_streams);// bit_rate: 媒體文件的碼率,單位為bpsprintf("media average ratio:%lldkbps\n",(int64_t)(ifmt_ctx->bit_rate/1024));// 時間int total_seconds, hour, minute, second;// duration: 媒體文件時長,單位微妙total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE;  // 1000us = 1ms, 1000ms = 1秒hour = total_seconds / 3600;minute = (total_seconds % 3600) / 60;second = (total_seconds % 60);//通過上述運算,可以得到媒體文件的總時長printf("total duration: %02d:%02d:%02d\n", hour, minute, second);printf("\n");

注意這里的duration為媒體總時長,單位為微秒,因此轉換為秒需要除以1e6AV_TIME_BASE宏對應就是1e6

![[Pasted image 20250330145622.png|400]]

獲取相應流

可以通過遍歷上下文中對應的二維流數組來找到自己想要的流,比如音頻流和視頻流

AVStream 是 FFmpeg 庫中一個關鍵的結構體,主要用于描述媒體文件中的一個流(例如視頻流、音頻流、字幕流等),結構體內存儲了很多關于流的信息:

AVRational time_baseAVRational 是一個表示分數的結構體,time_base 定義了流中時間戳的基本單位。時間戳(如 PTSDTS)是以 time_base 為單位的。例如,若 time_base{1, 1000},則表示時間戳的單位是 1/1000 秒。在進行時間戳的轉換和計算時,需要使用這個 time_baseAVCodecParameters 結構體包含了流的編解碼器相關的參數信息,如視頻流的分辨率、幀率、像素格式,音頻流的采樣率、聲道數、采樣格式等。這些信息對于選擇合適的解碼器以及進行解碼操作非常重要。

1. int index

此成員表示該流在 AVFormatContextstreams 數組中的索引。在處理多個流的媒體文件時,可以通過這個索引來區分不同的流。例如,在讀取數據包時,AVPacket 結構體中的 stream_index 就會指向這個索引,以此確定該數據包屬于哪個流。

2. AVRational time_base

AVRational 是一個表示分數的結構體,time_base 定義了流中時間戳的基本單位。時間戳(如 PTSDTS)是以 time_base 為單位的。例如,若 time_base{1, 1000},則表示時間戳的單位是 1/1000 秒。在進行時間戳的轉換和計算時,需要使用這個 time_base

3. AVCodecParameters *codecpar

AVCodecParameters 結構體包含了流的編解碼器相關的參數信息,如視頻流的分辨率、幀率、像素格式,音頻流的采樣率、聲道數、采樣格式等。這些信息對于選擇合適的解碼器以及進行解碼操作非常重要。

4. int64_t duration

該成員表示流的總時長,單位是 time_base。要將其轉換為秒,可以使用以下公式:duration_in_seconds = (double)duration * av_q2d(time_base)

5. int64_t start_time

表示流的起始時間,單位同樣是 time_base。在某些情況下,流的起始時間可能不是從 0 開始的,通過這個成員可以獲取到流實際的起始時間。

6. AVRational avg_frame_rateAVRational r_frame_rate
  • avg_frame_rate 表示流的平均幀率,是根據流中所有幀的時間間隔計算得出的平均幀率。
  • r_frame_rate 表示流的實際幀率,通常是固定的幀率,對于一些固定幀率的視頻流,這個值會比較準確。
7. void *priv_data

這是一個指向私有數據的指針,用于存儲特定格式的額外信息。不同的格式可能會使用這個指針來存儲一些自定義的數據,一般情況下不需要直接操作這個指針。

在我們的示例中,通過遍歷上下文所有的流,每個流都有唯一對應的流索引,因此可以通過流中的編解碼參數信息,打印出相應的音視頻格式:

  • 獲取當前索引的流結構體
AVStream *in_stream = ifmt_ctx->streams[i];// 音頻流、視頻流、字幕流
  • 通過編解碼器的參數獲取編解碼類型,返回相應的類型宏定義
  1. 音頻MEDIA_TYPE_AUDIO
if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type)
  1. 視頻 MEDIA_TYPE_VIDEO
else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type)  
  • 如果是音頻,可以打印出相關的音頻信息,如采樣率、采樣格式(如FLTPS16P)、通道數、壓縮格式(如AACMP3)、音頻總時長等
 printf("----- Audio info:\n");// index: 每個流成分在ffmpeg解復用分析后都有唯一的index作為標識printf("index:%d\n", in_stream->index);// sample_rate: 音頻編解碼器的采樣率,單位為Hzprintf("samplerate:%dHz\n", in_stream->codecpar->sample_rate);// codecpar->format: 音頻采樣格式if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format){printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");}else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format){printf("sampleformat:AV_SAMPLE_FMT_S16P\n");}// channels: 音頻信道數目printf("channel number:%d\n", in_stream->codecpar->channels);// codec_id: 音頻壓縮編碼格式if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id){printf("audio codec:AAC\n");}else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id){printf("audio codec:MP3\n");}else{printf("audio codec_id:%d\n", in_stream->codecpar->codec_id);}// 音頻總時長,單位為秒。注意如果把單位放大為毫秒或者微秒,音頻總時長跟視頻總時長不一定相等的if(in_stream->duration != AV_NOPTS_VALUE){int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);//將音頻總時長轉換為時分秒的格式打印到控制臺上printf("audio duration: %02d:%02d:%02d\n",duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));}else{printf("audio duration unknown");}printf("\n");
  • 注意,在計算音頻時長的時候,AVStream中的duration和上下文AVFormatContext中的單位不一樣,這里的單位是時間基time_base,不同的媒體文件可能時間基不同,比如可能是1/1000 s作為一個時間基,那么我們轉換為妙就需要如下操作
    s = A V S t r e a m ? > d u r a t i o n ? a v _ q 2 d ( A V S t r e a m ? > t i m e _ b a s e ) s = AVStream->duration*av\_q2d(AVStream->time\_base) s=AVStream?>duration?av_q2d(AVStream?>time_base)

這里的av_q2d實際上就是將分數形式轉換為double類型的小數形式,因此轉換實質上上就是:duration* time_base

  • 如果是視頻,同樣可以提取出視頻編解碼器的信息,比如視頻幀率(FPS)、視頻壓縮編碼格式(H264MPEG4)、視頻幀的寬高(1080x720),轉換視頻的持續時間的方式與音頻一樣,注意,time_base的值通常不同:

視頻

  • 典型值{1, 25}25 FPS)、{1, 30}30 FPS)、{1, 90000}(精確時間基)
  • 含義:視頻幀的時間間隔以幀率倒數為單位。

音頻

  • 典型值{1, 44100}44.1kHz 采樣率)、{1, 48000}48kHz 采樣率)
  • 含義:音頻幀的時間間隔以采樣周期為單位
printf("----- Video info:\n");printf("index:%d\n", in_stream->index);// avg_frame_rate: 視頻幀率,單位為fps,表示每秒出現多少幀printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //視頻壓縮編碼格式{printf("video codec:MPEG4\n");}else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //視頻壓縮編碼格式{printf("video codec:H264\n");}else{printf("video codec_id:%d\n", in_stream->codecpar->codec_id);}// 視頻幀寬度和幀高度printf("width:%d height:%d\n", in_stream->codecpar->width,in_stream->codecpar->height);//視頻總時長,單位為秒。注意如果把單位放大為毫秒或者微秒,音頻總時長跟視頻總時長不一定相等的if(in_stream->duration != AV_NOPTS_VALUE){int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);printf("video duration: %02d:%02d:%02d\n",duration_video / 3600,(duration_video % 3600) / 60,(duration_video % 60)); //將視頻總時長轉換為時分秒的格式打印到控制臺上}else{printf("video duration unknown");}printf("\n");
獲取相應包(Packet

上下文中還存儲了壓縮的數據包,比如對應的H264AAC壓縮包,我們可以讀取這些壓縮包

  • 首先我們需要為AVPacket結構體分配內存
 AVPacket *pkt = av_packet_alloc();
  • 通過一個循環來依次讀取每一幀的數據包到AVPacket中,每次讀取一幀后,內部的指針都會向后移動
while (1){ret = av_read_frame(ifmt_ctx, pkt);}
  • 判斷數據包內的流索引(視頻流、音頻流),進行相應操作,如打印ptsdts、包的大小size、包對應文件的偏移量pos,以及根據不同的索引在不同AVStream中找到對應的當前幀的持續時間,如下
  1. 音頻幀數據包持續時間
pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base)
  1. 視頻幀數據包持續時間
pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base)
  • 解碼完當前幀數據包后,需要將這一幀數據包釋放,否則會導致內存泄漏,直接調用av_packet_unref減少引用計數即可,引用計數為0會自動釋放幀數據包的buf內存
av_packet_unref(pkt);
  • 讀取所有幀數據包之后,需要釋放AVPacket結構體的內存
if(pkt)av_packet_free(&pkt);
釋放內存

所有操作之后,需要釋放上下文內存,并且關閉打開的文件或關閉對應網絡流的連接

調用 avformat_close_input函數即可實現上述功能

if(ifmt_ctx)avformat_close_input(&ifmt_ctx);

整體代碼

main.c

#include <stdio.h>
#include <libavformat/avformat.h>
#include<time.h>int main(int argc, char **argv)
{//打開網絡流。這里如果只需要讀取本地媒體文件,不需要用到網絡功能,可以不用加上這一句
//    avformat_network_init();const char *default_filename = "believe.mp4";char *in_filename = NULL;if(argv[1] == NULL){in_filename = default_filename;}else{in_filename = argv[1];}printf("in_filename = %s\n", in_filename);//AVFormatContext是描述一個媒體文件或媒體流的構成和基本信息的結構體AVFormatContext *ifmt_ctx = NULL;           // 輸入文件的demuxint videoindex = -1;        // 視頻索引int audioindex = -1;        // 音頻索引// 打開文件,主要是探測協議類型,如果是網絡文件則創建網絡鏈接int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);if (ret < 0)  //如果打開媒體文件失敗,打印失敗原因{char buf[1024] = { 0 };av_strerror(ret, buf, sizeof(buf) - 1);printf("open %s failed:%s\n", in_filename, buf);goto failed;}printf_s("\n==== av_dump_format in_filename:%s ===\n", in_filename);av_dump_format(ifmt_ctx, 0, in_filename, 0);printf_s("\n==== av_dump_format finish =======\n\n");clock_t started = clock();ret = avformat_find_stream_info(ifmt_ctx, NULL);clock_t ended = clock();double elapsed_time = (double)(ended - started) / CLOCKS_PER_SEC;printf("avformat_find_stream_info took %f seconds to execute.\n", elapsed_time);if (ret < 0)  //如果打開媒體文件失敗,打印失敗原因{char buf[1024] = { 0 };av_strerror(ret, buf, sizeof(buf) - 1);printf("avformat_find_stream_info %s failed:%s\n", in_filename, buf);goto failed;}//打開媒體文件成功printf_s("\n==== av_dump_format in_filename:%s ===\n", in_filename);av_dump_format(ifmt_ctx, 0, in_filename, 0);printf_s("\n==== av_dump_format finish =======\n\n");// url: 調用avformat_open_input讀取到的媒體文件的路徑/名字printf("media name:%s\n", ifmt_ctx->url);// nb_streams: nb_streams媒體流數量printf("stream number:%d\n", ifmt_ctx->nb_streams);// bit_rate: 媒體文件的碼率,單位為bpsprintf("media average ratio:%lldkbps\n",(int64_t)(ifmt_ctx->bit_rate/1024));// 時間int total_seconds, hour, minute, second;// duration: 媒體文件時長,單位微妙total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE;  // 1000us = 1ms, 1000ms = 1秒hour = total_seconds / 3600;minute = (total_seconds % 3600) / 60;second = (total_seconds % 60);//通過上述運算,可以得到媒體文件的總時長printf("total duration: %02d:%02d:%02d\n", hour, minute, second);printf("\n");/** 老版本通過遍歷的方式讀取媒體文件視頻和音頻的信息* 新版本的FFmpeg新增加了函數av_find_best_stream,也可以取得同樣的效果*/for (uint32_t i = 0; i < ifmt_ctx->nb_streams; i++){AVStream *in_stream = ifmt_ctx->streams[i];// 音頻流、視頻流、字幕流//如果是音頻流,則打印音頻的信息if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type){printf("----- Audio info:\n");// index: 每個流成分在ffmpeg解復用分析后都有唯一的index作為標識printf("index:%d\n", in_stream->index);// sample_rate: 音頻編解碼器的采樣率,單位為Hzprintf("samplerate:%dHz\n", in_stream->codecpar->sample_rate);// codecpar->format: 音頻采樣格式if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format){printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");}else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format){printf("sampleformat:AV_SAMPLE_FMT_S16P\n");}// channels: 音頻信道數目printf("channel number:%d\n", in_stream->codecpar->channels);// codec_id: 音頻壓縮編碼格式if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id){printf("audio codec:AAC\n");}else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id){printf("audio codec:MP3\n");}else{printf("audio codec_id:%d\n", in_stream->codecpar->codec_id);}// 音頻總時長,單位為秒。注意如果把單位放大為毫秒或者微秒,音頻總時長跟視頻總時長不一定相等的if(in_stream->duration != AV_NOPTS_VALUE){int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);//將音頻總時長轉換為時分秒的格式打印到控制臺上printf("audio duration: %02d:%02d:%02d\n",duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));}else{printf("audio duration unknown");}printf("\n");audioindex = i; // 獲取音頻的索引}else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type)  //如果是視頻流,則打印視頻的信息{printf("----- Video info:\n");printf("index:%d\n", in_stream->index);// avg_frame_rate: 視頻幀率,單位為fps,表示每秒出現多少幀printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //視頻壓縮編碼格式{printf("video codec:MPEG4\n");}else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //視頻壓縮編碼格式{printf("video codec:H264\n");}else{printf("video codec_id:%d\n", in_stream->codecpar->codec_id);}// 視頻幀寬度和幀高度printf("width:%d height:%d\n", in_stream->codecpar->width,in_stream->codecpar->height);//視頻總時長,單位為秒。注意如果把單位放大為毫秒或者微秒,音頻總時長跟視頻總時長不一定相等的if(in_stream->duration != AV_NOPTS_VALUE){int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);printf("video duration: %02d:%02d:%02d\n",duration_video / 3600,(duration_video % 3600) / 60,(duration_video % 60)); //將視頻總時長轉換為時分秒的格式打印到控制臺上}else{printf("video duration unknown");}printf("\n");videoindex = i;}}AVPacket *pkt = av_packet_alloc();int pkt_count = 0;int print_max_count = 10;printf("\n-----av_read_frame start\n");while (1){ret = av_read_frame(ifmt_ctx, pkt);if (ret < 0){printf("av_read_frame end\n");break;}if(pkt_count++ < print_max_count){if (pkt->stream_index == audioindex){printf("audio pts: %lld\n", pkt->pts);printf("audio dts: %lld\n", pkt->dts);printf("audio size: %d\n", pkt->size);printf("audio pos: %lld\n", pkt->pos);printf("audio duration: %lf\n\n",pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base));}else if (pkt->stream_index == videoindex){printf("video pts: %lld\n", pkt->pts);printf("video dts: %lld\n", pkt->dts);printf("video size: %d\n", pkt->size);printf("video pos: %lld\n", pkt->pos);printf("video duration: %lf\n\n",pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base));}else{printf("unknown stream_index:\n", pkt->stream_index);}}av_packet_unref(pkt);}if(pkt)av_packet_free(&pkt);
failed:if(ifmt_ctx)avformat_close_input(&ifmt_ctx);getchar(); //加上這一句,防止程序打印完信息馬上退出return 0;
}

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76706.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76706.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76706.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

1??5??three.js_GUI輔助調試器

15、GUI輔助調試器 3D虛擬工廠在線體驗 GUI輔助調試器將原本需要修改代碼調整參數并刷新頁面的操作&#xff0c;簡化為直接在GUI中實時調整&#xff0c;實現所見即所得的效果。 導入GUI 庫 //引入GUI輔助調試器 import { GUI } from three/addons/libs/lil-gui.module.min.js…

Redis 的指令執行方式:Pipeline、事務與 Lua 腳本的對比

Pipeline 客戶端將多條命令打包發送&#xff0c;服務器順序執行并一次性返回所有結果。可以減少網絡往返延遲&#xff08;RTT&#xff09;以提升吞吐量。 需要注意的是&#xff0c;Pipeline 中的命令按順序執行&#xff0c;但中間可能被其他客戶端的命令打斷。 典型場景&…

Linux下的網絡管理配置

一、 IPv4原理 IPv4&#xff08;Internet Protocol version 4&#xff09;&#xff0c;采用32位地址。IPv4地址通常用點分十進制表示&#xff0c;如 192.168.1.10。 IPv4網絡通信基于數據包交換原理&#xff0c;當一臺主機要向另一臺主機發送數據時&#xff0c;會將數據分割成…

基于Python(Django)+SQLite實現(Web)校園助手

校園助手 本校園助手采用 B/S 架構。并已將其部署到服務器上。在網址上輸入 db.uplei.com 即可訪問。 使用說明 可使用如下賬號體驗&#xff1a; 學生界面: 賬號1&#xff1a;123 密碼1&#xff1a;123 賬戶2&#xff1a;201805301348 密碼2&#xff1a;1 # --------------…

unity動態骨骼架設+常用參數分享(包含部分穿模解決方案)

Unity骨骼物理模擬插件Dynamic Bone Dynamic Bone 可用于對角色的骨骼&#xff08;bones&#xff09;或者鉸鏈系統&#xff08;joints&#xff09;施加物理效果。 物理效果可以使得游戲角色的頭發、衣服、胸部或者是其他的任何部位&#xff0c;都可以以近似真實的狀態運動。 …

科技天眼守望農田:珈和衛星遙感監測賦能智慧農業,護航糧食安全新未來

農情監測與糧食安全密切相關&#xff0c;以往農作物的長勢、環境、病蟲害、災情等相關數據和圖像信息都是靠物聯網硬件及縣、鎮、村等人力來完成&#xff0c;不僅要耗費大量人力、物力&#xff0c;而且數據時效性、準確性較差。珈和科技開發建設農情遙感監測系統&#xff0c;運…

【TeamFlow】4.2 Yew庫詳細介紹

Yew 是一個用于構建高效、交互式前端 Web 應用程序的現代 Rust 框架&#xff0c;它借鑒了 React 和 Elm 等框架的設計理念&#xff0c;同時充分利用 Rust 的語言特性。 核心特性 基于組件的架構 Yew 采用組件化開發模式&#xff0c;類似于 React: 組件是可重用的 UI 構建塊 …

畢設 - 數字孿生智慧農場(vue+高德地圖)項目分享

感興趣的同學可以私信我或者在下方添加我的qq 在線地址: 數字孿生智慧農場

深入理解 VMware 虛擬機網絡模式:為虛擬化管理鋪平道路

隨著云計算和虛擬化技術的快速發展&#xff0c;VMware作為行業領軍者&#xff0c;在企業的IT基礎設施中扮演著越來越重要的角色。無論是開發、測試還是生產環境&#xff0c;虛擬機&#xff08;VM&#xff09;都成為了我們不可或缺的工具。在VMware中&#xff0c;網絡是虛擬機能…

安恒安全滲透面試題

《網安面試指南》https://mp.weixin.qq.com/s/RIVYDmxI9g_TgGrpbdDKtA?token1860256701&langzh_CN 5000篇網安資料庫https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247486065&idx2&snb30ade8200e842743339d428f414475e&chksmc0e4732df793fa3bf39…

代碼隨想錄算法訓練營第五十三天 | 105.有向圖的完全可達性 106.島嶼的周長

105.有向圖的完全可達性 題目鏈接&#xff1a;101. 孤島的總面積 文章講解&#xff1a;代碼隨想錄 視頻講解&#xff1a;圖論&#xff1a;島嶼問題再出新花樣 | 深搜優先搜索 | 卡碼網&#xff1a;101.孤島總面積_嗶哩嗶哩_bilibili 思路&#xff1a; 1.確認遞歸函數&…

藍橋杯 18.分考場

分考場 原題目鏈接 題目描述 有 n 個人參加某項特殊考試。 為了公平&#xff0c;要求任何兩個認識的人不能分在同一個考場。 你的任務是求出最少需要分幾個考場才能滿足這個條件。 輸入描述 第一行&#xff1a;一個整數 n&#xff0c;表示參加考試的人數&#xff08;1 ≤…

分布式光纖測溫技術讓森林火災預警快人一步

2025年春季&#xff0c;多地接連發生森林火災&#xff0c;累計過火面積超 3萬公頃。春季歷來是森林草原火災易發、多發期&#xff0c;加之清明節已到來&#xff0c;生產生活用火活躍&#xff0c;民俗祭祀用火集中&#xff0c;森林火災風險進一步加大。森林防火&#xff0c;人人…

前端筆記-Vue3(上)

學習參考視頻&#xff1a;尚硅谷Vue3入門到實戰&#xff0c;最新版vue3TypeScript前端開發教程_嗶哩嗶哩_bilibili vue3學習目標&#xff1a; VUE 31、Vue3架構與設計理念2、組合式API&#xff08;Composition API&#xff09;3、常用API&#xff1a;ref、reactive、watch、c…

如何增加 Elasticsearch 中的 primary shard 數量

作者&#xff1a;來自 Elastic Kofi Bartlett 探索增加 Elasticsearch 中 primary shard 數量的方法。 更多閱讀&#xff1a; Elasticsearch&#xff1a;Split index API - 把一個大的索引分拆成更多分片 Elasticsearch&#xff1a;通過 shrink API 減少 shard 數量來縮小 El…

基于SA模擬退火算法的車間調度優化matlab仿真,輸出甘特圖和優化收斂曲線

目錄 1.程序功能描述 2.測試軟件版本以及運行結果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于SA模擬退火算法的車間調度優化matlab仿真,輸出甘特圖和優化收斂曲線。輸出指標包括最小平均流動時間&#xff0c;最大完工時間&#xff0c;最小間隙時間。 2…

Spring_MVC 快速入門指南

Spring_MVC 快速入門指南 一、Spring_MVC 簡介 1. 什么是 Spring_MVC&#xff1f; Spring_MVC 是 Spring 框架的一個模塊&#xff0c;用于構建 Web 應用程序。它基于 MVC&#xff08;Model-View-Controller&#xff09;設計模式&#xff0c;將應用程序分為模型&#xff08;M…

爬蟲獲取sku信息需要哪些庫

在使用 Python 爬蟲獲取淘寶商品的 SKU 詳細信息時&#xff0c;通常需要以下幾種庫來完成任務。這些庫各有其用途&#xff0c;可以幫助你更高效地實現爬蟲功能。 1. requests 用途&#xff1a;用于發送 HTTP 請求&#xff0c;獲取網頁內容。 安裝&#xff1a; bash pip insta…

賽靈思Xilinx FPGa XCKU15P?2FFVA1156I AMD Kintex UltraScale+

XCKU15P?2FFVA1156I 是 AMD Kintex UltraScale 系列中的高性能 FPGA&#xff0c;基于 16 nm FinFET UltraScale 架構 制造&#xff0c;兼顧卓越的性能與功耗比&#xff0c;該器件集成 1,143,450 個邏輯單元和 82,329,600 位片上 RAM&#xff0c;配備 1,968 個 DSP 切片&#…

從規則到大模型:知識圖譜信息抽取實體NER與關系RE任務近10年演進發展詳解

摘要: 本文回顧了關系抽取與實體抽取領域的經典與新興模型,清晰地梳理了它們的出現時間與核心創新,并給出在 2025 年不同資源與場景下的最佳實踐推薦。文章引用了 BiLSTM?CRF、BiLSTM?CNN?CRF、SpanBERT、LUKE、KnowBERT、CasRel、REBEL、UIE,大模型抽取 等模型的原始論…