FFmpeg常用API與示例(三)—— 音視頻解碼與編碼

編解碼層

1.解碼

在這里插入圖片描述

(1) 注冊所有容器格式和 CODEC:av_register_all()

(2) 打開文件:av_open_input_file()

(3) 從文件中提取流信息:av_find_stream_info()

(4) 窮舉所有的流,查找其中種類為 CODEC_TYPE_VIDEO

(5) 查找對應的解碼器:avcodec_find_decoder()

(6) 打開編解碼器:avcodec_open()

(7) 為解碼幀分配內存:avcodec_alloc_frame()

(8) 不停地從碼流中提取出幀數據:av_read_frame()

(9) 判斷幀的類型,對于視頻幀調用:avcodec_decode_video()

(10) 解碼完后,釋放解碼器:avcodec_close()

(11) 關閉輸入文件:av_close_input_file()

/*** @file* Demuxing and decoding example.** Show how to use the libavformat and libavcodec API to demux and* decode audio and video data.* @example demuxing_decoding.c*/#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>static AVFormatContext *fmt_ctx = NULL;
static AVCodecContext *video_dec_ctx = NULL, *audio_dec_ctx;
static int width, height;
static enum AVPixelFormat pix_fmt;
static AVStream *video_stream = NULL, *audio_stream = NULL;
static const char *src_filename = NULL;
static const char *video_dst_filename = NULL;
static const char *audio_dst_filename = NULL;
static FILE *video_dst_file = NULL;
static FILE *audio_dst_file = NULL;static uint8_t *video_dst_data[4] = {NULL};
static int      video_dst_linesize[4];
static int video_dst_bufsize;static int video_stream_idx = -1, audio_stream_idx = -1;
static AVFrame *frame = NULL;
static AVPacket pkt;
static int video_frame_count = 0;
static int audio_frame_count = 0;static int output_video_frame(AVFrame *frame)
{if (frame->width != width || frame->height != height ||frame->format != pix_fmt) {/* To handle this change, one could call av_image_alloc again and* decode the following frames into another rawvideo file. */fprintf(stderr, "Error: Width, height and pixel format have to be ""constant in a rawvideo file, but the width, height or ""pixel format of the input video changed:\n""old: width = %d, height = %d, format = %s\n""new: width = %d, height = %d, format = %s\n",width, height, av_get_pix_fmt_name(pix_fmt),frame->width, frame->height,av_get_pix_fmt_name(frame->format));return -1;}printf("video_frame n:%d coded_n:%d\n",video_frame_count++, frame->coded_picture_number);/* copy decoded frame to destination buffer:* this is required since rawvideo expects non aligned data */av_image_copy(video_dst_data, video_dst_linesize,(const uint8_t **)(frame->data), frame->linesize,pix_fmt, width, height);/* write to rawvideo file */fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file);return 0;
}static int output_audio_frame(AVFrame *frame)
{size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(frame->format);printf("audio_frame n:%d nb_samples:%d pts:%s\n",audio_frame_count++, frame->nb_samples,av_ts2timestr(frame->pts, &audio_dec_ctx->time_base));/* Write the raw audio data samples of the first plane. This works* fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However,* most audio decoders output planar audio, which uses a separate* plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P).* In other words, this code will write only the first audio channel* in these cases.* You should use libswresample or libavfilter to convert the frame* to packed data. */fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file);return 0;
}static int decode_packet(AVCodecContext *dec, const AVPacket *pkt)
{int ret = 0;// submit the packet to the decoderret = avcodec_send_packet(dec, pkt);if (ret < 0) {fprintf(stderr, "Error submitting a packet for decoding (%s)\n", av_err2str(ret));return ret;}// get all the available frames from the decoderwhile (ret >= 0) {ret = avcodec_receive_frame(dec, frame);if (ret < 0) {// those two return values are special and mean there is no output// frame available, but there were no errors during decodingif (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))return 0;fprintf(stderr, "Error during decoding (%s)\n", av_err2str(ret));return ret;}// write the frame data to output fileif (dec->codec->type == AVMEDIA_TYPE_VIDEO)ret = output_video_frame(frame);elseret = output_audio_frame(frame);av_frame_unref(frame);if (ret < 0)return ret;}return 0;
}static int open_codec_context(int *stream_idx,AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)
{int ret, stream_index;AVStream *st;AVCodec *dec = NULL;AVDictionary *opts = NULL;ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);if (ret < 0) {fprintf(stderr, "Could not find %s stream in input file '%s'\n",av_get_media_type_string(type), src_filename);return ret;} else {stream_index = ret;st = fmt_ctx->streams[stream_index];/* find decoder for the stream */dec = avcodec_find_decoder(st->codecpar->codec_id);if (!dec) {fprintf(stderr, "Failed to find %s codec\n",av_get_media_type_string(type));return AVERROR(EINVAL);}/* Allocate a codec context for the decoder */*dec_ctx = avcodec_alloc_context3(dec);if (!*dec_ctx) {fprintf(stderr, "Failed to allocate the %s codec context\n",av_get_media_type_string(type));return AVERROR(ENOMEM);}/* Copy codec parameters from input stream to output codec context */if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",av_get_media_type_string(type));return ret;}/* Init the decoders */if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {fprintf(stderr, "Failed to open %s codec\n",av_get_media_type_string(type));return ret;}*stream_idx = stream_index;}return 0;
}static int get_format_from_sample_fmt(const char **fmt,enum AVSampleFormat sample_fmt)
{int i;struct sample_fmt_entry {enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le;} sample_fmt_entries[] = {{ AV_SAMPLE_FMT_U8,  "u8",    "u8"    },{ AV_SAMPLE_FMT_S16, "s16be", "s16le" },{ AV_SAMPLE_FMT_S32, "s32be", "s32le" },{ AV_SAMPLE_FMT_FLT, "f32be", "f32le" },{ AV_SAMPLE_FMT_DBL, "f64be", "f64le" },};*fmt = NULL;for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {struct sample_fmt_entry *entry = &sample_fmt_entries[i];if (sample_fmt == entry->sample_fmt) {*fmt = AV_NE(entry->fmt_be, entry->fmt_le);return 0;}}fprintf(stderr,"sample format %s is not supported as output format\n",av_get_sample_fmt_name(sample_fmt));return -1;
}int main (int argc, char **argv)
{int ret = 0;if (argc != 4) {fprintf(stderr, "usage: %s  input_file video_output_file audio_output_file\n""API example program to show how to read frames from an input file.\n""This program reads frames from a file, decodes them, and writes decoded\n""video frames to a rawvideo file named video_output_file, and decoded\n""audio frames to a rawaudio file named audio_output_file.\n",argv[0]);exit(1);}src_filename = argv[1];video_dst_filename = argv[2];audio_dst_filename = argv[3];/* open input file, and allocate format context */if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {fprintf(stderr, "Could not open source file %s\n", src_filename);exit(1);}/* retrieve stream information */if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {fprintf(stderr, "Could not find stream information\n");exit(1);}if (open_codec_context(&video_stream_idx, &video_dec_ctx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {video_stream = fmt_ctx->streams[video_stream_idx];video_dst_file = fopen(video_dst_filename, "wb");if (!video_dst_file) {fprintf(stderr, "Could not open destination file %s\n", video_dst_filename);ret = 1;goto end;}/* allocate image where the decoded image will be put */width = video_dec_ctx->width;height = video_dec_ctx->height;pix_fmt = video_dec_ctx->pix_fmt;ret = av_image_alloc(video_dst_data, video_dst_linesize,width, height, pix_fmt, 1);if (ret < 0) {fprintf(stderr, "Could not allocate raw video buffer\n");goto end;}video_dst_bufsize = ret;}if (open_codec_context(&audio_stream_idx, &audio_dec_ctx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0) {audio_stream = fmt_ctx->streams[audio_stream_idx];audio_dst_file = fopen(audio_dst_filename, "wb");if (!audio_dst_file) {fprintf(stderr, "Could not open destination file %s\n", audio_dst_filename);ret = 1;goto end;}}/* dump input information to stderr */av_dump_format(fmt_ctx, 0, src_filename, 0);if (!audio_stream && !video_stream) {fprintf(stderr, "Could not find audio or video stream in the input, aborting\n");ret = 1;goto end;}frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate frame\n");ret = AVERROR(ENOMEM);goto end;}/* initialize packet, set data to NULL, let the demuxer fill it */av_init_packet(&pkt);pkt.data = NULL;pkt.size = 0;if (video_stream)printf("Demuxing video from file '%s' into '%s'\n", src_filename, video_dst_filename);if (audio_stream)printf("Demuxing audio from file '%s' into '%s'\n", src_filename, audio_dst_filename);/* read frames from the file */while (av_read_frame(fmt_ctx, &pkt) >= 0) {// check if the packet belongs to a stream we are interested in, otherwise// skip itif (pkt.stream_index == video_stream_idx)ret = decode_packet(video_dec_ctx, &pkt);else if (pkt.stream_index == audio_stream_idx)ret = decode_packet(audio_dec_ctx, &pkt);av_packet_unref(&pkt);if (ret < 0)break;}/* flush the decoders */if (video_dec_ctx)decode_packet(video_dec_ctx, NULL);if (audio_dec_ctx)decode_packet(audio_dec_ctx, NULL);printf("Demuxing succeeded.\n");if (video_stream) {printf("Play the output video file with the command:\n""ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",av_get_pix_fmt_name(pix_fmt), width, height,video_dst_filename);}if (audio_stream) {enum AVSampleFormat sfmt = audio_dec_ctx->sample_fmt;int n_channels = audio_dec_ctx->channels;const char *fmt;if (av_sample_fmt_is_planar(sfmt)) {const char *packed = av_get_sample_fmt_name(sfmt);printf("Warning: the sample format the decoder produced is planar ""(%s). This example will output the first channel only.\n",packed ? packed : "?");sfmt = av_get_packed_sample_fmt(sfmt);n_channels = 1;}if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0)goto end;printf("Play the output audio file with the command:\n""ffplay -f %s -ac %d -ar %d %s\n",fmt, n_channels, audio_dec_ctx->sample_rate,audio_dst_filename);}end:avcodec_free_context(&video_dec_ctx);avcodec_free_context(&audio_dec_ctx);avformat_close_input(&fmt_ctx);if (video_dst_file)fclose(video_dst_file);if (audio_dst_file)fclose(audio_dst_file);av_frame_free(&frame);av_free(video_dst_data[0]);return ret < 0;
}
2.編碼

在這里插入圖片描述

大流程可以劃分為輸入、輸出、轉碼、播放四大塊。

其中轉碼涉及比較多的處理環節,從圖中可以看出,轉碼功能在整個功能圖中占比很大。轉碼的核心功能在解碼和編碼兩個部分,但在一個可用的示例程序中,編碼解碼與輸入輸出是難以分割的。

解復用器為解碼器提供輸入,解碼器會輸出原始幀,對原始幀可進行各種復雜的濾鏡處理,濾鏡處理后的幀經編碼器生成編碼幀,多路流的編碼幀經復用器輸出到輸出文件。

/*** @file* video encoding with libavcodec API example** @example encode_video.c*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libavcodec/avcodec.h>#include <libavutil/opt.h>
#include <libavutil/imgutils.h>static void 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);ret = avcodec_send_frame(enc_ctx, frame);if (ret < 0) {fprintf(stderr, "Error sending a frame for encoding\n");exit(1);}while (ret >= 0) {ret = avcodec_receive_packet(enc_ctx, pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return;else if (ret < 0) {fprintf(stderr, "Error during encoding\n");exit(1);}printf("Write packet %3"PRId64" (size=%5d)\n", pkt->pts, pkt->size);fwrite(pkt->data, 1, pkt->size, outfile);av_packet_unref(pkt);}
}int mainsse22(int argc, char **argv)
{const char *filename, *codec_name;const AVCodec *codec;AVCodecContext *c= NULL;int i, ret, x, y;FILE *f;AVFrame *frame;AVPacket *pkt;uint8_t endcode[] = { 0, 0, 1, 0xb7 };if (argc <= 2) {fprintf(stderr, "Usage: %s <output file> <codec name>\n", argv[0]);exit(0);}filename = argv[1];codec_name = argv[2];/* find the mpeg1video encoder */codec = avcodec_find_encoder_by_name(codec_name);if (!codec) {fprintf(stderr, "Codec '%s' not found\n", codec_name);exit(1);}c = avcodec_alloc_context3(codec);if (!c) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}pkt = av_packet_alloc();if (!pkt)exit(1);/* put sample parameters */c->bit_rate = 400000;/* resolution must be a multiple of two */c->width = 352;c->height = 288;/* frames per second */c->time_base = (AVRational){1, 25};c->framerate = (AVRational){25, 1};/* emit one intra frame every ten frames* check frame pict_type before passing frame* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I* then gop_size is ignored and the output of encoder* will always be I frame irrespective to gop_size*/c->gop_size = 10;c->max_b_frames = 1;c->pix_fmt = AV_PIX_FMT_YUV420P;if (codec->id == AV_CODEC_ID_H264)av_opt_set(c->priv_data, "preset", "slow", 0);/* open it */ret = avcodec_open2(c, codec, NULL);if (ret < 0) {fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));exit(1);}f = fopen(filename, "wb");if (!f) {fprintf(stderr, "Could not open %s\n", filename);exit(1);}frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}frame->format = c->pix_fmt;frame->width  = c->width;frame->height = c->height;ret = av_frame_get_buffer(frame, 0);if (ret < 0) {fprintf(stderr, "Could not allocate the video frame data\n");exit(1);}/* encode 1 second of video */for (i = 0; i < 25; i++) {fflush(stdout);/* make sure the frame data is writable */ret = av_frame_make_writable(frame);if (ret < 0)exit(1);/* prepare a dummy image *//* Y */for (y = 0; y < c->height; y++) {for (x = 0; x < c->width; x++) {frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;}}/* Cb and Cr */for (y = 0; y < c->height/2; y++) {for (x = 0; x < c->width/2; x++) {frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;}}frame->pts = i;/* encode the image */encode(c, frame, pkt, f);}/* flush the encoder */encode(c, NULL, pkt, f);/* add sequence end code to have a real MPEG file */if (codec->id == AV_CODEC_ID_MPEG1VIDEO || codec->id == AV_CODEC_ID_MPEG2VIDEO)fwrite(endcode, 1, sizeof(endcode), f);fclose(f);avcodec_free_context(&c);av_frame_free(&frame);av_packet_free(&pkt);return 0;
}

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

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

