FFmpeg+javacpp中仿ffplay播放

FFmpeg+javacpp中仿ffplay播放

  • 1、[ffplay 基于 SDL 和 FFmpeg 庫的簡單媒體播放器](https://ffmpeg.org/ffplay.html)
  • 2、FFmpeg幀捕獲器 : FFmpegFrameGrabber
    • 2.1 grabSamples()
    • 2.2 grabImage()
    • 2.3 grab() 獲取音視頻幀

FFmpeg+javacpp+javacv使用
ffmpeg-6.0\fftools\ffplay.c

1、ffplay 基于 SDL 和 FFmpeg 庫的簡單媒體播放器

ffmpeg-6.0\fftools\ffplay.cFFplay是一款使用FFmpeg庫SDL庫的非常簡單便攜的媒體播放器。它主要用作各種FFmpeg API的測試平臺。

parse_options(NULL, argc, argv, options, opt_input_file);
is = stream_open(input_filename, file_iformat);
event_loop(is);
實質刷新每一幀流refresh_loop_wait_event(cur_stream, &event);video_refresh獲取,對應到 FFmpeg幀捕獲器 : FFmpegFrameGrabber

ffplay -i “G:\視頻\動漫\長安三萬里2023.mp4” -hide_banner -x 800 -y 600

/* Called from the main */
int main(int argc, char **argv)
{int flags;VideoState *is;init_dynload();av_log_set_flags(AV_LOG_SKIP_REPEATED);parse_loglevel(argc, argv, options);/* register all codecs, demux and protocols */
#if CONFIG_AVDEVICEavdevice_register_all();
#endifavformat_network_init();signal(SIGINT , sigterm_handler); /* Interrupt (ANSI).    */signal(SIGTERM, sigterm_handler); /* Termination (ANSI).  */show_banner(argc, argv, options);parse_options(NULL, argc, argv, options, opt_input_file);if (!input_filename) {show_usage();av_log(NULL, AV_LOG_FATAL, "An input file must be specified\n");av_log(NULL, AV_LOG_FATAL,"Use -h to get full help or, even better, run 'man %s'\n", program_name);exit(1);}if (display_disable) {video_disable = 1;}flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;if (audio_disable)flags &= ~SDL_INIT_AUDIO;else {/* Try to work around an occasional ALSA buffer underflow issue when the* period size is NPOT due to ALSA resampling by forcing the buffer size. */if (!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"))SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1", 1);}if (display_disable)flags &= ~SDL_INIT_VIDEO;if (SDL_Init (flags)) {av_log(NULL, AV_LOG_FATAL, "Could not initialize SDL - %s\n", SDL_GetError());av_log(NULL, AV_LOG_FATAL, "(Did you set the DISPLAY variable?)\n");exit(1);}SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);SDL_EventState(SDL_USEREVENT, SDL_IGNORE);if (!display_disable) {int flags = SDL_WINDOW_HIDDEN;if (alwaysontop)
#if SDL_VERSION_ATLEAST(2,0,5)flags |= SDL_WINDOW_ALWAYS_ON_TOP;
#elseav_log(NULL, AV_LOG_WARNING, "Your SDL version doesn't support SDL_WINDOW_ALWAYS_ON_TOP. Feature will be inactive.\n");
#endifif (borderless)flags |= SDL_WINDOW_BORDERLESS;elseflags |= SDL_WINDOW_RESIZABLE;#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITORSDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
#endifwindow = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags);SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");if (window) {renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);if (!renderer) {av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());renderer = SDL_CreateRenderer(window, -1, 0);}if (renderer) {if (!SDL_GetRendererInfo(renderer, &renderer_info))av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", renderer_info.name);}}if (!window || !renderer || !renderer_info.num_texture_formats) {av_log(NULL, AV_LOG_FATAL, "Failed to create window or renderer: %s", SDL_GetError());do_exit(NULL);}}is = stream_open(input_filename, file_iformat);if (!is) {av_log(NULL, AV_LOG_FATAL, "Failed to initialize VideoState!\n");do_exit(NULL);}event_loop(is);/* never returns */return 0;
}
static void video_refresh(void *opaque, double *remaining_time)
{VideoState *is = opaque;double time;Frame *sp, *sp2;if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime)check_external_clock_speed(is);if (!display_disable && is->show_mode != SHOW_MODE_VIDEO && is->audio_st) {time = av_gettime_relative() / 1000000.0;if (is->force_refresh || is->last_vis_time + rdftspeed < time) {video_display(is);is->last_vis_time = time;}*remaining_time = FFMIN(*remaining_time, is->last_vis_time + rdftspeed - time);}if (is->video_st) {
retry:if (frame_queue_nb_remaining(&is->pictq) == 0) {// nothing to do, no picture to display in the queue} else {double last_duration, duration, delay;Frame *vp, *lastvp;/* dequeue the picture */lastvp = frame_queue_peek_last(&is->pictq);vp = frame_queue_peek(&is->pictq);if (vp->serial != is->videoq.serial) {frame_queue_next(&is->pictq);goto retry;}if (lastvp->serial != vp->serial)is->frame_timer = av_gettime_relative() / 1000000.0;if (is->paused)goto display;/* compute nominal last_duration */last_duration = vp_duration(is, lastvp, vp);delay = compute_target_delay(last_duration, is);time= av_gettime_relative()/1000000.0;if (time < is->frame_timer + delay) {*remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);goto display;}is->frame_timer += delay;if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)is->frame_timer = time;SDL_LockMutex(is->pictq.mutex);if (!isnan(vp->pts))update_video_pts(is, vp->pts, vp->pos, vp->serial);SDL_UnlockMutex(is->pictq.mutex);if (frame_queue_nb_remaining(&is->pictq) > 1) {Frame *nextvp = frame_queue_peek_next(&is->pictq);duration = vp_duration(is, vp, nextvp);if(!is->step && (framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) && time > is->frame_timer + duration){is->frame_drops_late++;frame_queue_next(&is->pictq);goto retry;}}if (is->subtitle_st) {while (frame_queue_nb_remaining(&is->subpq) > 0) {sp = frame_queue_peek(&is->subpq);if (frame_queue_nb_remaining(&is->subpq) > 1)sp2 = frame_queue_peek_next(&is->subpq);elsesp2 = NULL;if (sp->serial != is->subtitleq.serial|| (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))|| (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000)))){if (sp->uploaded) {int i;for (i = 0; i < sp->sub.num_rects; i++) {AVSubtitleRect *sub_rect = sp->sub.rects[i];uint8_t *pixels;int pitch, j;if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {for (j = 0; j < sub_rect->h; j++, pixels += pitch)memset(pixels, 0, sub_rect->w << 2);SDL_UnlockTexture(is->sub_texture);}}}frame_queue_next(&is->subpq);} else {break;}}}frame_queue_next(&is->pictq);is->force_refresh = 1;if (is->step && !is->paused)stream_toggle_pause(is);}
display:/* display picture */if (!display_disable && is->force_refresh && is->show_mode == SHOW_MODE_VIDEO && is->pictq.rindex_shown)video_display(is);}is->force_refresh = 0;if (show_status) {AVBPrint buf;static int64_t last_time;int64_t cur_time;int aqsize, vqsize, sqsize;double av_diff;cur_time = av_gettime_relative();if (!last_time || (cur_time - last_time) >= 30000) {aqsize = 0;vqsize = 0;sqsize = 0;if (is->audio_st)aqsize = is->audioq.size;if (is->video_st)vqsize = is->videoq.size;if (is->subtitle_st)sqsize = is->subtitleq.size;av_diff = 0;if (is->audio_st && is->video_st)av_diff = get_clock(&is->audclk) - get_clock(&is->vidclk);else if (is->video_st)av_diff = get_master_clock(is) - get_clock(&is->vidclk);else if (is->audio_st)av_diff = get_master_clock(is) - get_clock(&is->audclk);av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);av_bprintf(&buf,"%7.2f %s:%7.3f fd=%4d aq=%5dKB vq=%5dKB sq=%5dB f=%"PRId64"/%"PRId64"   \r",get_master_clock(is),(is->audio_st && is->video_st) ? "A-V" : (is->video_st ? "M-V" : (is->audio_st ? "M-A" : "   ")),av_diff,is->frame_drops_early + is->frame_drops_late,aqsize / 1024,vqsize / 1024,sqsize,is->video_st ? is->viddec.avctx->pts_correction_num_faulty_dts : 0,is->video_st ? is->viddec.avctx->pts_correction_num_faulty_pts : 0);if (show_status == 1 && AV_LOG_INFO > av_log_get_level())fprintf(stderr, "%s", buf.str);elseav_log(NULL, AV_LOG_INFO, "%s", buf.str);fflush(stderr);av_bprint_finalize(&buf, NULL);last_time = cur_time;}}
}

