2016-02-20 70 views
1
import wave,struct 
f = wave.open('bird.wav', 'r') 

for i in range(5,10): 
    frame = f.readframes(i) 
    print frame 
struct.unpack('<H',frame) 

我使用上面的代碼從python的立體聲wav文件中提取字節。但是,不是字節,我得到一些亂碼字符。使用struct.unpack()功能我收到以下錯誤在python中讀取WAV文件

「解壓需要長度爲2的字符串參數」

我在代碼中進行打印這些字節中的1和0的什麼樣的變化?我想稍後修改用於隱寫的音頻幀的LSB。

+1

亂碼字符可能是字節,它們只是以這種方式打印。什麼讓你覺得他們不是?你有沒有嘗試'打印(類型(框架))'? – rofls

+0

子過程ffmpeg? – alphanumeric

+1

@rofls - 類型是str。如何以1和0打印這些字節? – aditya

回答

1

我不確定你爲什麼要用二進制打印這些字節,但這樣做很容易。

您需要將字節轉換爲整數,然後使用str.format方法對其進行格式化,舊的%風格的格式不會執行位。

執行該轉換的簡單方法是使用ord函數,但對於大量字節,最好通過創建bytearray來將它們轉換爲一次命中。

#Some bytes, using hexadecimal escape codes 
s = '\x01\x07\x0f\x35\xad\xff' 
print ' '.join(['{0:08b}'.format(ord(c)) for c in s]) 

b = bytearray(s) 
print ' '.join(['{0:08b}'.format(u) for u in b]) 

輸出

00000001 00000111 00001111 00110101 10101101 11111111 
00000001 00000111 00001111 00110101 10101101 11111111 

通常,十六進制符號是不是二進制讀取更方便。

from binascii import hexlify 

print hexlify(s) 
print ' '.join(['%02X' % u for u in b]) 
print ' '.join(['%02X' % ord(c) for c in s]) 
print ' '.join(['{0:02X}'.format(ord(c)) for c in s])  

輸出

01070f35adff 
01 07 0F 35 AD FF 
01 07 0F 35 AD FF 
01 07 0F 35 AD FF 

我剛纔看到重新隱寫您的評論。旋轉字節位的最方便的方法是使用bytearray。您可以使用str函數輕鬆地將bytearray轉換回字符串。

​​

輸出

01070f35adff 

字符串格式化選項在官方Python文檔描述。對於舊的%風格的格式,請參閱5.6.2. String Formatting Operations。對於現代str.format選項,請參閱7.1.3. Format String Syntax7.1.3.1. Format Specification Mini-Language

{0:08b}0之前的冒號是字段位置(可以在最近版本的Python中省略)。它表示我們要將這個格式代碼應用到.format的第一個參數,即索引爲零的參數。例如,

'{0} {2} {1}'.format('one', 'two', 'three') 

打印

one three two 

b意味着我們要打印一個數字作爲二進制文件。 08表示我們希望輸出爲8個字符寬,對於小於8位的二進制數填充零填充。

%02X大寫X意味着我們要打印一個數爲十六進制,使用大寫字母A-F的數字大於9,我們可以用小寫x獲得小寫字母。 02表示我們希望輸出爲2個字符寬,對於小於2個十六進制數字的十六進制數字填充零。

+0

你能解釋一些格式化選項,比如'{0:08b}'和'%02X'嗎?那些做特別的事情嗎? – rofls

0

如果要修改字節的LSB,將值表達爲二進制字符串沒有意義。實際上,你會做沿東西線(在僞代碼):

byte = '\x6h' 
binary = convert_to_bits(byte) # some way of getting 1s and 0s in a string 
binary = binary[:7] + my_bit_string 
byte = convert_to_byte(binary) 

有更直接,更有效的方式來修改一個位值,這就是與bitwise operators。例如,假設我們要將01001001(十進制73)更改爲01001000.我們想要創建一個位掩碼11111110,它的十進制值是254,而AND它與我們的值。

>>> value = 73 & 254 
>>> value 
72 
>>> '{0:08b}'.format(value) 
'01001000' 

當你嵌入位字節中,LSB可能改變也可能不會。有很多方法可以解決這個問題,但最直接的做法是將lsb清零,然後用OR(如果您還想嵌入多個位,則功能非常全面)將其覆蓋。

byte = (byte & 254) | my_bit 

你也可以零出LSB用right shift,隨後left shift,但這需要2個操作,而不是一個。

byte = ((byte >> 1) << 1) | my_bit 

或者你可以檢查LSB和你的位是否是不同的,並用XOR翻轉。但是,這種方法使用分支,效率最低。

if (byte & 1) != my_bit: 
    byte = byte^1 
# no need to do anything if they are the same 

所以,你需要做的就是將您的字節整數數組。你可以使用[ord(byte) for byte in frame],但有更高效的內置方式。隨着bytearray()bytes()

>>> frame = '\x0f\x02\x0e\x02\xf7\x00\xf7\x00T\xffT\xff' 
>>> frame_bytes = bytearray(frame) 
>>> frame_bytes[0] 
15 
>>> frame_bytes[0] = 14  # modify 
>>> bytes(frame_bytes)  # convert back to bytes 
'\x0e\x02\x0e\x02\xf7\x00\xf7\x00T\xffT\xff' 

array.array()(這似乎是幾十萬字節的一小一丁點兒慢):

>>> import array 
>>> frame = '\x0f\x02\x0e\x02\xf7\x00\xf7\x00T\xffT\xff' 
>>> frame_bytes = array.array('B', frame) 
>>> frame_bytes[0] 
15 
>>> frame_bytes[0] = 14  # modify 
>>> frame_bytes.tostring() # convert back to bytes; in Python 3 use `tobytes()` 
'\x0e\x02\x0e\x02\xf7\x00\xf7\x00T\xffT\xff' 

嵌入和提取的例子。

frame = '\x0f\x02\x0e\x02\xf7\xf7T\xffT\xff' 
bits = [0, 0, 1, 1, 0] 

# Embedding 
frame_bytes = bytearray(frame) 
for i, bit in enumerate(bits): 
    frame_bytes[i] = (frame_bytes[i] & 254) | bit 
frame_modified = bytes(frame_bytes) 

# Extraction 
frame_bytes = bytearray(frame_modified) 
extracted = [frame_bytes[i] & 1 for i in range(5)] 
assert bits == extracted 

如果你的祕密是一個字符串或字節序列,它很容易convert them to a list of 1s and 0s

最後,請確保您不要修改任何標題數據,因爲這可能會導致文件無法讀取。