本篇主要學習如何Python自定義模塊並調用該模塊,並重點介紹Python正則表達式的強大的文本處理能力。 案例故事: 任何一款終端產品只要涉及音頻輸出,就肯定涉及音頻的解碼, 作為一名專業的AV (Audio & Video)測試人員,我們需要一堆的規範化標準的的音頻測試文件, 但是發現音頻資源名 ...
本篇主要學習如何Python自定義模塊並調用該模塊,並重點介紹Python正則表達式的強大的文本處理能力。
案例故事: 任何一款終端產品只要涉及音頻輸出,就肯定涉及音頻的解碼,
作為一名專業的AV (Audio & Video)測試人員,我們需要一堆的規範化標準的的音頻測試文件,
但是發現音頻資源名字命名的很隨意比如:青藏高原.wma,
以上命名不能看出音頻文件的具體編碼規格,
測試經理要求我進行批量重命名工作,模板如下,
音頻編碼格式_音頻採樣率_聲道數_比特率_容器.容器, 例如:
wma_44.1KHz_stereo_192Kbps_wma.wma
音頻編解碼基本知識
將聲音存儲為音頻文件的時候,需要經過以下幾個步驟:
- 需要錄製(採樣)成音頻原始數據;
- 通過一定的編碼壓縮技術將音頻原始數據儘可能地壓縮成最小;
- 通過音頻容器以獨立文件的形式存儲音頻;
主要涉及以下技術參數:
音頻參數 | 參數釋義 | 舉例 |
---|---|---|
音頻編碼格式 (壓縮技術) |
即將音頻數據壓縮的一類技術, 不同的編碼格式, 其壓縮率與壓縮效果不一樣。 主要分成2類: 有損壓縮(會導致失真,壓縮率高) 無損壓縮(儘量保真,壓縮率低) |
有損壓縮: Mpeg1 Level3(即我們常說的Mp3); WMA;LCACC; LTPAAC; HE-AAC, HE-AACV2; AMR-WB, AMN-NB; Vorbis;MiDi; 無損壓縮: Flac;PCM;APE |
音頻採樣率 (單位:Khz) |
將聲音記錄成數據文件的時候, 需要對聲音進行採樣, 每秒鐘對聲音信號的採樣次數即採樣率。 採樣率越高,越能還原現場音質。 |
比如44.1Khz代表每秒採樣44100次 8Khz, 11.025Khz, 22.5Khz, 32Khz, 44.1Khz, 48Khz,96Khz |
音頻位深度 (單位:bit) |
每次採樣,採集數據量的大小 | 8bit, 16bit |
音頻聲道數 (單位:channel) |
一般有雙聲道即2個聲道, 錄音的時候肯定是需要2個麥克風同時錄製的, 即同時採集了2個音頻流。 5聲道,則需要有5個麥克風同時錄製 |
單聲道:1 channel, Mono 雙聲道:2 channels, Stereo 5聲道:5 channels |
音頻比特率 (單位:Kbps) |
每秒鐘的音頻流的數據量, 其大小是直接取決於: 音頻編碼格式(壓縮率), 採樣率,位深度,聲道數乘積 |
48Kbps, 96Kbps, 128Kbps,256Kbps |
音頻容器 | 文件尾碼,將音頻流封裝的一種文件格式 | .mp3; .wma; .aac; .3gp; .mp4; .flac ; .ape;.pcm; .raw; .mid; .ogg; .wav; .mkv; .m4a |
我們碰到的任何音頻文件,都是數據的集合,
一般數據越大,其音頻播放質量越好。
準備階段
- 確保mediainfo.exe 命令行工具已經加入環境變數,查看其具體功能方法。
- 以下是某個音頻文件的mediainfo信息, 都是文本,Python處理起來肯定很簡單的。
- 如果要進行批量重命名音頻,我們還是用輸入輸出文件架構,如下:
+---Input_Audio #批量放入待命名的音頻 | 1.mp3 | 青藏高原.wma | +---Output_Video #批量輸出已命名的音頻 | Mpeg3L1_44.1KHz_stereo_128Kbps_mp3.mp3 | wma_44.1Khz_stereo_96Kbps_wma.wma | \audio_info.py # 獲取音頻文件info信息的模塊, \rename_audio.py #調用audio_info.py並實現重名,可雙擊運行
定義audio_info.py模塊
由於涉及較複雜的代碼,建議直接用面向對象類的編程方式實現:
# coding=utf-8
import os
import re
class AudioInfoGetter():
'''獲取音頻文件的codec, sample_rate, channels, bitrate'''
def __init__(self, audio_file):
'''判斷文件是否存在,如果存在獲取其mediainfo信息'''
if os.path.exists(audio_file):
self.audio_file = audio_file
self.info = os.popen("mediainfo %s" % self.audio_file).read()
else:
raise FileNotFoundError("Not this File!") # 如果多媒體文件路徑不存在,必須中斷
def get_audio_codec(self):
'''獲取音頻的編碼格式,比如Mepg3L1就是我們常說的Mp3, 還有AAC系列,AMR系列,Flac等等'''
try:
audio_codec = re.findall(r"Format\s+:\s(.*)", self.info)[-1] # 取第最後一個Format欄位
if (audio_codec == "MPEG Audio"):
audio_codec = self.__format_mpeg_audio()
elif (audio_codec == "AMR"):
audio_codec = self.__format_amr_audio()
elif (audio_codec == "AAC"):
audio_codec = self.__format_aac_audio()
elif ("PCM" in audio_codec):
audio_codec = "PCM"
else:
pass
except:
audio_codec = "undef" # 防止程式因為異常而中斷
return audio_codec
def get_audio_channel(self):
'''獲取聲道數,如果是雙聲道是stereo, 如果是單聲道是mono,還存在5聲道的情況'''
try:
audio_channel = re.findall(r"Channel\(s\)\s+:\s(.*)\schannel.*", self.info)[-1]
if audio_channel == "2":
audio_channel = "stereo"
elif audio_channel == "1":
audio_channel = "mono"
elif audio_channel == "5":
audio_channel = "5-channels"
else:
audio_channel = "undef" # 設置為undef,表示特殊異常規格,建議人工驗證並考慮手動重命名
except:
audio_channel = "undef" # 防止程式因為異常而中斷
return audio_channel
def get_audio_sample_rate(self):
'''獲取音頻採樣率,比如常見的48Khz, 44.1Khz等'''
try:
audio_sample_rate = re.findall(r"Sampling rate\s+:\s(.*)", self.info)[-1]
audio_sample_rate = audio_sample_rate.replace(" ", "") # 去1 536 這個數字里的空格
if "K" not in audio_sample_rate:
audio_sample_rate = audio_sample_rate.replace("Hz", "") # 先去掉“Hz"
audio_sample_rate = str(int(int(audio_sample_rate) / 1000))
audio_sample_rate = audio_sample_rate + "Khz" # 再添上KHz的單位
elif audio_sample_rate.endswith(".0Khz"):
audio_sample_rate = audio_sample_rate.replace(".0", "")
elif "/" in audio_sample_rate: # 偶爾會有多個採樣率的情況 48.0 Khz / 44.1 KHz 這種
audio_sample_rate = "undef" # # 設置為undef,表示特殊異常規格,建議人工驗證並考慮手動重命名
else:
pass
except:
audio_sample_rate = "undef" # 防止程式因為異常而中斷
return audio_sample_rate
def get_audio_bitrate(self):
'''獲取音頻比特率,比如96Kbps, 128Kbps'''
try:
audio_bitrate = re.findall(r"Bit rate\s+:\s(.*)", self.info)[-1]
audio_bitrate = audio_bitrate.replace(" ", "") # 去掉1 536 這個數字里的空格
if "K" not in audio_bitrate:
audio_bitrate = audio_bitrate.replace("bps", "") # 先去掉“bps"
audio_bitrate = str(int(audio_bitrate) / 1000)
audio_bitrate = audio_bitrate + "Kbps" # 再添上KHz的單位
except:
audio_bitrate = "undef" # 防止程式因為異常而中斷
return audio_bitrate
def get_audio_container(self):
'''獲取音頻容器,即文件尾碼名'''
_, audio_container = os.path.splitext(self.audio_file)
if not audio_container:
raise NameError("This file no extension")
audio_container = audio_container.replace(".", "")
return audio_container
def __format_mpeg_audio(self):
'''如果是Mpeg Auido的音頻格式(常見的比如Mp3(Mpeg1 Level3)),進行格式化'''
try:
mpeg_audio_version = re.findall(r"Format version\s+:\sVersion\s(.*)", self.info)[-1]
mpeg_audio_profile = re.findall(r"Format profile\s+:\sLayer\s(.*)", self.info)[-1]
mpeg_audio_profile = "Mpeg%sL%s" % (mpeg_audio_version, mpeg_audio_profile)
except:
mpeg_audio_profile = "undef"
return mpeg_audio_profile
def __format_amr_audio(self):
'''如果是amr的音頻格式(常見的比如amr-nb amr-wb),進行格式化'''
try:
amr_profile = re.findall(r"Format profile\s+:\s(.*)", self.info)[-1]
if amr_profile == "Wide band":
amr_profile = "AMR-WB"
elif amr_profile == "Narrow band":
amr_profile = "AMR-NB"
else:
amr_profile = "undef" # 設置為undef,表示特殊異常規格,建議人工驗證並考慮手動重命名
except:
amr_profile = "undef"
return amr_profile
def __format_aac_audio(self):
'''如果是acc的音頻格式(常見的比如AAC-LC, AAC-LTP, HE-AAC, HE-AACV2),進行格式化'''
try:
amr_profile = re.findall(r"Format profile\s+:\s(.*)", self.info)[-1]
if amr_profile == "LC":
aac_profile = "AAC-LC"
elif amr_profile == "LTP":
aac_profile = "AAC-LTP"
elif amr_profile.startswith("HE-AACv2"):
aac_profile = "HE-AACV2"
elif amr_profile.startswith("HE-AAC"):
aac_profile = "HE-AAC"
else:
aac_profile = "undef" # 設置為undef,表示特殊異常規格,建議人工驗證並考慮手動重命名
except:
aac_profile = "undef"
return aac_profile
if __name__ == '__main__':
# 以下代碼塊,只是用來測試本模塊的,一般不建議直接在這裡大面積調用本模塊'''
a_obj = AudioInfoGetter("C:\\j.3gp")
audio_codec = a_obj.get_audio_codec()
print(audio_codec)
調用audio_info.py模塊並實現批量重命名
# coding=utf-8
import os
import audio_info
from shutil import copyfile
curdir = os.getcwd()
# 輸入文件夾,放入待重命名的音頻
input_audio_path = os.path.join(curdir, "Input_Audio")
filelist = os.listdir(input_audio_path) #獲取文件列表
# 輸出文件夾,已命名的視頻存放在這裡
output_audio_path = os.path.join(curdir, "Output_Audio")
# 如果沒有Output_Audio這個文件夾,則創建這個文件夾
if not os.path.exists(output_audio_path):
os.mkdir(output_audio_path)
if filelist: # 如果文件列表不為空
for i in filelist: # 變數文件列表
audio_file = os.path.join(input_audio_path, i)
a_obj = audio_info.AudioInfoGetter(audio_file)
audio_codec = a_obj.get_audio_codec()
audio_sample_rate = a_obj.get_audio_sample_rate()
audio_channel = a_obj.get_audio_channel()
audio_bitrate = a_obj.get_audio_bitrate()
audio_container = a_obj.get_audio_container()
new_audio_name = audio_codec + "_" + audio_sample_rate + "_" + audio_channel + "_" \
+ audio_bitrate + "_" + audio_container + "." + audio_container
print(new_audio_name)
new_audio_file = os.path.join(output_audio_path, new_audio_name)
copyfile(audio_file, new_audio_file) # 複製文件
else:
print("It's a Empty folder, please input the audio files which need to be renamed firstly!!!")
os.system("pause")
本案例練手素材下載
包含:mediainfo.exe(更建議丟到某個環境變數里去),
各種編碼格式的音頻文件,audio_info.py模塊,rename_audio.py批處理腳本
調轉自拍教程官網下載
運行效果如下:
小提示: 比如Android手機,Google推出了CDD(Compatibiltiy Definition Document相容性定義文檔),
其第5部分,涉及了很多音頻編解碼格式的規定:
這就是Android最主要的音頻多媒體編解碼測試需求。
更多更好的原創文章,請訪問官方網站:www.zipython.com
自拍教程(自動化測試Python教程,武散人編著)
原文鏈接:https://www.zipython.com/#/detail?id=2c26d313cea54e8ab9ab3ecb612b986c
也可關註“武散人”微信訂閱號,隨時接受文章推送。