相關文章

C++ 實現以xml的格式寫入文件

C XML類 該類主要將xml中的標簽分為兩類&#xff0c;無內容標簽統一稱為父標簽&#xff0c;有內容的就以鍵值對的方式直接輸出。 后面可能會優化通過函數參數的方式管控層級關系&#xff0c;現在是通過類里自動記錄層級深度來表示的。 #include <fstream> #include <…

【QT教程】QT6硬件圖形界面編程 QT硬件編程

QT6硬件圖形界面編程 使用AI技術輔助生成 QT界面美化視頻課程 QT性能優化視頻課程 QT原理與源碼分析視頻課程 QT QML C擴展開發視頻課程 免費QT視頻課程 您可以看免費1000個QT技術視頻 免費QT視頻課程 QT統計圖和QT數據可視化視頻免費看 免費QT視頻課程 QT性能優化視頻免費看…

數據結構-二叉樹結尾+排序

一、二叉樹結尾 1、如何判斷一棵樹是完全二叉樹。 我們可以使用層序遍歷的思路&#xff0c;利用一個隊列&#xff0c;去完成層序遍歷&#xff0c;但是這里會有些許的不同&#xff0c;我們需要讓空也進隊列。如果隊列里到最后只剩下空那么這棵樹就是完全二叉樹。具體的實現如下…

js 數據格式轉換,對象轉數組,數組轉對象

1.對象轉數組 // 對象obj轉換成數組格式 let obj { orgCode:分局編碼, alertId:告警ID, name:告警名稱 } let arr [] for(let key in obj) { console.log(11,key,obj[key]); // 定義一個對象&#xff0c;賦值 let o { id: key, // key是obj對象的鍵值 label: obj[key] …

學習Vue 3.0中的onMounted和onUnmounted鉤子函數

學習Vue 3.0中的onMounted和onUnmounted鉤子函數 一、什么是onMounted和onUnmounted&#xff1f;二、如何使用onMounted和onUnmounted&#xff1f;1、使用onMounted2、使用onUnmounted 三、總結 一、什么是onMounted和onUnmounted&#xff1f; Vue 3.0帶來了許多令人興奮的新特…

Modal h函數寫法

Modal h函數寫法 if (res.data.flag) {const ocapWarn res.data.ocaplList;Modal.warning({title: "提示",content: h("div", {}, [ocapWarn.map((item, index) > {return h("div", {}, [h("p",${index 1}、${item.defectItem}(…

IntersectionObserver對象

IntersectionObserver對象 IntersectionObserver對象&#xff0c;從屬于Intersection Observer API&#xff0c;提供了一種異步觀察目標元素與其祖先元素或頂級文檔視窗viewport交叉狀態的方法&#xff0c;祖先元素與視窗viewport被稱為根root&#xff0c;也就是說Intersectio…

c#---多態

在 C#語言中體現多態有三種方式&#xff1a;虛方法&#xff0c;抽象類&#xff0c; 接口 一、虛方法 什么是虛方法&#xff1f; 在父類中使用 virtual 關鍵字修飾的方法&#xff0c; 就是虛方法。在子類中可以使用 override 關鍵字對該虛方法進行重寫。 class Animal {public…

android apk沒有源碼如何修改程序

如果您擁有一個APK文件但沒有源代碼&#xff0c;您可以嘗試以下幾種方法來進行修改&#xff1a; 反編譯APK&#xff1a;使用工具如apktool對APK文件進行反編譯&#xff0c;這將為您提供源代碼和資源文件。 動態調試&#xff1a;使用調試工具連接設備或模擬器&#xff0c;并動態…

重裝前端整體流程

用戶管理 --匯總 -- 明細-CSDN博客 一、node 這個看環境變量 2023最新版Node.js下載安裝及環境配置教程&#xff08;非常詳細&#xff09;從零基礎入門到精通&#xff0c;看完這一篇就夠了_nodejs安裝及環境配置-CSDN博客 配置到國內鏡像的時候&#xff0c;去看&#xff0c;淘…

理解固化的Maven依賴:spring-boot-starter-parent 與 spring-boot-dependencies

目錄 理解固化的Maven依賴&#xff1a;spring-boot-starter-parent 與 spring-boot-dependencies1. spring-boot-starter-parent1.1 簡介1.2 特點 2. spring-boot-dependencies2.1 簡介2.2 特點 3. 異同點對比3.1 相同點3.2 不同點案例一&#xff1a;使用 spring-boot-starter-…

Java方法的重載

方法重載 1. 為什么需要方法重載 public class TestMethod{public static void main (String[] args){int a 10;int b 20;int ret add(a,b);System.out.println("ret "ret);double a2 10.5;double b2 20.5;double ret2 add(a2,b2);System.out.println("…

《QT實用小工具·六十二》基于QT實現貝塞爾曲線畫炫酷的波浪動畫

1、概述 源碼放在文章末尾 該項目實現了通過貝塞爾曲線畫波浪動畫&#xff0c;可控制 顏色密度速度加速度 安裝與運行環境 語言&#xff1a;C 框架&#xff1a;Qt 11.3 平臺&#xff1a;Windows 將屏幕水平平均分為10塊&#xff0c;在一定范圍內隨機高度的12個點&#xff08;…

飛天使-k8s知識點29-kubernetes安裝1.28.0版本

文章目錄 選用版本初始化服務器,自己修改里面的ipreboot haproxy安裝 &#xff0c;可以參考我之前寫的內核參數調整&#xff0c;安裝docker 安裝cri-dockerd開始安裝集群工具下載鏡像以及啟用完畢之后 此時的coredns 不通結果展示 選用版本 k8s 1.24版本之前還可以使用docker&…

【初階數據結構】順序表OJ題講解

前言 &#x1f4da;作者簡介&#xff1a;愛編程的小馬&#xff0c;正在學習C/C&#xff0c;Linux及MySQL。 &#x1f4da;本文收錄與初階數據結構系列&#xff0c;本專欄主要是針對時間、空間復雜度&#xff0c;順序表和鏈表、棧和隊列、二叉樹以及各類排序算法&#xff0c;持…

基于ambari hdp的kafka用戶授權讀寫權限

基于ambari hdp的kafka用戶授權讀寫權限 版本Kafka 2.0.0添加自定義配置修改admin密碼重啟kafka授權讀取授權寫入有效通配符部分舉例 版本Kafka 2.0.0 添加自定義配置 authorizer.class.name kafka.security.auth.SimpleAclAuthorizer super.users User:admin allow.everyo…

【LLM 論文】Step-Back Prompting:先解決更高層次的問題來提高 LLM 推理能力

論文&#xff1a;Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models ???? Google DeepMind, ICLR 2024, arXiv:2310.06117 論文速讀 該論文受到的啟發是&#xff1a;人類再解決一個包含很多細節的具體問題時&#xff0c;先站在更高的層次上解…

Android 屏幕適配全攻略(上)-掌握屏幕單位,應對千變萬化的設備

本文從 Android 開發中常見的長度單位 px、dp、sp 入手&#xff0c;詳細介紹了它們的特點及轉換關系。 接著深入探討了屏幕尺寸、分辨率、像素密度等重要的屏幕指標&#xff0c;幫助讀者全面理解它們之間的聯系。最后&#xff0c;通過實例代碼演示了如何在代碼中進行單位轉換&…

三分鐘上手安全滲透系統Kali Linux

kali linux系統集成了常用的安全滲透工具&#xff0c;省去了安裝工具的時間&#xff0c;做安全相關的工作是非常推薦使用的。 安裝Kalii Linux 安裝系統 一般使用虛擬機進行安裝&#xff0c;Kali Linux基于Debian內核&#xff0c;虛擬機的操作系統選擇Debian 7.x 64 選擇系統…

【SRC實戰】一鍵完成全部任務獲取獎勵

挖個洞先 https://mp.weixin.qq.com/s/LkPfJuuP1K8vaFXRn-8wVg “ 以下漏洞均為實驗靶場&#xff0c;如有雷同&#xff0c;純屬巧合 ” 01 — 漏洞證明 一、業務邏輯 “ 如何欺騙APP完成任務獲取獎勵&#xff1f; ” 1、記錄金幣數量20 2、瀏覽商品詳情頁 3、點擊瀏覽提…