Wall of Text #12: File Extension is Magic

วันนี้มีรูปมาให้ดู

ถามว่ารูปนี้มีอะไรพิเศษ แนะนำให้ลองเซฟรูปดูแล้วจะเห็นว่ามันเป็น JPG และอย่างที่รู้กันว่า JPG มันหมุนไม่ได้

🤔

ที่เป็นอย่างนี้ก็เพราะว่า นามสกุลไฟล์เป็นเพียงข้อเสนอแนะ ใน Windows ระบบจะใช้นามสกุลไฟล์เพื่อบอกว่าจะต้องเปิดไฟล์นี้ด้วยโปรแกรมอะไร แต่ใน Linux แล้ว ไฟล์หลายๆ ประเภทระบบสามารถระบุประเภทของไฟล์ได้เองแล้วแม้ไม่ต้องใส่นามสกุลไฟล์!

เวลาที่เราแปลงนามสกุลไฟล์ เราถึงจะต้องไปเปิดในโปรแกรมขึ้นมาแล้วเซฟใหม่เป็นนามสกุลใหม่ ถึงจะเปิดได้ถูกต้อง เช่นถ้าเรามีไฟล์ MKV อยู่แต่โทรทัศน์ของเรารองรับเฉพาะไฟล์ MP4 เราก็ต้องใช้โปรแกรม Handbrake แปลงก่อนถึงจะไปเปิดในโทรทัศน์ได้ เพราะการเปลี่ยนชื่อไฟล์ไม่ได้เปลี่ยนเนื้อหาในไฟล์

(ไฟล์ thinking.jpg ด้านบน ก็สร้างมาจากการ rename thinking.gif เป็น thinking.jpg เฉยๆ โดยไม่ได้ผ่านโปรแกรมใดๆ)

It’s Magic

แล้ว Linux ทราบได้อย่างไรว่าจริงๆ แล้วมันคือ GIF ไม่ใช้ JPG? คำตอบคือ It’s magic!

ถ้าเราลองเปิดไฟล์รูปที่เซฟมาใน Notepad หรือโปรแกรมแก้ไขข้อความอื่นๆ เราจะเห็นแบบนี้

จะสังเกตว่าไฟล์นี้จะขึ้นต้นด้วย GIF89a ซึ่งมาตรฐานของไฟล์ GIF กำหนดไว้ให้เป็น “Magic number” ของไฟล์ GIF ทั้งหมด

ไฟล์ที่ไม่ได้ขึ้นต้น GIF89a ไม่ใช่ GIF แน่นอน แต่ถ้ามีอาจจะเป็นหรือไม่เป็นก็ได้ขึ้นอยู่กับเนื้อหา วิธีนี้ช่วยให้คอมพิวเตอร์เลือกต่อได้ง่ายว่าต้องเอาโปรแกรมอะไรมาตรวจสอบเนื้อหาที่เหลือ

(นอกจาก GIF89a แล้ว GIF ยังมี GIF87a อีกด้วยซึ่งเป็นมาตรฐานเก่า โดย 1989 และ 1987 เป็นปีที่มาตรฐานนั้นออกมาครั้งแรก)


ไม่ใช่ทุกไฟล์เสมอไปที่จะมีเครื่องหมายเป็นภาษามนุษย์อ่านง่ายแบบนี้ การเขียนโปรแกรมเองก็ไม่ได้มีกฎหมายอะไรที่บอกว่าไฟล์จะต้องมีเครื่องหมายบอกไฟล์เสมอไป ทำให้แต่ละไฟล์มีจุดบอกตำแหน่งแตกต่างกันออกไป เช่นไฟล์ doc (Microsoft Word รุ่นเก่า) นั้นเมื่อเปิดในโปรแกรม Hex Editor จะขึ้นต้นด้วย D0 CF 11 E0 A1 B1 1A E1 ซึ่งถ้าใช้โปรแกรม Notepad ทั่วๆ ไปอ่านจะเป็นตัวยึกยือ

เพื่อให้การจำแนกไฟล์ได้ง่ายขึ้นและเรียกจากโค้ดได้ จึงมีผู้รวบรวมข้อมูลเหล่านี้เป็นฐานข้อมูลไว้และทำเป็นโปรแกรมออกมาชื่อ file (ตรงไปตรงมาดี) เนื่องจากโปรแกรมนี้ออกมาพร้อมกับ UNIX แล้วทำให้ลินุกซ์ทุกตัวติดตั้งโปรแกรม file ได้หมด วิธีการใช้งานใช้งานเพียงพิมพ์ file ตามด้วยตำแหน่งของไฟล์ปริศนา

$ file thinking
thinking: GIF image data, version 89a, 498 x 498

นอกจากจะบอกว่าเป็นไฟล์ประเภทใดถูกแล้ว สำหรับไฟล์ GIF โปรแกรมยังบอกขนาดได้อีกด้วย!

Standing on the shoulder of giants

ภาษาอังกฤษมีสำนวนว่า “ยืนบนไหล่ของยักษ์” หมายถึงว่าความก้าวหน้าต่างๆ เกิดขึ้นจากการต่อยอดจากความก้าวหน้าเดิม

ในโปรแกรมมิ่งก็เช่นเดียวกัน เวลาเขียนโปรแกรมเราก็มักจะใช้ Library สำเร็จรูป เช่น ถ้าเราจะส่งข้อมูลเราระหว่างโปรแกรมเราอาจจะใช้ฟอร์แมต JSON จะได้ไม่ต้องคิดค้นขึ้นมาใหม่

ฟอร์แมตไฟล์ต่างๆ ก็เช่นเดียวกัน หลายๆ ฟอร์แมตเมื่อเราเปิดมาแล้วก็อาจจะพบว่ามันมี Magic ของไฟล์ประเภทอื่นอยู่ เช่นถ้าเราเปิดไฟล์ docx (Microsoft Word 2007+), jar (โปรแกรม Java), APK (แอพ Android), IPA (แอพ iOS) ขึ้นมาจะเห็นว่ามันขึ้นต้นด้วย PK ซึ่งเป็น Magic ของไฟล์ Zip และสามารถใช้โปรแกรมเปิดไฟล์ Zip เปิดได้ด้วย

docx

ไฟล์เหล่านี้บางประเภทก็สามารถใช้โปรแกรม file ตรวจสอบได้เช่นกัน

$ file file-sample
file-sample: Microsoft Word 2007+

Next Time

Video – Free HD Stock-Footage and Motion Graphics by CyberWebFX https://www.youtube.com/c/CyberWebFX
Music – Floaters – Jimmy Fontanez CC-BY 2.0

วิดีโอข้างบนขนาด 1MB ความละเอียด 480p ภาพก็ค่อนข้างโอเค เสียงเพลงก็โอเค

ส่วนวิดีโอนี้ในอัพโหลดในปี 2008

ความละเอียด 240p ขนาด 4MB

Only if technology could’ve come sooner

Wall of Text #11: C Array and Fraps

น้องเค้ามีคำถามจากการบ้านว่า

จงเปรียบเทียบระหว่าง Array ในภาษาเก่าๆ กับ Python list

ผมชอบคำถามนี้นะ คำตอบที่เค้าเขียนคือ

ในภาษา C สมาชิกของ Array ทั้งหมดจะต้องเป็น data type เดียวกันทั้งหมด และขนาดของ Array จะตายตัว แต่ใน Python list สามารถคละ data type กันได้ และสามารถเปลี่ยนขนาดของ Array ได้โดยการเพิ่มหรือลบสมาชิก

ที่น่าสนใจคือ จริงๆ แล้ว C Array ไม่ได้มีขนาดตายตัว…. มันไม่มีขนาดด้วยซ้ำ

Array ในภาษา C จริงๆ แล้วมันคือ Pointer ซึ่งเก็บที่อยู่ของข้อมูล การใช้ Pointer ในภาษา C จะประมาณนี้

ถ้าลองรันดูจะเห็นว่า output คือ 10 ถึงเราจะไม่ได้แก้ x ตรงๆ แต่เราแก้ผ่าน pointer แทน

คำสั่ง *x_ptr จะเป็นการอ่านค่าที่ตำแหน่งที่ x_ptr ชี้ไปอยู่ ซึ่งจริงๆ แล้วคำสั่งนี้มีค่าเท่ากับ x_ptr[0]… แล้ว x_ptr[1] คืออะไรล่ะ?

x_ptr[1] มีค่าเท่ากับ *(x_ptr + 1) เนื่องจากใน C สมาชิกทุกตัวใน array จะอยู่ใน memory ตำแหน่งที่ติดกันไปเรื่อยๆ และสมาชิกทุกตัวมีขนาดเท่ากัน (เพราะเป็น data type เดียวกัน) ดังนั้นถ้าเราทราบที่อยู่ของสมาชิกตัวแรก ตัวถัดไปก็คือเลื่อนไปจากสมาชิกตัวแรกเท่ากับขนาดของสมาชิก 1 ตัว

ดังนั้นตัวแปรที่เก็บ C array จริงๆ แล้วไม่ได้เก็บ array แต่เก็บตำแหน่งของสมาชิกตัวแรกเท่านั้น ส่วนการประกาศ array จริงๆ แล้วก็คือการจองพื้นที่พอให้เก็บสมาชิกใน array ตามจำนวนที่ขอ และคืนตำแหน่งของสมาชิกตัวแรกมาให้

แต่จริงๆ แล้ว เราสามารถอ่านหรือเขียนค่าไปเกินกว่าพื้นที่ที่ขอได้เช่นเดียวกัน เนื่องจาก C ไม่มีการเก็บไว้ด้วยซ้ำว่า array มีขนาดเท่าไร (แต่ไม่การันตีผลลัพท์นะว่าถ้าอ่านเขียนเกินพื้นที่ที่จองไว้จะได้ผลอย่างไร)

Portable assembly

หลังฉากของภาษา C แล้วน่าสนใจว่ามันเป็นภาษาที่ไม่ค่อยสูงกว่า assembly เท่าไรนัก มันไม่พยายามซ่อนการทำงานของคอมพิวเตอร์ ซึ่งก็ตรงตามสิ่งที่ผู้สร้าง C คิดไว้คือมันเป็นเพียงแค่ Portable Assembly

ซึ่งในภาษา C เองก็ยังมีคำสั่ง __asm__ ที่เอาไว้เขียน assembly code ผสมกับภาษา C ได้เมื่อจำเป็น

และคำสั่งนี้ก็มีใช้อยู่ในโค้ดจริงๆ ด้วย เช่นถ้าเราไปอ่านโค้ดของ H.264 decoder ก็จะเห็นว่าเค้าใช้ Assembly ผสมกับโค้ด C เพราะการ decode video นั้นอาจจะใช้ฟีเจอร์เฉพาะของ CPU เข้ามาช่วยได้ ซึ่ง compiler อาจจะไม่สามารถแปลงโค้ด C เป็นคำสั่ง CPU พวกนี้ได้ (*)

Hardware acceleration

หลายๆ อย่างที่คอมพิวเตอร์เราทำอยู่นั้น ถึงแม้ว่าคอมพิวเตอร์จะเร็วขึ้นแต่การที่ไม่มี hardware พิเศษมารองรับก็อาจจะทำให้ทำงานได้ช้าหรือทำงานไม่ได้เลย

GPU ของ Raspberry Pi นั้นสามารถถอดรหัส video H.264 (mp4) ได้สบายๆ หลายคนเอา Raspberry Pi มาลง Kodi ดูหนังซึ่งก็จะเห็นว่าภาพก็ลื่นดี แต่ถ้าโปรแกรมที่ใช้นั้นไม่รองรับการใช้ hardware acceleration แล้วก็จะพบว่า CPU ของ Raspberry Pi decode ไฟล์ mp4 ไม่ไหวเลย

บน PC ก็เช่นเดียวกัน ถ้าเราดูไฟล์วิดีโอ MP4 ในเครื่อง จะเห็นว่า CPU load จะไม่ถึง 10% เท่านั้น แต่ถ้าลองดู YouTube 4K จะพบว่า CPU ขึ้นไปสูงถึง 50-100% เลยทีเดียว เนื่องจากฟอร์แมต VP9 (ในขณะที่เขียนนี้) hardware หลายๆ รุ่นยังไม่รองรับการถอดรหัส VP9 เลยจะต้องใช้พลัง CPU ประมวลผล ซึ่งนอกจากจะหน่วงเครื่องแล้วยังเปลืองแบตเตอรี่อีกด้วย

อย่าลืมปรับเป็น 4k 60fps

สมัย Windows XP ถ้าใครเคย Capture โปรแกรมวิดีโอด้วยปุ่ม print screen ก็จะเห็นว่ากรอบที่เล่น video จะเป็นจอดำ หรือเวลากด Print screen บนเกม Full screen ก็เช่นกัน วิธีแก้ไขก็คือใช้โปรแกรมพิเศษที่จับภาพหน้าจอเกมได้ เช่น Fraps

ที่เป็นอย่างนี้ก็เพราะ Video หรือเกมก็แล้วแต่นั้น ภาพถูกสร้างขึ้นบนการ์ดจอโดยตรงโดยไม่ได้ผ่าน desktop เมื่อเรากด print screen จึงจะเห็นแค่ฉากหลังดำๆ เท่านั้น

ตั้งแต่ Windows Vista เป็นต้นมา Desktop compositing (Aero) เป็นที่นิยมมากขึ้นซึ่งทำให้ประสบการณ์ใช้งาน desktop ดีขึ้น ภาพไม่กระพริบ เวลาลากโปรแกรมไม่กระตุก (ข้อเสียคือเกมที่รันใน Windowed mode จะไม่สามารถรันเร็วกว่าเฟรมเรทของ Compositor ได้ ซึ่งถ้าใครใช้จอ 120Hz อาจจะต้องรันเกมใน Full screen แทน) ซึ่ง Desktop compositing นี้ทำงานบนการ์ดจอทำให้การทำ Print screen เปลี่ยนไปและเรามักจะสามารถ screenshot ทุกอย่างบนหน้าจอได้หมดแล้ว นอกเหนือจากเกม Full screen


ผมคิดว่าการศึกษาการทำงานของคอมพิวเตอร์เป็นเรื่องที่น่าสนใจมาก ถ้ามีคนถามว่าทำไม print screen แล้วจอดำ เราจะได้เข้าใจเหตุผลว่ามันไม่ใช่ “เพราะคุณกด print screen จอเกมไม่ได้ถ้าไม่ได้ใช้ Fraps” แต่เพราะภาพมันอยู่ที่การ์ดจอแล้วเรา print screen บน CPU ต่างหากล่ะ