QT开发技术【ffmpeg + QAudioOutput】音乐播放器 - 指南

news/2025/10/6 14:59:58/文章来源:https://www.cnblogs.com/yxysuanfa/p/19127616

一、 介绍 使用ffmpeg 4.2.2

在数字化浪潮席卷全球的当下,音视频内容犹如璀璨繁星,点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频,到在线课堂中知识渊博的专家授课,再到影视平台上扣人心弦的高清大片,音视频以其直观、生动的特性,成为信息传播与娱乐休闲的重要媒介。而在这繁华音视频世界的幕后,有一位低调而强大的“魔法工匠”——FFmpeg。

FFmpeg 是一款声名远扬的开源音视频处理工具集,凭借其跨平台、功能强大等显著优势,在音视频领域占据着不可撼动的地位。它就像一个拥有无数神奇工具的百宝箱,集音视频的录制、编解码、转换、流媒体传输等众多功能于一身,为音视频内容的创作、传播与消费提供了坚实的技术支撑。

对于开发者来说,FFmpeg 宛如一座取之不尽的宝藏。它提供了丰富且易用的 API 接口,能够无缝集成到各类应用程序中。无论是开发一个简洁实用的音视频播放器,还是打造一个功能全面的专业视频编辑软件,FFmpeg 都能凭借其强大的功能和高效的性能,助力开发者快速实现目标,大大降低开发的难度和成本。

在科研领域,FFmpeg 也发挥着重要作用。研究人员可以利用其先进的编解码算法,探索音视频质量优化的新途径;借助其流媒体传输功能,开展网络带宽利用、视频传输延迟等方面的研究,为提升音视频在不同网络环境下的播放体验提供理论依据和技术支持。

此外,FFmpeg 的开源属性更是为其发展注入了源源不断的活力。全球的开发者们汇聚于此,共同参与项目的开发与维护,不断为其增添新功能、修复漏洞,使得 FFmpeg 始终保持着与时俱进的姿态,适应不断变化的音视频市场需求。

下面为你详细介绍使用 FFmpeg 库在 C++ 里播放 MP3 文件的代码流程,整个流程主要包含初始化、打开文件、查找流信息、查找解码器、打开解码器、重采样、播放音频以及资源释放等步骤。

  1. 初始化 FFmpeg 库
    在使用 FFmpeg 库的功能之前,需要对其进行初始化,主要是注册所有可用的编解码器、格式和协议。
