『壹』 ffmpeg-python中文文檔(三)——API參考
表示上游節點的傳出邊緣;可以用來創建更多的下游節點。
輸入文件 URL (ffmpeg -i option)
在一個 ffmpeg 命令行中包含所有給定的輸出
輸出文件地址
不詢問就覆蓋輸出文件(ffmpeg -y 選項)
在指定文件上運行 ffprobe 並返回輸出的 JSON 表示。
構建用於調用 ffmpeg 的命令行。
構建要傳遞給 ffmpeg 的命令行參數。
為提供的節點圖調用 ffmpeg 。
參數
為提供的節點圖非同步調用 ffmpeg。
參數
例子
運行和流式輸入:
運行並捕獲輸出:
使用 numpy 逐幀處理視頻:
通過重新混合顏色通道來調整視頻輸入幀。
連接音頻和視頻流,將它們一個接一個地連接在一起。
篩選器適用於同步視頻和音頻流的片段。所有段必須具有每種類型的相同數量的流,這也是輸出時的流數。
參數
裁剪輸入視頻。
參數
在輸入圖像上繪制一個彩色框。
參數
使用 libfreetype 庫從視頻頂部的指定文件中繪制文本字元串或文本。
要啟用此過濾器的編譯,您需要使用 --enable-libfreetype . 要啟用默認字體回退和字體選項,您需要使用 --enable-libfontconfig . 要啟用 text_shaping 選項,您需要使用 --enable-libfribidi
參數
· box - 用於使用背景顏色在文本周圍繪制一個框。該值必須是 1(啟用)或 0(禁用)。框的默認值為 0。
· boxborderw – 使用 boxcolor 設置要在框周圍繪制的邊框寬度。boxborderw 的默認值為 0。
· boxcolor - 用於在文本周圍繪制框的顏色。有關此選項的語法,請查看 ffmpeg-utils 手冊中的「顏色」部分。 boxcolor 的默認值為「white」。
· line_spacing – 使用 box 設置要在框周圍繪制的邊框的行間距(以像素為單位)。line_spacing 的默認值為 0。
· borderw – 使用邊框顏色設置要在文本周圍繪制的邊框寬度。邊框的默認值為 0。
· bordercolor – 設置用於在文本周圍繪制邊框的顏色。有關此選項的語法,請查看 ffmpeg-utils 手冊中的「顏色」部分。邊框顏色的默認值為「黑色」。
· 擴展- 選擇文本的擴展方式。可以是 none、strftime(已棄用)或 normal(默認)。有關詳細信息,請參閱下面的文本擴展部分。
· basetime – 設置計數的開始時間。值以微秒為單位。僅適用於已棄用的 strftime 擴展模式。要在正常擴展模式下進行模擬,請使用 pts 函數,提供開始時間(以秒為單位)作為第二個參數。
· fix_bounds - 如果為 true,檢查並修復文本坐標以避免剪切。
· fontcolor - 用於繪制字體的顏色。有關此選項的語法,請查看 ffmpeg-utils 手冊中的「顏色」部分。fontcolor 的默認值為「黑色」。
· fontcolor_expr – 與文本相同的擴展字元串以獲得動態字體顏色值。默認情況下,此選項具有空值並且不被處理。設置此選項時,它會覆蓋 fontcolor 選項。
· font - 用於繪制文本的字體系列。默認情況下無。
· fontfile – 用於繪制文本的字體文件。必須包含路徑。如果禁用了 fontconfig 支持,則此參數是必需的。
· alpha – 繪制應用 alpha 混合的文本。該值可以是介於 0.0 和 1.0 之間的數字。該表達式也接受相同的變數 x、y。默認值為 1。請參閱 fontcolor_expr。
· fontsize – 用於繪制文本的字體大小。字體大小的默認值為 16。
· text_shaping – 如果設置為 1,則在繪制文本之前嘗試對文本進行整形(例如,反轉從右到左文本的順序並加入阿拉伯字元)。否則,只需按照給定的方式繪制文本。默認為 1(如果支持)。
· ft_load_flags –用於載入字體的標志。這些標志映射了 libfreetype 支持的相應標志,並且是以下值的組合:
默認值為「默認」。有關更多信息,請參閱 FT_LOAD_* libfreetype 標志的文檔。
· shadowcolor – 用於在已繪制文本後面繪制陰影的顏色。有關此選項的語法,請查看 ffmpeg-utils 手冊中的「顏色」部分。shadowcolor 的默認值為「黑色」。
· shadowx – 文本陰影位置相對於文本位置的 x 偏移量。它可以是正值或負值。默認值為「0」。
· shadowy – 文本陰影位置相對於文本位置的 y 偏移量。它可以是正值或負值。默認值為「0」。
· start_number – n/frame_num 變數的起始幀號。默認值為「0」。
· tabsize - 用於呈現選項卡的空格數大小。默認值為 4。
· timecode – 以「hh:mm:ss[:;.]ff」格式設置初始時間碼表示。它可以帶或不帶文本參數使用。必須指定 timecode_rate 選項。
· rate – 設置時間碼幀率(僅限時間碼)。
· timecode_rate – 的別名rate。
· r – 的別名rate。
· tc24hmax – 如果設置為 1,時間碼選項的輸出將在 24 小時左右回繞。默認值為 0(禁用)。
· text -- 要繪制的文本字元串。文本必須是 UTF-8 編碼字元序列。如果沒有使用參數 textfile 指定文件,則此參數是必需的。
· textfile – 包含要繪制的文本的文本文件。文本必須是 UTF-8 編碼字元序列。如果沒有使用參數 text 指定文本字元串,則此參數是必需的。如果同時指定了 text 和 textfile,則會引發錯誤。
· reload – 如果設置為 1,文本文件將在每一幀之前重新載入。一定要自動更新它,否則它可能會被部分讀取,甚至失敗。
· x – 指定將在視頻幀內繪制文本的偏移量的表達式。它相對於輸出圖像的左邊框。默認值為「0」。
· y - 指定將在視頻幀內繪制文本的偏移量的表達式。它相對於輸出圖像的上邊框。默認值為「0」。有關接受的常量和函數的列表,請參見下文。
表達式常量:
x 和 y 的參數是包含以下常量和函數的表達式:
· dar:輸入顯示縱橫比,同 (w / h) * sar
· hsub:水平色度子樣本值。例如,對於像素格式「yuv422p」,hsub 為 2,vsub 為 1。
· vsub:垂直色度子樣本值。例如,對於像素格式「yuv422p」,hsub 為 2,vsub 為 1。
· line_h:每個文本行的高度
· lh:別名為line_h.
· main_h:輸入高度
· h: 的別名main_h。
· H: 的別名main_h。
· main_w:輸入寬度
· w: 的別名main_w。
· W: 的別名main_w。
· ascent:對於所有渲染的字形,從基線到用於放置字形輪廓點的最高/上網格坐標的最大距離。這是一個正值,因為網格的 Y 軸向上。
· max_glyph_a: 的別名ascent。
· 下降:對於所有渲染的字形,從基線到用於放置字形輪廓點的最低網格坐標的最大距離。由於網格的方向,這是一個負值,Y 軸向上。
· max_glyph_d: 的別名descent。
· max_glyph_h:最大字形高度,即渲染文本中包含的所有字形的最大高度,相當於上升-下降。
· max_glyph_w:最大字形寬度,即渲染文本中包含的所有字形的最大寬度。
· n:輸入幀數,從0開始
· rand(min, max):返回一個包含在 min 和 max 之間的隨機數
· sar:輸入樣本縱橫比。
· t:時間戳,以秒為單位,如果輸入時間戳未知,則為 NAN
· text_h:渲染文本的高度
· th: 的別名text_h。
· text_w:渲染文本的寬度
· tw: 的別名text_w。
· x:繪制文本的 x 偏移坐標。
· y:繪制文本的 y 偏移坐標。
這些參數允許 x 和 y 表達式相互引用,因此您可以例如指定 y=x/dar.
應用自定義過濾器。
filter通常由更高級別的過濾器函數使用,例如 hflip ,但如果缺少過濾器實現 ffmpeg-python ,您可以 filter 直接調用以 ffmpeg-python 將過濾器名稱和參數逐字傳遞給 ffmpeg 。
參數
函數名稱後綴_是為了避免與標准 pythonfilter 函數混淆。
例子
替代名稱 filter ,以免與內置的 pythonfilter 運算符沖突。
應用具有一個或多個輸出的自定義過濾器。
這 filter 與過濾器可以產生多個輸出相同。
要引用輸出流,請使用 .stream 運算符或括弧簡寫:
例子
水平翻轉輸入視頻。
修改輸入的色調和/或飽和度。
參數
將一個視頻疊加在另一個視頻之上。
參數
更改輸入幀的 PTS(表示時間戳)。
FFmpeg里有兩種時間戳:DTS(Decoding Time Stamp)和PTS(Presentation Time Stamp)。 顧名思義,前者是解碼的時間,後者是顯示的時間。
參數
修剪輸入,使輸出包含輸入的一個連續子部分。
參數
垂直翻轉輸入視頻。
應用縮放和平移效果。
參數
『貳』 python 怎麼獲取mp4的解析度
獲得H.264視頻解析度的方法
From: http //www cnblogs.com/likwo/p/3531241.html
在使用ffmpeg解碼播放TS流的時候(例如之前寫過的UDP組播流),在連接時往往需要耗費大量時間。經過debug發現是av_find_stream_info(已拋棄,現在使用的是avformat_find_stream_info)這個方法十分耗時,而且是阻塞的。av_find_stream_info方法主要是獲得相應的流信息,其中對我的應用最有用的就是視頻的解析度。在av_find_stream_info中是要不斷的讀取數據包,解碼獲得相應的信息,而其中除了解析度信息以外的東西對我的應用中是無用的。所以,考慮自己手動從H.264碼流中解析出視頻的解析度信息。
以下內容主要參考了這篇文章:http //www myexception.cn/internet/586390.html
H.264碼流的流信息都存儲在了特殊的結構中,叫做SPS(Sequence Parameter Set)。要解析SPS就需要知道一些H.264碼流的格式信息。
在H.264碼流中,都是以0x00 0x00 0x01 或者 0x00 0x00 0x00 0x01為開始碼的(在我的應用中為後者),之後通過檢測開始碼後第一個位元組的後五位是否為7(00111)來判斷其是否為SPS。得到SPS之後,就可以解析出視頻的解析度。SPS中有兩個成員,pic_width_in_mbs_minus1和pic_height_in_map_units_minus_1,分別表示圖像的寬和高,但是要注意的是它們都是以16為單位(在面積上就是以16*16的塊為單位)再減1,所以實際的寬是(pic_width_in_mbs_minus1 + 1)*16,高為(pic_height_in_map_units_minus_1+1)*16。
歡迎轉載,轉載請註明出處:http //guoyb.com/Tech/34.html
以下是解析寬高的代碼:
轉載http //guoyb.com/Tech/34.html
以下部分 轉自 http //blog.csdn.NET/pkueecser/article/details/7367641
使用RTP傳輸H264的時候,需要用到sdp協議描述,其中有兩項:Sequence Parameter Sets (SPS) 和Picture Parameter Set (PPS)需要用到,那麼這兩項從哪裡獲取呢?答案是從H264碼流中獲取.在H264碼流中,都是以"0x00 0x00 0x01"或者"0x00 0x00 0x00 0x01"為開始碼的,找到開始碼之後,使用開始碼之後的第一個位元組的低5位判斷是否為7(sps)或者8(pps), 及data[4] & 0x1f == 7 || data[4] & 0x1f == 8.然後對獲取的nal去掉開始碼之後進行base64編碼,得到的信息就可以用於sdp.sps和pps需要用逗號分隔開來.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
如何解析SDP中包含的H.264的SPS和PPS串
http //www pernet.tv.sixxs.org/thread-109-1-1.html
SDP中的H.264的SPS和PPS串,包含了初始化H.264解碼器所需要的信息參數,包括編碼所用的profile,level,圖像的寬和高,deblock濾波器等。
由於SDP中的SPS和PPS都是BASE64編碼形式的,不容易理解,附件有一個工具軟體可以對SDP中的SPS和PPS進行解析。
用法是在命令行中輸入:
spsparser sps.txt pps.txt output.txt
例如sps.txt中的內容為:
Z0LgFNoFglE=
pps.txt中的內容為:
aM4wpIA=
最終解析的到的結果為:
Start mping SPS:
profile_idc = 66
constrained_set0_flag = 1
constrained_set1_flag = 1
constrained_set2_flag = 1
constrained_set3_flag = 0
level_idc = 20
seq_parameter_set_id = 0
chroma_format_idc = 1
bit_depth_luma_minus8 = 0
bit_depth_chroma_minus8 = 0
seq_scaling_matrix_present_flag = 0
log2_max_frame_num_minus4 = 0
pic_order_cnt_type = 2
log2_max_pic_order_cnt_lsb_minus4 = 0
delta_pic_order_always_zero_flag = 0
offset_for_non_ref_pic = 0
offset_for_top_to_bottom_field = 0
num_ref_frames_in_pic_order_cnt_cycle = 0
num_ref_frames = 1
gaps_in_frame_num_value_allowed_flag = 0
pic_width_in_mbs_minus1 = 21
pic_height_in_mbs_minus1 = 17
frame_mbs_only_flag = 1
mb_adaptive_frame_field_flag = 0
direct_8x8_interence_flag = 0
frame_cropping_flag = 0
frame_cropping_rect_left_offset = 0
frame_cropping_rect_right_offset = 0
frame_cropping_rect_top_offset = 0
frame_cropping_rect_bottom_offset = 0
vui_parameters_present_flag = 0
Start mping PPS:
pic_parameter_set_id = 0
seq_parameter_set_id = 0
entropy_coding_mode_flag = 0
pic_order_present_flag = 0
num_slice_groups_minus1 = 0
slice_group_map_type = 0
num_ref_idx_l0_active_minus1 = 0
num_ref_idx_l1_active_minus1 = 0
weighted_pref_flag = 0
weighted_bipred_idc = 0
pic_init_qp_minus26 = 0
pic_init_qs_minus26 = 0
chroma_qp_index_offset = 10
deblocking_filter_control_present_flag = 1
constrained_intra_pred_flag = 0
rendant_pic_cnt_present_flag = 0
transform_8x8_mode_flag = 0
pic_scaling_matrix_present_flag = 0
second_chroma_qp_index_offset = 10
/////////////////////////////////////////////////////////////////////////////////////////////////
這里需要特別提一下這兩個參數
pic_width_in_mbs_minus1 = 21
pic_height_in_mbs_minus1 = 17
分別表示圖像的寬和高,以宏塊(16x16)為單位的值減1
因此,實際的寬為 (21+1)*16 = 352
spsparser.rar
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
http //krdai.info.sixxs.org/blog/mp4-sps-pps-data.html
最近在做跟 h264 encode/decode 相關的研究,目標是希望可以從 Android 的 MediaRecorder 當中取出 h264 的資訊。目前問題是在於 SPS 以及 PPS 到底要怎樣得到。由於 MediaRecorder 是寫入 mp4 檔案中,所以不得已只好來去分析一下 mp4 的檔案格式,發現沒有想像中的困難. 主要是參照 ISO/IEC 14496-15 這部份. 在 mp4 的檔案之中, 找到 avcC 這個字串, 之後就是接上 AVCDecoderConfigurationRecord. AVCDecoderConfigurationRecord 的 format 如下:
aligned(8) class AVCDecoderConfigurationRecord {
unsigned int(8) configurationVersion = 1;
unsigned int(8) AVCProfileIndication;
unsigned int(8) profile_compatibility;
unsigned int(8) AVCLevelIndication;
bit(6) reserved = '111111'b;
unsigned int(2) lengthSizeMinusOne;
bit(3) reserved = '111'b;
unsigned int(5) numOfSequenceParameterSets;
for (i=0; i< numOfSequenceParameterSets; i++) {
unsigned int(16) sequenceParameterSetLength ;
bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;
}
unsigned int(8) numOfPictureParameterSets;
for (i=0; i< numOfPictureParameterSets; i++) {
unsigned int(16) pictureParameterSetLength;
bit(8*pictureParameterSetLength) pictureParameterSetNALUnit;
}
}
對照一下這樣就可以找到 SPS 和 PPS
+++++++++++++++++++++++++++++++++++++++++++++
vlc沒有收到pps和sps
2010-10-08 16:16
問題 packetizer_h264 packetizer warning: waiting for SPS/PPS
是因為解碼器只是在第一次執行編碼的時候,才編碼出 SPS、PPS、和I_Frame;
h264 packetizer has set so, that it sends sps/pps only first keyframe,
I'm trying to figure what breaks if that is changed so sps/pps is written in every keyframe.
[出自| http //trac.videolan.org/vlc/ticket/1384]
解決辦法:
1、編碼器編碼出每個關鍵幀都加上SPS、PPS ,據說通常情況編碼器編出的 SPS、PPS是一樣的,所以這種方法耗費資源。
2、在伺服器接收到客戶端請求時,發送第一個package 加上 SPS、PPS。
具體如下:
1、在 VideoOpenFileSource 添加一個變數 isFirstFrame;
2、構造時初始化 isFirstFrame = true;
3、在int VideoOpenFileSource::readFromBufferChain() 修改如下:
1 if(isFirstFrame == true)
2 {
3 memcpy(fTo, h264_header, sizeof(h264_header)); /* h264_header = pps +sps*/
4 offset = sizeof(h264_header);
5 framesize = BufferChain_get(fInput.video_bufs, fTo + offset);
6 offset += framesize;
7 isFirstFrame = false;
8 printf("this is the first fime\n");
9 sleep(1);
10 }
11 else
12 {
13 framesize = BufferChain_get(fInput.video_bufs, fTo + offset);
14 offset += framesize;
15 }
1
[http //topic.csdn.net/u/20100801/17/ef35e664-92ff-4144-a35f-3984dcf11da3.html| 參考]
========================================================================
sdp 關於pps和sps的疑問:
packetization-mode 主要是定義包的模式,單一 NALU單元模式(0);非交錯(non-interleaved)封包模式(1);交錯(interleaved)封包模式(2)
sprop-parameter-sets 等於H.264 的序列參數集和圖像參數 NAL單元,base64轉換;(即= sps+pps)
profile-level-id 這個參數用於指示 H.264 流的 profile 類型和級別。這知道這個是啥東東
ffmpeg decode 關於pps sps問題:
stackoverflow.com/questions/3493742/problem-to-decode-h264-video-over-rtp-with-ffmpeg-libavcodec/3500432#3500432
如何用C語言取出H.264ES文件里的nal(sps,pps)信息。比如width, height, profile等等
請高手指點指點。。。 http //www oschina.net/question/225813_35707
解析sps,pps的代碼在ffmpeg裡面就有, 抄出來就行了, 我以前也自己寫過...
ffmpeg的libavcodec/h264_parser.c,
h264_ps.c
函數
ff_h264_decode_seq_parameter_set
ff_h264_decode_picture_parameter_set
自己可以看代碼.
H264參數語法文檔: SPS、PPS、IDR http //blog.csdn.net/heanyu/article/details/6205390
H.264碼流第一個 NALU 是 SPS(序列參數集Sequence Parameter Set)
對應H264標准文檔 7.3.2.1 序列參數集的語法進行解析
『叄』 基於Python快速整理微信視頻與圖片
作為超級APP,微信已經成了移動互聯網的入口。而我們也早已習慣通過微信收發圖片與視頻等多媒體文件。身為一名屌絲通信狗,經常出差海外,跟家人的溝通方式主要就靠微信了,每當累了或抑鬱了,銀早打開微信看看媳婦兒和孩子的視頻照片,就覺得開心多了。最終,微信變得越來越臃腫,視頻與圖片也必須早點保存到手機或鋒亮雀者電腦。但是最終導出的文件命名是讓人相當崩潰的。。。
是能忍 孰不能忍 ,作為一名有(xia)抱(zhe)負(teng)的屌絲,是絕不能容忍這么雜亂的文件命名的,也絕不可能因此就對這些文件逐一手動重命名的。
作為批量處理文件的利器,當然非Python莫屬了,而且有一堆優質庫可供調用,基本搜索一下能解決90%需求。對於這個需求初始想法是直接通過os庫獲取文件的創始時間,依此來重新命名文件,可是最終發現os庫獲取的是文件第一次拷貝到系統的時間,並非文件的初始創建時間。比如,2016-09-10日19:00:23拷貝2016-06-16日10:00:00拍攝的視頻文件到系統硬碟,系鍵拆統就記錄其時間為2016-09-10日19:00:23,並非6月16日。
上網搜了很久,發現使用exif與ffmpeg可以分別對照片與視頻提取初始創建時間:
Exif是用來存儲數碼照片的屬性信息與拍攝數據的,可以附加於JPEG、TIFF、RIFF等文件之中,為其增加有關數碼相機拍攝信息的內容和索引圖或圖像處理軟體的版本信息 。
使用exif工具查詢當前目錄下的pic1.png文件可以得到如下信息:
MacBook:BaiYun meixuhong$ exif -i pic1.png
顯然獲取到照片拍攝時間為2016:03:08 21:22:46。
ffmpeg自然不用多說,一款開源的牛逼哄哄的絕大多數開發者與公司都在用的音頻解碼軟體。使用它解析視頻文件自然也不在話下。如使用它解析1.mov文件,則會輸出如下信息:
MacBook:BaiYun meixuhong$ ffmpeg -i 1.mov
獲取到視頻文件拍攝於2016-07-10 09:36:54。
這倆工具當然無懈可擊,可是要想用python解析的話就得使用進程調用它們,而且只能將輸出結果保存在文本中,那如果有300個文件就得要輸出300個文本文件,很復雜,顯然不是我要的結果,需要換思路。
Hachoir is a Python library to view and edit a binary stream field by field. In other words, Hachoir allows you to 「browse」 any binary stream just like you browse directories and files.
即是說使用Hachoir可以直接查看文件的二進制文件,提取文件的 metadata 即可獲取到數碼照片與視頻的初始創建時間了。
實現過程到還是到我的項目主頁 Github 一覽吧。
執行完Python腳本,再來看各個文件名,以具體拍照時間格式嚴格命名並排序,感覺整個世界瞬間變得美好了。
『肆』 python拼接視頻不渲染
您要問的是python拼接視頻不渲染怎麼辦嗎?確枯鋒認視頻編碼格式、添加必要的視頻流參數、確認視頻解析度、幀率等參數。
1、確認視頻編碼格式:拼接視頻時,需要確保所有視頻的編碼格式相同,否則會導致視頻不渲染的問題。
2、添加必要的視頻流參數:視頻編碼格式需要添加必沒鉛晌要的視頻流參數,以便正確解碼和渲染視頻。
3、確認視頻解析度、幀率等參數:拼接視頻時,需要確保所有視頻的解析度、幀率等參數相同,否則會導致視頻不渲染的激念問題。
『伍』 Python文件處理里encoding和encode有事區別,bytes類型是什麼意思
python問題我來回答你。
首先你要知道的是,字元串在Python內部的表示是unicode(統一碼、萬國碼)編碼,很多編程語言都是這么設計的,各個國家通用編碼,因此,在做編碼轉換時,通常需要以unicode作為中間編碼,即先將其他編碼的字元串解碼(decode)成unicode,再從unicode編碼(encode)成另一種編碼。
decode的作用是將其他編碼的字元串轉換成unicode編碼,如str1.decode('gb2312'),表示將gb2312編碼的字元串str1轉換成unicode編碼。
encode的作用是將unicode編碼轉換成其他編碼的字元串,如str2.encode('gb2312'),表示將unicode編碼的字元串str2轉換成gb2312編碼。
因此,轉碼的時候一定要先搞明白,字元串str是什麼編碼,然後decode成unicode,然後再encode成其他編碼。
bytes類型是 Python 3.x版本新增的數據類型,在 Python 2.x 中是不存在的。字元串是以字元為單位進行處理的,bytes類型是以位元組為單位處理的。
bytes 只負責以位元組序列的形式(二進制形式)來存儲數據,至於這些數據到底表示什麼內容(字元串、數字、圖片、音頻等),完全由程序的解析方式決定。
說白了,bytes 只是簡單地記錄內存中的原始數據,至於如何使用這些數據,bytes 並不在意,你想怎麼使用就怎麼使用,bytes 並不約束你的行為。
bytes 類型的數據非常適合在互聯網上傳輸,可以用於網路通信編程;bytes 也可以用來存儲圖片、音頻、視頻等二進制格式的文件。
舉個例子:
b = b'' # 創建一個空的bytes
b = byte() # 創建一個空的bytes
b = b'hello' # 直接指定這個hello是bytes類型
b = bytes('string',encoding='編碼類型') #利用內置bytes方法,將字元串轉換為指定編碼的bytes
b = str.encode('編碼類型') # 利用字元串的encode方法編碼成bytes,默認為utf-8類型
bytes.decode('編碼類型'):將bytes對象解碼成字元串,默認使用utf-8進行解碼。
『陸』 一篇文章帶你深度解析Python線程和進程
使用Python中的線程模塊,能夠同時運行程序的不同部分,並簡化設計。如果你已經入門Python,並且想用線程來提升程序運行速度的話,希望這篇教程會對你有所幫助。
線程與進程
什麼是進程
進程是系統進行資源分配和調度的一個獨立單位 進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。每個進程都有自己的獨立內存空間,不同進程通過進程間通信來通信。由於進程比較重量,占據獨立的內存,所以上下文進程間的切換開銷(棧、寄存器、虛擬內存、文件句柄等)比較大,但相對比較穩定安全。
什麼是線程
CPU調度和分派的基本單位 線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。線程間通信主要通過共享內存,上下文切換很快,資源開銷較少,但相比進程不夠穩定容易丟失數據。
進程與線程的關系圖
線程與進程的區別:
進程
現實生活中,有很多的場景中的事情是同時進行的,比如開車的時候 手和腳共同來駕駛 汽車 ,比如唱歌跳舞也是同時進行的,再比如邊吃飯邊打電話;試想如果我們吃飯的時候有一個領導來電,我們肯定是立刻就接聽了。但是如果你吃完飯再接聽或者回電話,很可能會被開除。
注意:
多任務的概念
什麼叫 多任務 呢?簡單地說,就是操作系統可以同時運行多個任務。打個比方,你一邊在用瀏覽器上網,一邊在聽MP3,一邊在用Word趕作業,這就是多任務,至少同時有3個任務正在運行。還有很多任務悄悄地在後台同時運行著,只是桌面上沒有顯示而已。
現在,多核CPU已經非常普及了,但是,即使過去的單核CPU,也可以執行多任務。由於CPU執行代碼都是順序執行的,那麼,單核CPU是怎麼執行多任務的呢?
答案就是操作系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,執行0.01秒,這樣反復執行下去。表面上看,每個任務都是交替執行的,但是,由於CPU的執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。
真正的並行執行多任務只能在多核CPU上實現,但是,由於任務數量遠遠多於CPU的核心數量,所以,操作系統也會自動把很多任務輪流調度到每個核心上執行。 其實就是CPU執行速度太快啦!以至於我們感受不到在輪流調度。
並行與並發
並行(Parallelism)
並行:指兩個或兩個以上事件(或線程)在同一時刻發生,是真正意義上的不同事件或線程在同一時刻,在不同CPU資源呢上(多核),同時執行。
特點
並發(Concurrency)
指一個物理CPU(也可以多個物理CPU) 在若幹道程序(或線程)之間多路復用,並發性是對有限物理資源強制行使多用戶共享以提高效率。
特點
multiprocess.Process模塊
process模塊是一個創建進程的模塊,藉助這個模塊,就可以完成進程的創建。
語法:Process([group [, target [, name [, args [, kwargs]]]]])
由該類實例化得到的對象,表示一個子進程中的任務(尚未啟動)。
注意:1. 必須使用關鍵字方式來指定參數;2. args指定的為傳給target函數的位置參數,是一個元祖形式,必須有逗號。
參數介紹:
group:參數未使用,默認值為None。
target:表示調用對象,即子進程要執行的任務。
args:表示調用的位置參數元祖。
kwargs:表示調用對象的字典。如kwargs = {'name':Jack, 'age':18}。
name:子進程名稱。
代碼:
除了上面這些開啟進程的方法之外,還有一種以繼承Process的方式開啟進程的方式:
通過上面的研究,我們千方百計實現了程序的非同步,讓多個任務可以同時在幾個進程中並發處理,他們之間的運行沒有順序,一旦開啟也不受我們控制。盡管並發編程讓我們能更加充分的利用IO資源,但是也給我們帶來了新的問題。
當多個進程使用同一份數據資源的時候,就會引發數據安全或順序混亂問題,我們可以考慮加鎖,我們以模擬搶票為例,來看看數據安全的重要性。
加鎖可以保證多個進程修改同一塊數據時,同一時間只能有一個任務可以進行修改,即串列的修改。加鎖犧牲了速度,但是卻保證了數據的安全。
因此我們最好找尋一種解決方案能夠兼顧:1、效率高(多個進程共享一塊內存的數據)2、幫我們處理好鎖問題。
mutiprocessing模塊為我們提供的基於消息的IPC通信機制:隊列和管道。隊列和管道都是將數據存放於內存中 隊列又是基於(管道+鎖)實現的,可以讓我們從復雜的鎖問題中解脫出來, 我們應該盡量避免使用共享數據,盡可能使用消息傳遞和隊列,避免處理復雜的同步和鎖問題,而且在進程數目增多時,往往可以獲得更好的可獲展性( 後續擴展該內容 )。
線程
Python的threading模塊
Python 供了幾個用於多線程編程的模塊,包括 thread, threading 和 Queue 等。thread 和 threading 模塊允許程序員創建和管理線程。thread 模塊 供了基本的線程和鎖的支持,而 threading 供了更高級別,功能更強的線程管理的功能。Queue 模塊允許用戶創建一個可以用於多個線程之間 共享數據的隊列數據結構。
python創建和執行線程
創建線程代碼
1. 創建方法一:
2. 創建方法二:
進程和線程都是實現多任務的一種方式,例如:在同一台計算機上能同時運行多個QQ(進程),一個QQ可以打開多個聊天窗口(線程)。資源共享:進程不能共享資源,而線程共享所在進程的地址空間和其他資源,同時,線程有自己的棧和棧指針。所以在一個進程內的所有線程共享全局變數,但多線程對全局變數的更改會導致變數值得混亂。
代碼演示:
得到的結果是:
首先需要明確的一點是GIL並不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。就好比C++是一套語言(語法)標准,但是可以用不同的編譯器來編譯成可執行代碼。同樣一段代碼可以通過CPython,PyPy,Psyco等不同的Python執行環境來執行(其中的JPython就沒有GIL)。
那麼CPython實現中的GIL又是什麼呢?GIL全稱Global Interpreter Lock為了避免誤導,我們還是來看一下官方給出的解釋:
主要意思為:
因此,解釋器實際上被一個全局解釋器鎖保護著,它確保任何時候都只有一個Python線程執行。在多線程環境中,Python 虛擬機按以下方式執行:
由於GIL的存在,Python的多線程不能稱之為嚴格的多線程。因為 多線程下每個線程在執行的過程中都需要先獲取GIL,保證同一時刻只有一個線程在運行。
由於GIL的存在,即使是多線程,事實上同一時刻只能保證一個線程在運行, 既然這樣多線程的運行效率不就和單線程一樣了嗎,那為什麼還要使用多線程呢?
由於以前的電腦基本都是單核CPU,多線程和單線程幾乎看不出差別,可是由於計算機的迅速發展,現在的電腦幾乎都是多核CPU了,最少也是兩個核心數的,這時差別就出來了:通過之前的案例我們已經知道,即使在多核CPU中,多線程同一時刻也只有一個線程在運行,這樣不僅不能利用多核CPU的優勢,反而由於每個線程在多個CPU上是交替執行的,導致在不同CPU上切換時造成資源的浪費,反而會更慢。即原因是一個進程只存在一把gil鎖,當在執行多個線程時,內部會爭搶gil鎖,這會造成當某一個線程沒有搶到鎖的時候會讓cpu等待,進而不能合理利用多核cpu資源。
但是在使用多線程抓取網頁內容時,遇到IO阻塞時,正在執行的線程會暫時釋放GIL鎖,這時其它線程會利用這個空隙時間,執行自己的代碼,因此多線程抓取比單線程抓取性能要好,所以我們還是要使用多線程的。
GIL對多線程Python程序的影響
程序的性能受到計算密集型(CPU)的程序限制和I/O密集型的程序限制影響,那什麼是計算密集型和I/O密集型程序呢?
計算密集型:要進行大量的數值計算,例如進行上億的數字計算、計算圓周率、對視頻進行高清解碼等等。這種計算密集型任務雖然也可以用多任務完成,但是花費的主要時間在任務切換的時間,此時CPU執行任務的效率比較低。
IO密集型:涉及到網路請求(time.sleep())、磁碟IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低於CPU和內存的速度)。對於IO密集型任務,任務越多,CPU效率越高,但也有一個限度。
當然為了避免GIL對我們程序產生影響,我們也可以使用,線程鎖。
Lock&RLock
常用的資源共享鎖機制:有Lock、RLock、Semphore、Condition等,簡單給大家分享下Lock和RLock。
Lock
特點就是執行速度慢,但是保證了數據的安全性
RLock
使用鎖代碼操作不當就會產生死鎖的情況。
什麼是死鎖
死鎖:當線程A持有獨占鎖a,並嘗試去獲取獨占鎖b的同時,線程B持有獨占鎖b,並嘗試獲取獨占鎖a的情況下,就會發生AB兩個線程由於互相持有對方需要的鎖,而發生的阻塞現象,我們稱為死鎖。即死鎖是指多個進程因競爭資源而造成的一種僵局,若無外力作用,這些進程都將無法向前推進。
所以,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確定資源的合理分配演算法,避免進程永久占據系統資源。
死鎖代碼
python線程間通信
如果各個線程之間各干各的,確實不需要通信,這樣的代碼也十分的簡單。但這一般是不可能的,至少線程要和主線程進行通信,不然計算結果等內容無法取回。而實際情況中要復雜的多,多個線程間需要交換數據,才能得到正確的執行結果。
python中Queue是消息隊列,提供線程間通信機制,python3中重名為為queue,queue模塊塊下提供了幾個阻塞隊列,這些隊列主要用於實現線程通信。
在 queue 模塊下主要提供了三個類,分別代表三種隊列,它們的主要區別就在於進隊列、出隊列的不同。
簡單代碼演示
此時代碼會阻塞,因為queue中內容已滿,此時可以在第四個queue.put('蘋果')後面添加timeout,則成為 queue.put('蘋果',timeout=1)如果等待1秒鍾仍然是滿的就會拋出異常,可以捕獲異常。
同理如果隊列是空的,無法獲取到內容默認也會阻塞,如果不阻塞可以使用queue.get_nowait()。
在掌握了 Queue 阻塞隊列的特性之後,在下面程序中就可以利用 Queue 來實現線程通信了。
下面演示一個生產者和一個消費者,當然都可以多個
使用queue模塊,可在線程間進行通信,並保證了線程安全。
協程
協程,又稱微線程,纖程。英文名Coroutine。
協程是python個中另外一種實現多任務的方式,只不過比線程更小佔用更小執行單元(理解為需要的資源)。為啥說它是一個執行單元,因為它自帶CPU上下文。這樣只要在合適的時機, 我們可以把一個協程 切換到另一個協程。只要這個過程中保存或恢復 CPU上下文那麼程序還是可以運行的。
通俗的理解:在一個線程中的某個函數,可以在任何地方保存當前函數的一些臨時變數等信息,然後切換到另外一個函數中執行,注意不是通過調用函數的方式做到的,並且切換的次數以及什麼時候再切換到原來的函數都由開發者自己確定。
在實現多任務時,線程切換從系統層面遠不止保存和恢復 CPU上下文這么簡單。操作系統為了程序運行的高效性每個線程都有自己緩存Cache等等數據,操作系統還會幫你做這些數據的恢復操作。所以線程的切換非常耗性能。但是協程的切換只是單純的操作CPU的上下文,所以一秒鍾切換個上百萬次系統都抗的住。
greenlet與gevent
為了更好使用協程來完成多任務,除了使用原生的yield完成模擬協程的工作,其實python還有的greenlet模塊和gevent模塊,使實現協程變的更加簡單高效。
greenlet雖說實現了協程,但需要我們手工切換,太麻煩了,gevent是比greenlet更強大的並且能夠自動切換任務的模塊。
其原理是當一個greenlet遇到IO(指的是input output 輸入輸出,比如網路、文件操作等)操作時,比如訪問網路,就自動切換到其他的greenlet,等到IO操作完成,再在適當的時候切換回來繼續執行。
模擬耗時操作:
如果有耗時操作也可以換成,gevent中自己實現的模塊,這時候就需要打補丁了。
使用協程完成一個簡單的二手房信息的爬蟲代碼吧!
以下文章來源於Python專欄 ,作者宋宋
文章鏈接:https://mp.weixin.qq.com/s/2r3_ipU3HjdA5VnqSHjUnQ
『柒』 Python之OpenCV把一個視頻切分成多個等長視頻
import cv2
cap = cv2.VideoCapture('E:/極樂凈土.mp4')#導入路徑
j=1
i=1
fourcc = cv2.VideoWriter_fourcc(*'XVID')
fps =cap.get(cv2.CAP_PROP_FPS)
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
out = cv2.VideoWriter('E:/新建文件夾 (3)/' + str(j) + '.avi', fourcc,fps, size)#輸出路徑都是可以自己改的
while (True):
ret, frame = cap.read() # 捕獲一幀圖像
i = i + 1
out.write(frame) # 保存幀
if (i % 200== 0): # 每n幀切割為一個新的視頻
j = j + 1
out = cv2.VideoWriter('E:/新建文件夾 (3)/' + str(j) + '.avi', fourcc, fps, size)#路徑都是可以自己改的
cv2.imshow('frame', frame) # 顯示幀
# 判斷按鍵,如果按鍵為q,退出循環
if cv2.waitKey(25) & 0xFF == ord('q'):
break
cap.release()
out.release()
cv2.destroyAllWindows()