2、FFmpeg幀捕獲器 : FFmpegFrameGrabber

JavaCV 1.5.12 API
JavaCPP Presets for FFmpeg 7.1.1-1.5.12 API

在這里插入圖片描述

2.1 grabSamples()

grabber.grabSamples() 獲取音頻信息
javax.sound.sampled.AudioFormat 設置PCM編碼的AudioFormat參數,注意每個樣本中的位數,無法獲取,一般默認設置16位深。

package org.xhbruce.test;import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.xhbruce.Log.XLog;import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;public class AudioPlayer implements Runnable {private String filePath;public AudioPlayer(String filePath) {this.filePath = filePath;}public void playAudio() throws Exception {FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filePath);grabber.start();int sampleRate = grabber.getSampleRate();int audioChannels = grabber.getAudioChannels();int audioBitsPerSample = getAudioBitsPerSample(grabber);XLog.d("audioBitsPerSample=" + audioBitsPerSample);if (audioBitsPerSample <= 0) {audioBitsPerSample = 16; // 默認使用16位深度}AudioFormat audioFormat = new AudioFormat((float) sampleRate,audioBitsPerSample,audioChannels,true,false);DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);line.open(audioFormat);line.start();Frame frame;while ((frame = grabber.grabSamples()) != null) {if (frame.samples == null || frame.samples.length == 0) continue;Buffer buffer = frame.samples[0];byte[] audioBytes;if (buffer instanceof ShortBuffer) {ShortBuffer shortBuffer = (ShortBuffer) buffer;audioBytes = new byte[shortBuffer.remaining() * 2]; // Each short is 2 bytesfor (int i = 0; i < shortBuffer.remaining(); i++) {short value = shortBuffer.get(i);audioBytes[i * 2] = (byte) (value & 0xFF);audioBytes[i * 2 + 1] = (byte) ((value >> 8) & 0xFF);}} else if (buffer instanceof ByteBuffer) {ByteBuffer byteBuffer = (ByteBuffer) buffer;audioBytes = new byte[byteBuffer.remaining()];byteBuffer.get(audioBytes);} else {throw new IllegalArgumentException("Unsupported buffer type: " + buffer.getClass());}line.write(audioBytes, 0, audioBytes.length);}line.drain();line.stop();line.close();grabber.stop();}private int getAudioBitsPerSample(FFmpegFrameGrabber grabber) {AVFormatContext formatContext = grabber.getFormatContext();if (formatContext == null) {return -1;}int nbStreams = formatContext.nb_streams();for (int i = 0; i < nbStreams; i++) {AVStream stream = formatContext.streams(i);if (stream.codecpar().codec_type() == avutil.AVMEDIA_TYPE_AUDIO) {AVCodecParameters codecParams = stream.codecpar();return codecParams.bits_per_raw_sample();}}return -1;}@Overridepublic void run() {try {playAudio();} catch (Exception e) {throw new RuntimeException(e);}}public static void main(String[] args) {
//        new Thread(new AudioPlayer("G:\\視頻\\動漫\\長安三萬里2023.mp4")).start();new Thread(new AudioPlayer("F:\\Music\\Let Me Down Slowly.mp3")).start();}
}

2.2 grabImage()

grabImage() 獲取視頻信息
JFrame、JLabel圖片顯示

package org.xhbruce.test;import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;import javax.swing.*;
import java.awt.*;public class VideoPlayer extends JFrame implements Runnable {private String filePath;private JLabel videoLabel;public VideoPlayer(String title, String filePath) {super(title);this.filePath = filePath;setSize(800, 600);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setLocationRelativeTo(null);videoLabel = new JLabel();getContentPane().add(videoLabel, BorderLayout.CENTER);}public void playVideo() throws Exception {FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filePath);grabber.start();int width = grabber.getImageWidth();int height = grabber.getImageHeight();Java2DFrameConverter converter = new Java2DFrameConverter();Frame frame;while ((frame = grabber.grabImage()) != null) {if (frame.image == null) continue;Image image = converter.convert(frame);int windowWidth = getWidth();int windowHeight = getHeight();float scaleX = (float) width / windowWidth;float scaleY = (float) height / windowHeight;float maxScale = Math.max(scaleX, scaleY);ImageIcon icon = new ImageIcon(image.getScaledInstance((int) (width / maxScale), (int) (height / maxScale), Image.SCALE_SMOOTH));SwingUtilities.invokeLater(() -> videoLabel.setIcon(icon));Thread.sleep(40); // 控制幀率}grabber.stop();}@Overridepublic void run() {try {setVisible(true);playVideo();} catch (Exception e) {throw new RuntimeException(e);}}public static void main(String[] args) {String mp4Url = "G:\\視頻\\動漫\\長安三萬里2023.mp4";new Thread(new VideoPlayer("JavaCV Video Player", mp4Url)).start();}
}

2.3 grab() 獲取音視頻幀

將上面組合處理,但存在同步卡頓問題,音頻尤其嚴重,建議音視頻不同線程處理。

package org.xhbruce.test;import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.xhbruce.Log.XLog;import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;public class SimpleFFPlay extends JFrame implements Runnable {private String filePath;private JLabel videoLabel;public SimpleFFPlay(String filePath) {this.filePath = filePath;setTitle("Simple FFPlay - JavaCV");setSize(800, 600);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setLocationRelativeTo(null);videoLabel = new JLabel();getContentPane().add(videoLabel, BorderLayout.CENTER);setVisible(true);}@Overridepublic void run() {FFmpegFrameGrabber grabber = null;try {grabber = FFmpegFrameGrabber.createDefault(filePath);} catch (FFmpegFrameGrabber.Exception e) {throw new RuntimeException(e);}Java2DFrameConverter converter = new Java2DFrameConverter();try {grabber.start();//grabber.setFrameRate(30);// 音頻播放初始化int sampleRate = grabber.getSampleRate();int audioChannels = grabber.getAudioChannels();int audioBitsPerSample = getAudioBitsPerSample(grabber);XLog.d("sampleRate="+sampleRate+", audioChannels="+audioChannels+", audioBitsPerSample=" + audioBitsPerSample+"; getAudioBitrate="+grabber.getAudioBitrate());if (audioBitsPerSample <= 0) {audioBitsPerSample = 16; // 默認使用16位深度}AudioFormat audioFormat = new AudioFormat((float) sampleRate,audioBitsPerSample/*grabber.getAudioBitsPerSample()*/,audioChannels,true,false);DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat, 4096);SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info);//SourceDataLine audioLine = AudioSystem.getSourceDataLine(audioFormat);audioLine.open(audioFormat);audioLine.start();// 主循環讀取幀Frame frame;while ((frame = grabber.grab()) != null) {if (frame.image != null) {// 視頻幀BufferedImage image = converter.getBufferedImage(frame);int width = grabber.getImageWidth();int height = grabber.getImageHeight();paintFrame(image, width, height);}if (frame.samples != null && frame.samples.length > 0) {Buffer buffer = frame.samples[0];byte[] audioBytes;if (buffer instanceof ShortBuffer) {ShortBuffer shortBuffer = (ShortBuffer) buffer;audioBytes = new byte[shortBuffer.remaining() * 2]; // Each short is 2 bytesfor (int i = 0; i < shortBuffer.remaining(); i++) {short value = shortBuffer.get(i);audioBytes[i * 2] = (byte) (value & 0xFF);audioBytes[i * 2 + 1] = (byte) ((value >> 8) & 0xFF);}} else if (buffer instanceof ByteBuffer) {ByteBuffer byteBuffer = (ByteBuffer) buffer;audioBytes = new byte[byteBuffer.remaining()];byteBuffer.get(audioBytes);} else {throw new IllegalArgumentException("Unsupported buffer type: " + buffer.getClass());}audioLine.write(audioBytes, 0, audioBytes.length);}Thread.sleep(40); // 控制幀率}audioLine.drain();audioLine.stop();audioLine.close();grabber.stop();} catch (Exception e) {e.printStackTrace();}}private int getAudioBitsPerSample(FFmpegFrameGrabber grabber) {AVFormatContext formatContext = grabber.getFormatContext();if (formatContext == null) {return -1;}int nbStreams = formatContext.nb_streams();for (int i = 0; i < nbStreams; i++) {AVStream stream = formatContext.streams(i);if (stream.codecpar().codec_type() == avutil.AVMEDIA_TYPE_AUDIO) {AVCodecParameters codecParams = stream.codecpar();return codecParams.bits_per_raw_sample();}}return -1;}private void paintFrame(BufferedImage image, int width, int height) {int windowWidth = getWidth();int windowHeight = getHeight();float scaleX = (float) width / windowWidth;float scaleY = (float) height / windowHeight;float maxScale = Math.max(scaleX, scaleY);//XLog.d("width="+width+", height="+height+"; windowWidth="+windowWidth+", windowHeight="+windowHeight+"; scaleX="+scaleX+", "+scaleY);ImageIcon icon = new ImageIcon(image.getScaledInstance((int) (width/maxScale), (int) (height/maxScale), Image.SCALE_SMOOTH));SwingUtilities.invokeLater(() -> {
//            setSize(800, 600);videoLabel.setIcon(icon);});}public static void main(String[] args) {if (args.length < 1) {System.out.println("Usage: java SimpleFFPlay <video_file>");return;}new Thread(new SimpleFFPlay(args[0])).start();}
}

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

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

相關文章

【后端】 FastAPI

&#x1f680; FastAPI 是什么&#xff1f;FastAPI 是一個用于構建 Web API 的 Python 框架。可以理解成&#xff1a;&#x1f9f0; “一個工具箱&#xff0c;讓你用 Python 寫出能被瀏覽器、App、小程序調用的接口&#xff08;API&#xff09;。”&#x1f527; 那什么是 API&…

不畫一張架構圖講透架構思維

&#x1f449;目錄1 架構的定義2 架構是為了解無解的問題-分工3 抱殘守缺的好架構應該是怎樣的4 適可而止的設計、恰如其分的架構與成敗論英雄本文深入探討軟件架構的本質與設計方法論&#xff0c;從架構定義演變到現代架構實踐挑戰&#xff0c;系統分析架構設計面臨的業務復雜…

SpringCloudGateWay 使用nacos網關自動負載均衡

安裝好nacos后&#xff08;參考以前文章SpringCloud 使用nacos注冊服務&#xff0c;使用openFeign調用服務-CSDN博客&#xff09; 新建一個項目&#xff0c;添加 spring-cloud-starter-gateway-server-webmvc spring-cloud-loadbalancer spring-cloud-starter-alibaba-nacos-d…

Hiredis 構建 Redis 命令實戰指南

一、同步命令構造 1.1 redisCommand(fmt, …) 最常用的同步接口即 redisCommand&#xff0c;其原型如下&#xff1a; void *redisCommand(redisContext *c, const char *format, ...);參數 c&#xff1a;已連接的 redisContext*format&#xff1a;與 printf 類似的格式字符串//…

【數據庫】國產數據庫的新機遇:電科金倉以融合技術同步全球競爭

7月15日&#xff0c;國產數據庫廠商中電科金倉&#xff08;北京&#xff09;科技股份有限公司&#xff08;以下簡稱“電科金倉”&#xff09;在北京舉行了一場技術發布會&#xff0c;集中發布四款核心產品&#xff1a;AI時代的融合數據庫KES V9 2025、企業級統一管控平臺KEMCC、…

大模型 Function Call 的實現步驟及示例詳解

大模型 Function Call 的實現步驟及示例詳解一、Function Call的核心流程拆解二、結合代碼詳解Function Call實現步驟1&#xff1a;定義工具&#xff08;對應代碼中的tools列表&#xff09;步驟2&#xff1a;實現工具函數&#xff08;對應代碼中的get_current_weather和get_cur…

Linux運維新手的修煉手扎之第24天

mysql服務1 mysql命令客戶端(mysql.cnf)命令 \c--ctrl c \s--顯示當前狀態 \r--客戶端重新連接 \h--查看幫助信息 exit退出客戶端 \G--垂直格式顯示查詢結果連接MySQL服務器--[rootrocky9 ~]# mysql(mysql -u用戶名 - p密碼 -h服務端ip -P服務端port -S服務端sock -e "my…

面向對象分析與設計40講(7)設計原則之合成復用原則

文章目錄 一、概念 二、示例(C++ 實現) 1. 違反合成復用原則的示例(過度使用繼承) 2. 遵循合成復用原則的示例(使用組合) 三、總結 1. 繼承是“強綁定”,組合是“弱關聯” 2. 繼承固化“靜態結構”,組合支持“動態變化” 3. 繼承放大“設計缺陷”,組合隔離“局部問題”…

Git 完全手冊:從入門到團隊協作實戰(4)

Hello大家好&#xff01;很高興我們又見面啦&#xff01;給生活添點passion&#xff0c;開始今天的編程之路&#xff01; 我的博客&#xff1a;<但凡. 我的專欄&#xff1a;《編程之路》、《數據結構與算法之美》、《C修煉之路》、《Linux修煉&#xff1a;終端之內 洞悉真理…

解決Spring事務中RPC調用無法回滾的問題

文章目錄問題分析解決方案實現原理解析執行流程說明運行實例正常流程執行執行異常流程關鍵優勢在分布式系統開發中&#xff0c;我們經常會遇到本地事務與遠程服務調用結合的場景。當本地事務包含RPC調用時&#xff0c;如果事務回滾&#xff0c;RPC調用已經執行就會導致數據不一…

sqli-labs通關筆記-第13關 POST報錯型注入(單引號括號閉合 手工注入+腳本注入兩種方法)

目錄 一、字符型注入 二、limit函數 三、GET方法與POST方法 四、源碼分析 1、代碼審計 2、SQL注入安全分析 3、報錯型注入與聯合注入 五、滲透實戰 1、進入靶場 2、注入點分析 &#xff08;1&#xff09;SQL語句 &#xff08;2&#xff09;admin) #注入探測 &…

康復器材動靜態性能測試臺:精準檢測,為康復器械安全保駕護航

在康復醫療領域&#xff0c;無論是輪椅、拐杖、假肢還是康復床&#xff0c;每一件器械的強度與穩定性都直接關系到使用者的安全與康復效果。如何確保這些器械在實際使用中經得起反復考驗&#xff1f;Delta德爾塔儀器推出的康復器材動靜態性能測試臺&#xff0c;憑借其高精度、智…

vue3中el-table表頭篩選

效果如下&#xff0c;可以勾選表頭進行隱藏&#xff0c;也可以對表頭進行拖動排序index主界面 <script> let tempHead []; const showFilter ref<boolean>(false); let tableHeadList ref<TableHeadItem[]>([{ prop: "displayId", label: "…

數據結構 之 【排序】(直接選擇排序、堆排序、冒泡排序)

目錄 1.直接選擇排序 1.1直接選擇排序的思想 1.2直接選擇排序的代碼邏輯 1.3完整排序代碼 1.3.1一次只選一個最值 1.3.2一次篩選出兩個最值 1.4直接選擇排序的時間復雜度與空間復雜度 2.堆排序 2.1堆排序的思想 2.2堆排序的具體步驟 2.3堆排序圖解 2.4完整排序代碼…

用手機當外掛-圖文并茂做報告紀要

前陣參加一個峰會,看到演講嘉賓每翻一頁PPT,下面的觀察就舉起手機一頓拍。實話說這種拍下來的,難說還會拿出來看,而且再看的時候也未必能對應到當時主講人的一些解釋 。 如果現場將圖片保存到筆記本電腦,并快速記錄關鍵信息,這樣聽完一個報告可能就直接輸出一篇報道了。 有…

Vue的ubus emit/on使用

這段代碼是 Vue.js 組件中的 mounted 生命周期鉤子函數&#xff0c;主要作用是監聽一個名為 “macSelectData” 的全局事件。具體行為如下&#xff1a;分步解釋&#xff1a;mounted() 生命周期鉤子 當組件被掛載到 DOM 后&#xff0c;Vue 會自動調用 mounted() 方法。這里常用于…

rsync報錯解決

問題說明 [rootlocalhost shyn]# rsync -avz --checksum "root192.168.159.133:/tmp/shyn" "/tmp /shyn"WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! …

ArKTS: DAL,Model,BLL,Interface,Factory using SQLite

HarmonyOS 用ohos.data.rdb 用DBHelper.ets 共用調用SQLite 庫&#xff0c;進行DAL,Model,BLL,Interface,Factory 框架模式&#xff0c;表為CREATE TABLE IF NOT EXISTS signInRecord ( id INTEGER PRIMARY KEY AUTOINCREMENT, employeeId TEXT NOT NULL, employeeName TEXT NO…

MySQL JSON 數據類型用法及與傳統JSON字符串的對比 JSON數據類型簡介

文章目錄前言1. 基本用法JSON數據類型 vs 傳統JSON字符串1. 存儲方式2. 查詢方式對比3. 索引支持JSON存儲對象和數組的性能考慮1. 存儲對象2. 存儲數組性能對比總結最佳實踐建議前言 MySQL從 5.7 版本開始引入了 JSON 數據類型&#xff0c;專門用于存儲 JSON 格式的數據。與傳…

C++:list(1)list的使用

list的使用一.list基本的結構1.環狀雙向鏈表2.哨兵節點3.迭代器4.節點結構5.鏈表遍歷6.迭代器失效二.list的基本使用1.test01函數&#xff1a;主要測試std::list的初始化方式及遍歷2.test02函數&#xff1a;主要測試std::list的常用成員函數操作3.測試結果如下三.list的其他操作…