#
include <iostream>extern "C" {#include <libavformat/avformat.h>#include <libavcodec/avcodec.h>#include <libswresample/swresample.h>#include <libavutil/avutil.h>}// 初始化 FFmpeg 库avformat_network_init();av_register_all();
  1. 打开输入文件
    使用 avformat_open_input 函数打开 MP3 文件,同时创建 AVFormatContext 结构体来保存文件的格式信息。
AVFormatContext* formatContext =
nullptr
;
const
char* filePath = "example.mp3"
;
if (avformat_open_input(&formatContext, filePath,
nullptr
,
nullptr
) != 0
) {
std::cerr <<
"Could not open input file" << std::endl;
return -1
;
}
  1. 获取流信息
    调用 avformat_find_stream_info 函数获取文件的流信息,例如音频流、视频流等。
if (avformat_find_stream_info(formatContext,
nullptr
) <
0
) {
std::cerr <<
"Could not find stream information" << std::endl;
avformat_close_input(&formatContext)
;
return -1
;
}
  1. 查找音频流
    通过 av_find_best_stream 函数在文件中查找音频流。
int audioStreamIndex = -1
;
AVCodecParameters* codecParameters =
nullptr
;
for (
unsigned
int i = 0
; i < formatContext->nb_streams; i++) {if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {audioStreamIndex = i;codecParameters = formatContext->streams[i]->codecpar;break;}}if (audioStreamIndex == -1) {std::cerr <<"Could not find audio stream" << std::endl;avformat_close_input(&formatContext);return -1;}
  1. 查找并打开解码器
    使用 avcodec_find_decoder 函数查找对应的音频解码器,再用 avcodec_open2 函数打开解码器。
AVCodec* codec = avcodec_find_decoder(codecParameters->codec_id)
;
if (!codec) {
std::cerr <<
"Could not find codec" << std::endl;
avformat_close_input(&formatContext)
;
return -1
;
}
AVCodecContext* codecContext = avcodec_alloc_context3(codec)
;
if (!codecContext) {
std::cerr <<
"Could not allocate codec context" << std::endl;
avformat_close_input(&formatContext)
;
return -1
;
}
if (avcodec_parameters_to_context(codecContext, codecParameters) <
0
) {
std::cerr <<
"Could not copy codec parameters to context" << std::endl;
avcodec_free_context(&codecContext)
;
avformat_close_input(&formatContext)
;
return -1
;
}
if (avcodec_open2(codecContext, codec,
nullptr
) <
0
) {
std::cerr <<
"Could not open codec" << std::endl;
avcodec_free_context(&codecContext)
;
avformat_close_input(&formatContext)
;
return -1
;
}
  1. 初始化重采样上下文
    由于 MP3 文件的音频格式可能与系统播放设备支持的格式不一致,需要使用 swr_alloc_set_opts 函数进行重采样。
SwrContext* swrContext = swr_alloc_set_opts(
nullptr
,
AV_CH_LAYOUT_STEREO, // 输出声道布局
AV_SAMPLE_FMT_S16, // 输出样本格式
codecContext->sample_rate, // 输出采样率
codecContext->channel_layout, // 输入声道布局
codecContext->sample_fmt, // 输入样本格式
codecContext->sample_rate, // 输入采样率
0
,
nullptr
)
;
if (!swrContext || swr_init(swrContext) <
0
) {
std::cerr <<
"Could not initialize resampler" << std::endl;
avcodec_free_context(&codecContext)
;
avformat_close_input(&formatContext)
;
return -1
;
}
  1. 读取、解码和重采样音频数据
    使用 av_read_frame 函数读取音频数据包,再用 avcodec_send_packet 和 avcodec_receive_frame 函数进行解码,最后使用 swr_convert 函数进行重采样。
AVPacket* packet = av_packet_alloc(
)
;
AVFrame* frame = av_frame_alloc(
)
;
while (av_read_frame(formatContext, packet) >= 0
) {
if (packet->stream_index == audioStreamIndex) {
if (avcodec_send_packet(codecContext, packet) <
0
) {
std::cerr <<
"Error sending packet to decoder" << std::endl;
continue
;
}
while (avcodec_receive_frame(codecContext, frame) == 0
) {
// 重采样
uint8_t* outputData[1]
;
int outputSamples = swr_convert(swrContext, outputData, frame->nb_samples,
(
const
uint8_t**
)frame->data, frame->nb_samples)
;
// 这里需要将重采样后的数据发送到音频设备进行播放
// 不同系统的音频播放 API 不同,例如在 Windows 下可以使用 WaveOut 或 WASAPI
}
}
av_packet_unref(packet)
;
}
  1. 释放资源
    在音频播放结束后,需要释放之前分配的所有资源。
av_frame_free(&frame)
;
av_packet_free(&packet)
;
swr_free(&swrContext)
;
avcodec_free_context(&codecContext)
;
avformat_close_input(&formatContext)
;

整个播放 MP3 文件的流程可以概括为:初始化 FFmpeg 库 → 打开输入文件 → 获取流信息 → 查找音频流 → 查找并打开解码器 → 初始化重采样上下文 → 读取、解码和重采样音频数据 → 释放资源。需要注意的是,上述代码中重采样后的数据需要使用相应系统的音频播放 API 发送到音频设备进行播放,不同操作系统的音频播放 API 不同,例如 Windows 下的 WaveOut、WASAPI,Linux 下的 ALSA、PulseAudio 等。

二、效果

在这里插入图片描述

三、读取歌词类

#
pragma once
#
include <QList>#include <QString>// 歌词条目结构体struct stLyricsEntry {int m_nTime;// 时间,单位为毫秒std::string m_strText;// 歌词文本};class CLyricsReader {public:CLyricsReader() =default;bool LoadLrcFile(const QString& strFilePath);std::string GetCurrentLyrics(int nCurrentTime);private:QList<stLyricsEntry> m_lstLyricsEntries;// 歌词条目列表};#include "../Include/LyricsReader.h"#include "QtGui/Include/Conversion.h"#include <QFile>#include <QTextStream>#include <QRegularExpression>#include <QTextCodec>#include <QDebug>bool CLyricsReader::LoadLrcFile(const QString& strFilePath){QFile file(strFilePath);if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){qDebug() <<"Failed to open LRC file:" << strFilePath;return false;}QByteArray data = file.readAll();QTextCodec* codec =nullptr;if (QTextCodec::codecForUtfText(data)) {codec = QTextCodec::codecForName("UTF-8");}else {// 尝试其他常见编码,如 GBKcodec = QTextCodec::codecForLocale();}QString text = codec->toUnicode(data);QTextStream in(&text);QRegularExpression timeRegex(R"(\[(\d+):(\d+\.\d+)\])");while (!in.atEnd()){QString line = in.readLine();QRegularExpressionMatchIterator matchIterator = timeRegex.globalMatch(line);while (matchIterator.hasNext()){QRegularExpressionMatch match = matchIterator.next();int minutes = match.captured(1).toInt();double seconds = match.captured(2).toDouble();int time = static_cast<int>((minutes * 60 + seconds) * 1000);QString text = line.mid(match.capturedEnd());if (!text.isEmpty()){stLyricsEntry entry;entry.m_nTime = time;entry.m_strText = TransUnicode2String(text);m_lstLyricsEntries.append(entry);}}}file.close();// 按时间排序std::sort(m_lstLyricsEntries.begin(), m_lstLyricsEntries.end(), [](const stLyricsEntry& a,const stLyricsEntry& b) {return a.m_nTime < b.m_nTime;});return true;}std::string CLyricsReader::GetCurrentLyrics(int nCurrentTime){for (int i = m_lstLyricsEntries.size() - 1; i >= 0;--i){if (m_lstLyricsEntries[i].m_nTime<= nCurrentTime){return m_lstLyricsEntries[i].m_strText;}}return "";}

三、播放类

#
include "../Include/AudioPlayer.h"
#
include "QtGui/Include/Conversion.h"
#
include <QIODevice>#include <QBuffer>#include <QDebug>extern "C" {#include <libavformat/avformat.h>#include <libavcodec/avcodec.h>#include <libswresample/swresample.h>#include <libavutil/avutil.h>}CAudioPlayer::CAudioPlayer(QObject* parent): QThread(parent){m_eControlType = E_CONTROL_NONE;m_pAudioOutput =nullptr;m_bRun = false;m_nSeekMs = 0;}CAudioPlayer::~CAudioPlayer(){}void CAudioPlayer::Quit(){m_bRun = false;wait();}void CAudioPlayer::Play(const std::string& strFilePath){m_strFilePath = strFilePath;m_eControlType = E_CONTROL_PLAY;if (!isRunning()){m_bRun = true;start();}}void CAudioPlayer::Stop(){if (isRunning()){m_eControlType = E_CONTROL_STOP;}}void CAudioPlayer::Pause(){if (isRunning()){m_eControlType = E_CONTROL_PAUSE;}}void CAudioPlayer::Resume(){if (isRunning()){m_eControlType = E_CONTROL_RESUME;}}void CAudioPlayer::Seek(int nMs){if (isRunning()){m_eControlType = E_CONTROL_SEEK;m_nSeekMs = nMs;}}void CAudioPlayer::run(){if (!InitAudioOutput(44100)){qDebug() <<"InitAudioOutput failed";return;}while (m_bRun){switch (m_eControlType){case E_CONTROL_NONE:{msleep(20);}break;case E_CONTROL_PLAY:{m_eControlType = E_CONTROL_NONE;RunPlay();}break;default:m_eControlType = E_CONTROL_NONE;break;}}}bool CAudioPlayer::InitAudioOutput(int nSampleRate){if (m_pAudioOutput){return false;}QAudioFormat format;format.setSampleRate(nSampleRate);format.setChannelCount(2);format.setSampleSize(16);format.setCodec("audio/pcm");format.setByteOrder(QAudioFormat::LittleEndian);format.setSampleType(QAudioFormat::SignedInt);QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());if (!info.isFormatSupported(format)){qDebug() <<"Raw audio format not supported by backend, cannot play audio.";return false;}m_pAudioOutput = std::make_unique<QAudioOutput>(format);m_pAudioOutput->setBufferSize(100000);return true;}bool CAudioPlayer::CheckControlType(){if (!m_pAudioOutput){return false;}bool bRet = false;if (m_eControlType == E_CONTROL_PAUSE){while (m_eControlType == E_CONTROL_PAUSE){m_pAudioOutput->suspend();msleep(20);}if (m_eControlType == E_CONTROL_RESUME){m_pAudioOutput->resume();}}if (m_eControlType == E_CONTROL_PLAY){bRet = true;if (m_pAudioOutput->state() == QAudio::StoppedState){m_pAudioOutput->stop();}}if (m_eControlType == E_CONTROL_STOP){bRet = true;if (m_pAudioOutput->state() == QAudio::ActiveState){m_pAudioOutput->stop();}}return bRet;}void CAudioPlayer::DebugError(int nError){char cError[1024] = {0};av_strerror(nError, cError,sizeof(cError));qDebug() <<"Error:" << cError;}void CAudioPlayer::RunPlay(){int nRet = 0;int nDestMs = 0;int nCurMs = 0;if (!m_pAudioOutput){qDebug() <<"m_pAudioOutput is nullptr";return;}av_log_set_level(AV_LOG_ERROR);avformat_network_init();AVFormatContext* pAvFormatContext =nullptr;if ((nRet = avformat_open_input(&pAvFormatContext, m_strFilePath.c_str(),nullptr,nullptr)) != 0){qDebug() <<"Could not open input file";return;}AVDictionary* pOpts =nullptr;av_dict_set(&pOpts, "analyzeduration", "2147483647", 0);// 设置最大分析时长av_dict_set(&pOpts, "probesize", "2147483647", 0);// 设置最大探测大小if ((nRet = avformat_find_stream_info(pAvFormatContext, &pOpts)) <0){qDebug() <<"Could not find stream information";av_dict_free(&pOpts);avformat_close_input(&pAvFormatContext);return;}av_dict_free(&pOpts);int nAudioStreamIndex = -1;nAudioStreamIndex = av_find_best_stream(pAvFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1,nullptr, 0);if (nAudioStreamIndex <0){qDebug() <<"Could not find audio stream";avformat_close_input(&pAvFormatContext);return;}qDebug() <<"Audio stream index:" << nAudioStreamIndex;AVCodec *pCodec = avcodec_find_decoder(pAvFormatContext->streams[nAudioStreamIndex]->codecpar->codec_id);AVCodecContext* pCodecContext = avcodec_alloc_context3(pCodec);avcodec_parameters_to_context(pCodecContext, pAvFormatContext->streams[nAudioStreamIndex]->codecpar);nRet = avcodec_open2(pCodecContext, pCodec,nullptr);if (nRet <0){qDebug() <<"Could not open codec";avcodec_free_context(&pCodecContext);avformat_close_input(&pAvFormatContext);return;}SwrContext *pSwrContext =nullptr;pSwrContext = swr_alloc_set_opts(nullptr, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, pCodecContext->sample_rate,pCodecContext->channel_layout, pCodecContext->sample_fmt, pCodecContext->sample_rate, 0,nullptr);swr_init(pSwrContext);nDestMs = av_q2d(pAvFormatContext->streams[nAudioStreamIndex]->time_base) * 1000 * pAvFormatContext->streams[nAudioStreamIndex]->duration;qDebug() <<TransString2Unicode("码率:") << pCodecContext->bit_rate;qDebug() <<TransString2Unicode("格式:") << pCodecContext->sample_fmt;qDebug() <<TransString2Unicode("通道:") << pCodecContext->channels;qDebug() <<TransString2Unicode("采样率:") << pCodecContext->sample_rate;qDebug() <<TransString2Unicode("时长:") << nDestMs;qDebug() <<TransString2Unicode("解码器:") << pCodec->name;Q_EMIT SigDuration(nCurMs, nDestMs);AVPacket* pAvPacket = av_packet_alloc();AVFrame* pAvFrame = av_frame_alloc();m_pAudioOutput->stop();QIODevice* pAudioDevice = m_pAudioOutput->start();while (1){if (CheckControlType()){break;}if (m_eControlType == CAudioPlayer::E_CONTROL_SEEK){av_seek_frame(pAvFormatContext, nAudioStreamIndex, m_nSeekMs / (double)1000.0 / av_q2d(pAvFormatContext->streams[nAudioStreamIndex]->time_base), AVSEEK_FLAG_BACKWARD);m_eControlType = E_CONTROL_NONE;Q_EMIT SigSeekOk();}nRet = av_read_frame(pAvFormatContext, pAvPacket);if (nRet == AVERROR_EOF){// 到达文件末尾,向解码器发送刷新信号nRet = avcodec_send_packet(pCodecContext,nullptr);if (nRet <0){DebugError(nRet);qDebug() <<"Could not send flush packet";}while (nRet >= 0){nRet = avcodec_receive_frame(pCodecContext, pAvFrame);if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF){break;}elseif (nRet <0){DebugError(nRet);break;}// 处理剩余的帧uint8_t* pData[2] = {0};int nByteCnt = pAvFrame->nb_samples * 2 * 2;std::unique_ptr<uint8_t[]>pDestBuf(newuint8_t[nByteCnt]);pData[0] = pDestBuf.get();nRet = swr_convert(pSwrContext, &pData[0], pAvFrame->nb_samples,(constuint8_t**)(pAvFrame->data), pAvFrame->nb_samples);while (m_pAudioOutput->bytesFree() < nByteCnt){if (CheckControlType()){break;}msleep(10);}if (!CheckControlType()){pAudioDevice->write((constchar*)pDestBuf.get(), nByteCnt);}nCurMs = av_q2d(pAvFormatContext->streams[nAudioStreamIndex]->time_base) * 1000 * pAvFrame->pts;Q_EMIT SigDuration(nCurMs, nDestMs);}qDebug() <<"End of file reached";SigDuration(nDestMs, nDestMs);break;}elseif (nRet <0){DebugError(nRet);qDebug() <<"Could not read frame";SigDuration(nDestMs, nDestMs);av_packet_unref(pAvPacket);break;}if (pAvPacket->stream_index == nAudioStreamIndex){if (CheckControlType()){av_packet_unref(pAvPacket);break;}// 发送 AVPacket 到解码器nRet = avcodec_send_packet(pCodecContext, pAvPacket);av_packet_unref(pAvPacket);// 发送后释放 AVPacketif (nRet <0){DebugError(nRet);qDebug() <<"Could not send packet";continue;}// 从解码器接收 AVFramewhile (true){nRet = avcodec_receive_frame(pCodecContext, pAvFrame);if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF){break;}elseif (nRet <0){DebugError(nRet);break;}uint8_t* pData[2] = {0};int nByteCnt = pAvFrame->nb_samples * 2 * 2;std::unique_ptr<uint8_t[]>pDestBuf(newuint8_t[nByteCnt]);pData[0] = pDestBuf.get();nRet = swr_convert(pSwrContext, &pData[0], pAvFrame->nb_samples,(constuint8_t**)(pAvFrame->data), pAvFrame->nb_samples);while (m_pAudioOutput->bytesFree() < nByteCnt){if (CheckControlType()){break;}msleep(10);}if (!CheckControlType()){pAudioDevice->write((constchar*)pDestBuf.get(), nByteCnt);}nCurMs = av_q2d(pAvFormatContext->streams[nAudioStreamIndex]->time_base) * 1000 * pAvFrame->pts;Q_EMIT SigDuration(nCurMs, nDestMs);}}else{av_packet_unref(pAvPacket);// 非音频包,释放}}av_frame_free(&pAvFrame);av_packet_free(&pAvPacket);swr_free(&pSwrContext);avcodec_free_context(&pCodecContext);avformat_close_input(&pAvFormatContext);}

四、主界面类

#
include "../Include/EMusicMainWindow.h"
#
include "ui_EMusicMainWindow.h"
#
include "QtGui/Include/Conversion.h"
#
include <QFileDialog>#include <QThread>#include <QDebug>CEMusicMainWindow::CEMusicMainWindow(QWidget* parent): QMainWindow(parent), ui(std::make_unique<Ui::CEMusicMainWindow>()){ui->setupUi(this);InitUI();}CEMusicMainWindow::~CEMusicMainWindow(){m_AudioPlayer.Stop();m_AudioPlayer.Quit();}void CEMusicMainWindow::InitUI(){m_bIsPlaying = false;m_bSeeking = false;connect(&m_AudioPlayer, &CAudioPlayer::SigDuration,this, &CEMusicMainWindow::SlotUpdateLyricsAndTime);connect(&m_AudioPlayer, &CAudioPlayer::SigSeekOk,this, &CEMusicMainWindow::SlotSeekOk);}void CEMusicMainWindow::on_pushButton_StopOrPlay_clicked(){if (m_strMusicFilePath.isEmpty()){return;}if (m_bIsPlaying){m_bIsPlaying = false;m_AudioPlayer.Pause();ui->pushButton_StopOrPlay->setStyleSheet("image: url(:/Play.png);");}else{m_bIsPlaying = true;m_bSeeking = false;ui->pushButton_StopOrPlay->setStyleSheet("image: url(:/Stop.png);");if (m_strOldMusicFilePath.isEmpty()){m_strOldMusicFilePath = m_strMusicFilePath;m_AudioPlayer.Play(TransUnicode2String(m_strMusicFilePath));}elseif (m_strMusicFilePath != m_strOldMusicFilePath){m_strOldMusicFilePath = m_strMusicFilePath;m_AudioPlayer.Play(TransUnicode2String(m_strMusicFilePath));}elseif(m_AudioPlayer.GetControlType() == CAudioPlayer::E_CONTROL_PAUSE){m_AudioPlayer.Resume();}else{m_strOldMusicFilePath = m_strMusicFilePath;m_AudioPlayer.Play(TransUnicode2String(m_strMusicFilePath));}}}void CEMusicMainWindow::SlotUpdateLyricsAndTime(int nCurrentTime,int nDestTime){std::string strLyrics = m_LyricsReader.GetCurrentLyrics(nCurrentTime);ui->label_Words->setText(TransString2Unicode(strLyrics));if (nCurrentTime == nDestTime && nCurrentTime != 0){m_bIsPlaying = false;ui->pushButton_StopOrPlay->setStyleSheet("image: url(:/Play.png);");}staticint currentMs1 = -1, destMs1 = -1;if (currentMs1 == nCurrentTime && destMs1 == nDestTime){return;}currentMs1 = nCurrentTime;destMs1 = nDestTime;//qDebug() << "onDuration:" << nCurrentTime << nDestTime << m_bSeeking;QString currentTime = QString("%1:%2:%3").arg(currentMs1 / 360000 % 60, 2, 10, QChar('0')).arg(currentMs1 / 6000 % 60, 2, 10, QChar('0')).arg(currentMs1 / 1000 % 60, 2, 10, QChar('0'));QString destTime = QString("%1:%2:%3").arg(destMs1 / 360000 % 60, 2, 10, QChar('0')).arg(destMs1 / 6000 % 60, 2, 10, QChar('0')).arg(destMs1 / 1000 % 60, 2, 10, QChar('0'));ui->label_Process->setText(currentTime + "/" + destTime);if (!m_bSeeking) //未滑动{ui->slider_Seek->setMaximum(nDestTime);ui->slider_Seek->setValue(nCurrentTime);}}void CEMusicMainWindow::SlotSeekOk(){m_bSeeking = false;}void CEMusicMainWindow::on_slider_Seek_sliderPressed(){m_bSeeking = true;}void CEMusicMainWindow::on_slider_Seek_sliderReleased(){m_AudioPlayer.Seek(ui->slider_Seek->value());}void CEMusicMainWindow::on_pushButton_Select_clicked(){m_strMusicFilePath = QFileDialog::getOpenFileName(this, TransString2Unicode("选择音乐文件"), "", "Music Files (*.mp3)");if (!m_strMusicFilePath.isEmpty()){m_LyricsReader.LoadLrcFile(m_strMusicFilePath.left(m_strMusicFilePath.lastIndexOf('.')) + ".lrc");}on_pushButton_StopOrPlay_clicked();}

五、后续完善

完成其他功能

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

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

相关文章

长春网站建设哪里好杭州软件开发培训学校

场景描述 安装系统时可以进入安装界面&#xff0c;但是无法识别到硬盘&#xff0c;查看服务器硬件均无异常且从bios或者raid配置界面中能正常看到raid信息及硬盘信息&#xff0c;运行lspci 命令查看到服务器有raid卡&#xff0c;但是未加载驱动。 获取驱动程序模块 查看raid…

进程工具类 - C#小函数类推荐

进程工具类 - C#小函数类推荐Posted on 2025-10-06 14:51 lzhdim 阅读(0) 评论(0) 收藏 举报/***进程工具类Austin Liu 刘恒辉Project Manager and Software DesignerE-Mail: lzhdim@163.comBlog: http://lzhdi…

实用指南:React 组件异常捕获机制详解

实用指南:React 组件异常捕获机制详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mon…

win11 为什么我的程序断网就转入导后台进程

win11 为什么我的程序断网就转入导后台进程this->lockdraw 去掉这个就好了

山东大禹建设集团网站263企业邮箱手机版登录

实验要求&#xff1a; 1.R2为ISP&#xff0c;只能配置IP 2.R1-R2之间为HDLC封装 3.R2-R3之间为PPP封装&#xff0c;pap认证&#xff0c;R2为主认证方 4.R2-R4之间为PPP封装&#xff0c;chap认证&#xff0c;R2为主认证方 5.R1、R2、R3构建MGRE&#xff0c;仅R1的IP地址固定…

深入解析:AI与区块链:数据确权与模型共享的未来

深入解析:AI与区块链:数据确权与模型共享的未来pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas",…

国外网页设计欣赏网站做网站用哪个编程语言

上链接&#xff1a; https://download.csdn.net/download/jasonhongcn/89146520

湖北网站建设论文题目要求做seo排名好的网站

## 双塔 题目描述 有n个数字&#xff0c;要求将这n个数字分成两部分&#xff08;两部分可以数字个数不同&#xff09;&#xff0c;使得两部分数字之和的差最小 输入输出格式 输入&#xff1a; 第一行为n 第二行有n个数&#xff0c;即题目中所描述那样 输出&#xff1a; 两部分和…

找人做seo要给网站程序如何看网站是否被降权

使用Clion时&#xff0c;配置的编译器是Visual Studio&#xff0c;当中的Debugger只有选择LLDB。 项目是CMAKE类型&#xff0c;这里启动调试会发现断点无法命中。 先检查项目的CMakeLists.txt&#xff0c;发现如下配置会影响&#xff1a; set(CMAKE_BUILD_TYPE Debug) set(CM…

做网站的职责电子商务营销与传统营销的区别

Android&#xff1a;从通知中删除通知b我已经创建了一个应用程序&#xff0c;并且我设法在android通知栏中添加通知。 现在我需要示例如何从事件通知栏中删除该通知&#xff1f;11个解决方案197 votes你可以尝试这个快速代码public static void cancelNotification(Context ctx…

10.6阅读笔记

我正在尝试用继承来重构一些重复代码,正好与这本书的第二章内容产生了强烈共鸣。这一章读下来,感觉就像是给我的编程习惯做了一次“大扫除”。 ​​1. 重复的邪恶(DRY原则)—— 一次痛苦的领悟​​ “不要重复你自…

详细介绍:Qwen2.5-VL 损失函数

详细介绍:Qwen2.5-VL 损失函数pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&quo…

hetao 国庆

Day 5 T3 Solution Subtask 1 可能比较具有启发性。 每个人只关心:每条路径上的最大值。我们要让这个值最小。所以容易发现他们只会在最小瓶颈树上走。 由于 MST 一定是最小瓶颈树,所以我们跑 kruskal 然后把树建出来…

详细介绍:AI健康小屋+微高压氧舱:科技如何重构我们的健康防线?

详细介绍:AI健康小屋+微高压氧舱:科技如何重构我们的健康防线?2025-10-06 14:34 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !im…

网站制作关键技术服务器可以备案别人的域名吗

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼/*程序功能&#xff1a;计算一个正整数n的阶乘&#xff0c;目前最大能运算10000的阶乘&#xff0c;可秒杀。程序意义&#xff1a;加强自己对于大数的处理。说明&#xff1a;此程序对乘法和除法还未做任何优化&#xff0c;如果用上位…

详细介绍:运维 pgsql 安装完后某次启动不了

详细介绍:运维 pgsql 安装完后某次启动不了pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &quo…

廊坊高端品牌网站建设广州西樵网站制作

基坑整体坍塌不亲身经历&#xff0c;不知其恐怖▼前段时间&#xff0c;南宁绿地中央广场房地产项目D号地块(二期)基坑北侧约60米支护桩突然崩塌&#xff01;所幸无人伤亡。深基坑施工安全生产管理要点一、基坑开挖 1、 临边防护(1)基坑施工必须按要求进行&#xff0c;具体临边防…

visual studio

Microsoft Visual Studio 无法启动程序” D:\install\installedLibllibigl-2.1.0\build\x64\Debug\INSTALL" 系统找不到指定的文件。

[MCP] StreamableHTTPServer

远程通信方式 通信方式:Stdio: 推荐,高效、简洁、本地 Streamable HTTP: 远程前置知识 SSE 全称 Server-Sent Events,中文是“服务器发送事件”。是一种基于 HTTP 的单向通信协议,由浏览器发起连接,服务器可以持…

HttpServletResponse 对象用来做什么? - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …