Apple codec & Sony codec

1. Vô đề.
Nghỉ hè dẫn vợ con đi chơi Fuji-Q, vùng dưới chân nói Phú Sỹ. Nhân dịp này mang con Handycam HDR-CX470 của Sony đi quay thằng ku chơi để về up lên cho các cụ ở nhà xem.
Vì không phải dân biết chơi Camcorder nên không có ý định review đánh giá con Handycam, chỉ là về xử lý thấy dung lượng file lớn quá, trong chừng mức có thể (về công cụ lẫn thời gian) thử xem qua xem các thánh Sony đang thiết kế bộ codec thế nào.
Vì đang dùng iphoneSE thần thánh, nên cũng tiện tay lướt qua xem Apple bí hiểm có gì khác không.

2. Công cụ sử dụng.
2.1. FFMPEG.
Trong thế giới multimedia processing, thì ffmpeg thì quá nổi tiểng nên không cần giới thiệu chi tiết. Đây là công cụ mã nguồn mở (opensource) mạnh mẽ, hỗ trợ đủ mọi video/audio đến multimedia file format (container).

FFMPEG được bắt đầu từ hồi “em mong ước đến năm 2000″(nhưng chiến tranh không tàn mà còn sống khỏe) bởi Fabrice Bellard (một trong những lập trình viên đẳng cấp nhất thế gian mà đang còn sống cho đến thời điểm hiện tại 07-2018) cho đến 2004. Sau đó được dẫn dắt bởi Michael Niedermayer Từ 2004 đến 2015.

Đến 13/03/2011, sau một thập kỉ phát triên, do không đồng quan điểm, một số thành viên nhóm phát triển (chắc ngộ Clean Code) muốn ưu tiên nâng cao chất lượng code & API. Các thanh niên đó đã tách thành dự án libav, vụ này gây ra bao nhiêu phiền hà cho cộng đồng :-(. Tuy nhiên các sửa đổi của libav vẫn được merger vào ffmpeg, có vẻ công việc này nhàm chán quá nên Michael Niedermayer đã từ chức leader vào 2015[4].

Ffmpeg được release theo package cho từng os riêng. Nếu không có mục đích tham gia phát triển ffmpeg hoặc cần sửa đổi source code thì strong recommend là lên trang chủ down về mà dùng luôn, vì nó đã enable rất nhiều lib phụ thuộc.

2.2. JM tool.
Trái ngược với ffmpeg, thì không nhiều người biết đến công cụ này, chắc chỉ sử dụng trong giới nghiên cứu về video coding, vốn dĩ đấy cũng là mục đích của nó chứ không phải hướng đến lĩnh vực ứng dụng. JM là viết tắt của Join Model (nghe tên hơi cà rốt), đây là h.264 codec (jvt codec),được nhóm JVT (nhóm hợp tác MPEG và VCEG) phát triển và dùng để xây dựng chuẩn nén h.264. Làm gì có codec nào có trước khi public chuẩn nén, nên dĩ nhiên nó dành quán quân về h.264 codec đầu tiên trên thế giới :-).

JM là full codec,  đầy đủ cả decoder lẫn encoder h.264. Sử dụng decoder của JM cho phép phân tích các thông tin chi tiết hơn về h.264.
JM có thể down trên trang chủ của ITU-T ở đây[3], theo README là có thể biên dịch ^^.

3. Thực hành.
3.1. Chuẩn bị video.
Đặt HDR-CX470 ở chế độ 1080P, 60fps, quay video nhiều cảnh động để đảm bảo bộ encoder làm việc tích cực nhất có thể. Sẽ phân tích tầm 1 phút, thời gian đủ dài để bộ encoder ổn định, do đó cần quay hơn 1 phút.

ffmpeg command cắt lấy 1 phút đầu tiên.

ffmpeg -ss 00:00:00 -to 00:01:00 -i 20180718125711.MTS -c copy sony.mts

3.2. Phân tích thông tin video cơ bản.
Cần tham khảo document của ffmpeg để hiểu các thông tin trong khi phân tích.
● Đầu tiên là xem thông tin cơ bản nhất về các stream video/audio, bằng command dưới.

ffprobe -show_streams sony.mts

Từ output log, lấy ra một số giá trị như dưới.

str1
str2

Từ đó có thể rút ra được vài thông tin sau.
– Birate 27 Mbps
– High profile, level 4.2. xem ý nghĩa profile/level ở [7].
– Khi encode, chỉ sử dụng 1 picture để tham chiếu đối với tham chiếu liên ảnh. (Với level 4.2 thì h.264 support tối đa có thể tham chiếu đồng thời 4 ảnh).

● Phân tích chi tiết hơn chút về thông tin từng ảnh.

ffprobe  -show_frames -select_streams v:0 sony.mts

Chỉ tập trung vào 2 thông tin khoanh đỏ.
str3
Thông tin này thay đổi theo từng picture, hình ở trên chỉ là picture đầu tiên.
Sau khi kiểm tra toàn bộ thông tin ở trên với tất cả các picture thì được như dưới.
Về pict_type thì thấy được cấu trúc I,P,P …I,P,P … Có 29 ảnh P giữa các ảnh I, không có ảnh B.
→ GOP có dạng I,P,P …P (30 ảnh). Việc không có ảnh B làm cho độ phức tạp khi thiết kế bộ codec giảm đi đáng kể.
→ Theo thông tin trên chỉ số ảnh dùng tham chiếu (refs=1), do đó chỉ là ảnh sau tham chiếu ảnh trước.

pts (Presentation Time Stamp) để chỉ thời điểm mà picture đó được hiện thị, xem giá trị này với tất cả các picture thì ta thấy giá trị này tăng đều.
→ Các ảnh không bị thay đổi thứ tự trong quá trình encode.  Từ thông tin chỉ có ảnh I,P (không có B) cũng dễ đoán được điều này.

● Và nhiều thông tin cực kì chi tiết
Dùng ffmpeg bằng các option của nó (ex: debug …) có thể phân tích thông tin rất chi tiết đến cả macroblock, nhưng nó ngoài phạm vi bài viết.

3.3. Phân tích QP của picture.
QP là của mỗi picture, sinh ra từ quá trình rate control xem ở [5]. Xem QP của mỗi picture cho ta hình dung qua được độ phức tạp của chiến lược rate control trong bộ codec.

Tìm mãi không thấy command cho phép ffmpeg log giá trị trung bình QP của từng picture nên đành dùng JM. JM chỉ làm việc với element stream, tức là raw h.264 chứ không chơi với container, như mts, mp4 … Do đó đầu tiên vẫn phải nhờ ffmpeg bóc cái h.264 element stream ra.

ffmpeg.exe -i sony.mts -c:v copy sony.264

Đưa vào JM, và thực hiện decode bằng command dưới.

ldecod.exe -d decoder -P InputFile=sony.264

qp
Ôi, than ơi, các thánh chơi QP cố định = 30 cho các picture cmnl. Vậy ở level picture thì giá trị QP chả điều chỉnh gì hết, tất nhiên xuống mức macroblock có thể điều chỉnh quanh giá trị này.

4. IphoneSE của Apple thì sao.
– Birate 23.7 Mbps
– High profile, level 4.2.
– Khi encode, chỉ sử dụng 1 picture để tham chiếu.
– GOP: I,P,P ..P (60 ảnh).
– Không có ảnh B, tức là không có sự thay đổi thứ tự để hiển thị.
– Rate control có vẻ tích cực hơn, QP thay đổi từ 20 ~ 31 . I picture là ảnh quan trọng ảnh hưởng nhiều đến chất lượng,  vì thế nên nên được encoder với QP thấp hơn.
– Cơ bản Apple dùng QP thấp hơn, độ mất mát thông tin ít hơn, nhưng vẫn nén được bitrate thấp hơn Sony? ( thật ra phải cùng 1 data đầu vào mới nói được vậy).

5. Kết luận.
A/e được học video coding, bao nhiêu thứ phức tạp như ảnh B tham chiếu 2 chiều cả quá khứ lẫn tương lai, h.264 cho phép 1 picture có thể đa tham chiếu (multi refer) lên đến 16 picture, cấu trúc GOP của h.264 rất tự do cho phép reorder, tham chiếu thoải mái …
Nhưng thực tế phũ phàng là để đạt được realtime (chắc là thế) các bộ encoder trong các thiết bị có vẻ phải hi sinh nhiều thứ để giảm độ phức tạp. Thậm chí cấu trúc GOP trên của h.264 còn đơn giản hơn cấu trúc của MPEG2-Video có ảnh B.
Mặc dù có vẻ codec trong IphoneSE chỉnh chu hơn HDR-CX470, nhưng để so sánh codec là vấn đề rất phức tạp mà cái quan trọng nhất là phải chung 1 dữ liệu đầu vào, trong khi đã đóng gói trong sản phẩm thì điều đó là không thể. Bài viết cũng chỉ là ngó qua một số thông tin từ kết quả video để phán đoán 1 chút về bộ encoder.

Tuy nhiên, một thiết bị quay video tốt, thì bộ encoder không phải là tất cả, ngoài ra còn ống kính, tính năng room, chống rung … tác động rất lớn đến chất lượng video và trải nghiệm.

Tham khảo.
[1]https://www.ffmpeg.org/
[2]https://en.wikipedia.org/wiki/FFmpeg
[3]https://www.itu.int/rec/T-REC-H.264.2-201602-I/en
[4]https://ffmpeg.org/pipermail/ffmpeg-devel/2015-July/176489.html
[5]https://vcostudy.com/2018/04/21/rate-control-video-coding
[6]https://www.ffmpeg.org/documentation.html
[7]https://vcostudy.com/2018/04/14/profile-va-level-trong-ma-hoa-video

Raspberry PI transcodes video

1. Transcode là gì.
Transcode là quá trình chuyển đổi từ định dạng mã hóa hiện tại sang một dạng mã hóa khác, ví dụ như chuyển đổi video, audio coding format …
Thông thường thì việc thực hiện transcode có 2 lý do chính:
– Giảm dung lượng lưu trữ mà có thể đảm bảo được chất lượng video, bằng việc sử dụng video coding format tân tiến hơn, tăng khả năng nén hơn, nên các dữ liệu video đang sử dụng coding format cũ thường chuyển sang format mới để giảm dung lượng lưu trữ.
– Thiết bị đầu cuối không hỗ trợ video or audio hiện tại, do đó cần convert sang cái dạng mà nó hỗ trợ.

Trên thực tế hiện nay thì dễ thấy nhất là khi upload 1 file video lên facebook hoặc youtube, thì file video đó luôn được youtube/facebook xử lý thành định dạng khác trong quá trình upload.

Cơ bản thì quá trình transcode có thể hình dung đơn giản như dưới.

input file/stream → |decode| → |encode| → output file/stream

Từ dữ liệu đầu vào, có thể là multimedia file format (MKV, MP4 …) hoặc stream (udp, tcp/ip …). Bước đầu tiên là giải mã (decode) thành phần đầu vào (video or audio tùy thuộc vào cái nào cần transcode) thành raw data. Tiếp đó thực hiện mã hóa (encode) thành định dạng mới ở dữ liệu đầu ra.

Transcode có thể realtime or non-realtime tùy thuộc vào từng yêu cầu của hệ thống. Trong các dữ liệu cần transcode thì video là phức tạp, tốn nhiều hiện năng nhất, và thường quan tâm nhất , do việc nén video theo chuẩn mới có thể tiết kiệm rất nhiều dung lượng or bitrate.

2. Video decoder/encoder trên RPI.
Raspberry pi(RPI) hỗ trợ cả hardware decoder lẫn encoder, do đó rất phù hợp nếu sử dụng nó để thực hiện transcode.
2 bộ decoder và encoder trên RPI được thiết kế theo API của openmax mà cụ thể là openmax-il.
Openmax là chuẩn các api do khronos đưa ra để thiết kế hệ thống xử  lý multimedia, nó gồm có 3 phần tương được với 3 layer, nhìn hình dưới lấy từ trang chủ có nó thì hiểu, khỏi cần giải thích.
media_portability
Cái gì mà được chuẩn hoá thì mục đích cao nhất của nó là giúp giảm thời gian tích hợp, 2 thằng không cần biết nhau trước đó mà chỉ cần cùng tuân theo 1 chuẩn là dễ dàng tích hợp với nhau.
Openmax-il có thể hiểu là lớp trung gian giữa phần cứng (hardware) và application hay còn gọi là firmware, đây cũng là phần API được sử dụng rộng rãi nhất của openmax. Android media framework, ffmpeg, gstreamer đều đã hỗ trợ openmax-il với tư cách là IL client, tức là layer ngay ở trên openmax-il . Do đó nếu bạn rảnh rỗi thiết kế 1 con chip hỗ trợ video decoder tuân thủ theo đúng api của openmax-il thì bạn có thể nhanh chóng tích hợp được vào các hế thống trên :-), và bán con chip đó luôn được để kiếm xèng.

Chi tiết openmax thì vào trang chủ của nó để tham khảo ở đây[3].
Thông tin các openmax-il component trên RPI thì tham khảo chỗ này[5].

3. Thực hiện transcode video trên RPI.
3.1. Thực hành transcode với RPI.
Omxplayer là cli hoạt động rất ổn định khi dùng play video trên RPI. Mình vẫn dùng để mở clip tàu điện Nhật Bản phục vụ ông con thích tàu xe.
Omxplayer sử dụng hardware decoder của RPI, và tất nhiên là thông qua openmax-il, cấu trúc của nó như hình dưới.
dec_rpi

Xuất phát từ ý tưởng dùng RPI để chuyển đổi video streaming mpeg2-video đang có sẵn thành h.264 để giảm bitrate nhằm tiết kiệm băng thông. Dựa trên omxplayer
với một chút sửa đổi nhỏ sau:
– Bỏ audio decoder, vì audio chiếm băng thông không đáng kể, không cần thiết transcode nên audio vào thế nào cho ra thế đó.
– Cắt bỏ phần xử lý hiện thị (render component), vì chỉ cần chuyển đổi sang video format khác, kết quả sẽ streaming hoặc lưu thành file.
– Thêm component encoder, nối vào ngay sau decoder. Để thực hiện encode lại ảnh, kết quả của bộ decoder sau khi giải mã đầu vào.
– Thêm phần xử lý output streamer, module này có nhiệm vụ lưu audio + video sau khi convert thành file hoặc streaming qua udp hoặc tcp/ip hoặc protocol nào đấy.

Sau khi sửa đổi thì cấu trúc nó thay hình đổi dạng như dưới.
transcode_rpi

Source code & readme hướng dẫn sử dụng chứa ở đây

Kết quả chạy thử, đạt được realtime và bitrate giảm 1 nửa.
input http streaming: mpeg2-video,size 720×576, 25fps, bitrate 4mbps.
output udp streaming: h.264, size 720×576, 25fps, bitrate 2mbps.

Khi play stream gốc và stream sau khi đã transcode thì chất lượng theo đánh giá chủ quan là tương đương nhau. Cần chú ý rằng, đây chỉ là về mặt visual, còn nén video là nén mất dữ liệu (lossly) do đó cái output không bao giờ bằng được input nếu nói trên quan điểm thông tin.

●Hiện tại đang có các điểm hạn chế cần điều tra và cải thiện:
– Chạy được tầm 1h ~ 2h thì tự dưng lăn quay là treo → bug cần điều tra.
– Openmax-il có định nghĩa 2 kiểu kết nối để component truyền data cho nhau, là tunnel (kết nối trực tiếp) và non-tunnel (kết nối gián tiếp thông qua IL client). Định dùng tunnel cho đơn giản, nhưng thử mãi không được nên đang dùng non-tunnel, thành ra quản lý buffer đang hơi stupid nên output buffer của decoder được copy sang encoder. → Cần điều tra lại đển dùng tunnel hoặc improve việc quản lý buffer để bỏ xử lý copy.
–  Phần output streamer module cần design là 1 thread độc lập, nhận video/audio buffer ở component trước đó thông qua queue, nhưng hiện tại đang thực hiện như bằng context của video & audio (được buffer nào thì streaming luôn).

3.2. Dùng ffmpeg.
Đợt này có chút thời gian, định quay lại xử lý mấy hạn chế & bug của 3.1 thì mới để ý thấy ffmpeg nó cũng support openmax rồi, như vậy đơn giản thế này thôi.
– Clone ffmpeg và tham khảo chỗ này để enable omx khi compile.
– Nếu ngại complile từ source code thì có thể cài đặt ffmpeg thông qua repo của RPI, package đã được enable omx: sudo apt-get install ffmpeg

Dùng command dưới là xong :-).

ffmpeg -c:v mpeg2_mmal -i http://address_input -c:a copy -c:v h264_omx -b:v 2000k -f mpegts udp://address:port

Tuy nhiên có vẻ có vấn đề với input streaming interlace, ngoài ra chưa stress test nên chưa biết thế nào. Từ POC (proof-of-concept) đến Mass Product là một khoảng cách lớn, haizz …

●24/07/2018 update: Input stream đang sử dụng là interlace, trong khi đó bộ encoder h.264 trên RPI không support interlace nên aspect ratio output stream bị sai. Có thể cần xử lý de-interlace trước khi encode. Hoặc với video hiện tại thì có thể dùng encoder bằng software encoder (libx264) như command dưới, sử dụng option encode performance gần cao nhất, với video size 720×576 thì vẫn đạt được realtime, tuy nhiên con chip nóng quá, không dám chạy stress-test vì sợ nó ngỏm.

ffmpeg -c:v mpeg2_mmal -i http://address_input
-c:v libx264 -preset superfast -f mpegts -b:v 2000k -s 720×576
udp://address:port

P/S: Lưu ý, RPI mặc định chưa enable hardware mpeg2-video codec, muốn dùng phải bỏ tiền ra mua ở đây.

Tham khảo.
[1]https://en.wikipedia.org/wiki/Transcoding
[2]https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=72260
[3]https://www.khronos.org/openmax/
[4]https://source.android.com/devices/media/
[5]http://www.jvcref.com/files/PI/documentation/ilcomponents/
[6]https://github.com/popcornmix/omxplayer
[7]https://github.com/truongpt/rpi_transcode

Lần đầu học online

Hôm nay viết mail xin nghỉ việc ở FPT Japan, chuẩn bị kết thúc sau 9 năm gắn bó.
Vậy đã 9 năm, kinh nghiệm chỉ gói gọn trong vài ba chữ, embedded, c/c++, video coding, linux driver. 9 năm làm có từng đó, nói ra lại tưởng là cỡ chuyên gia nhưng hóa ra không phải, cái nào cũng lõm bõm.

Tốt nghiệp kĩ sư điện, nên cơ bản kinh nghiệm coding đều học hỏi trong quá trình làm việc, thành ra nhận được kiến thức kiểu làm nhiều thì quen tay hơn là dùng não, nhìn đến cái nào cũng thấy cái nền móng thiêu thiếu cái gì đấy.

Ờ, thôi biết vậy, thiếu nhưng biết bù thì không sao, khóa học online giờ rất rất nhiều, cứ muốn là học được, chỉ là có chịu học hay không mới là vấn đề thôi.

Thử bắt đầu bằng khóa này xem, thấy kha khá các expert gợi ý.
https://see.stanford.edu/Course/CS107/
Song song đọc quyển “programming perspective“, là giáo trình của course trên.

Có 27 bài giảng, nếu cứ đều đều 1 ngày 1 bài hết 27 ngày.
Có tài liệu & bài tập … thong thả tính 2 ngày để làm.
Vậy có 3 ngày/1 bài → 3 tháng.
Nếu bắt đầu học từ giờ 21/06 thì đến 21/08 là xong.
Thử bắt đầu thật nghiêm túc 1 khóa học online xem thế nào vậy!

Từ khóa register trong C

Trong lập trình nhúng (embedded programming), thi thoảng hay bắt gặp từ khóa register, thường là những chỗ tính toán loằng ngoằng, bit biếc dịch ngược dịch xuôi. Register được dùng đặt trước kiểu dữ liệu khi khai báo biến. Tác dụng của từ khóa register, nói một cách ngắn gọn là làm tăng hiệu năng(performance) của chương trình.

Thêm cái của nợ này vào thì tại sao có thể tăng được hiệu năng?. Mà thực sự tăng thật thì tăng được bao nhiêu?
Để thêm phần sinh động, thử một ví dụ nhỏ dưới xem nó có ra cái gì không?

void main()
{
clock_t start, end;
double t;
int i; // không dùng register
//register int i; // sử dụng register
start = clock();
while(i < 0xFFFFFFFF) i++;
end = clock();

t = ((double) (end – start)) / CLOCKS_PER_SEC;
printf("time used %f\n",t);
}

Biên dịch với gcc version 6.3.0
Chạy trên Raspberry Pi 2B (ARMv7 Processor rev 5 (v7l)).
– Sử dụng từ khóa register: time used 9.583080 giây
– Không sử dụng từ khóa register: time used 38.256520 giây

Ví dụ trên cho thấy dùng từ khóa register, thì hiệu năng nó tăng thật, riêng trong trường hợp này thì cũng đang kể đấy chứ nhỉ :-).Tất nhiên cải thiện được bao nhiêu thì phải tùy vào hoàn cảnh cụ thể khi sử dụng, mức độ sử dụng biến đó … Nhưng mà ngon lành vậy thì toàn bộ khai báo biến trong chương trình cứ mặc định thêm luôn từ khóa này thì chuyện gì xẩy ra?.
Để hiểu được điều đó thì trước hết thử xem tại sao hiệu năng nó tăng?
Quay lại với vấn đề cơ bản hơn một chút, trong kiến trúc của vi xử lý thì ALU (Arithmetic Logic Unit) là con trâu đóng vai trò xử lý các tính toán số học. Dữ liệu đưa vào làm việc với ALU phải chứa trong một vùng đặc biệt, gọi là các thanh ghi(register), và ALU chỉ làm việc với đống thanh ghi đó. Trong khi đó các biến khai báo trong chương trình thì đặt ở bộ nhớ ngoài (RAM chẳng hạn …). Do đó với khai báo biến thông thường, để thực hiện một phép tính thì cần có 3 bước.
① Nạp giá trị từ vùng nhớ chứa biến vào register
➁ Yêu cầu ALU xử lý register vừa được nạp giá trị.
③ Đưa kết quả vừa xử lý của ALU ra ngoài vùng nhớ chứa biến.
Hình dung thô thiển như hình vẽ dưới đây.

alu

Khi thêm từ khóa register để khai báo biến, thì tức là ta đã yêu cầu trình biên dịch ưu tiên đặc biệt dành luôn vùng register để chứa biến đó. Và hiển nhiên khi thực hiện tính toán trên biến đó thì giảm được bước ①&③, giảm bớt thủ tục thì hiệu năng nó tăng lên là chuyện dễ hiểu :-).
Quay lại với ví dụ trên, để đảm bảo đúng như thánh phán, thử thêm option “-save-temps” để lấy tập mã lệnh assembly xem nó có thật vậy không.

gcc register.c -o test -save-temps

Đoạn mã assembly ở dưới tương ứng với vòng while loop ở ví dụ trên. Dễ thấy chỗ bôi màu đó khi không dùng từ khóa register tương ứng với bước ①&③ ở trên. Còn khi dùng từ khóa register thì trình biên dịch nó dùng luôn thanh ghi r4 cho việc chứa biến i.
p/s: Nếu không dễ thấy thì tự tra cứu lại lệnh assembly của ARM

Không sử dụng từ khóa register

ldr r3, [fp, #-8] // bước ①
add r3, r3, #1
str r3, [fp, #-8] // bước ③
.L2:
ldr r3, [fp, #-8]
cmn r3, #1
bne .L3

Sử dụng từ khóa register

.L3:
add r4, r4, #1
.L2:
cmn r4, #1
bne .L3

Đến đây, ta thấy rõ ràng khi dùng từ khóa register, thì thay vì dùng bộ nhớ ngoài đển lưu biến thì chương trình sẽ sử dụng luôn register để lưu biến đó. Cái gì ngon, quí thì dĩ nhiên hiếm, register cũng thấy, số lượng register rất nhỏ so với bộ nhớ ngoài, mà đây còn là tài nguyên dùng chung. Do đó không thể chơi kiểu vô tổ chức để thằng nào thích lấy làm đồ riêng thì lấy được. Tùy từng tình huống, yêu cầu để lựa chọn phần xử lý nào nên sử dụng register để tăng hiệu năng mà có thể chấp nhận cho nó xin một vài register về làm của riêng.
Nếu không tin thì thử thêm đoạn code này “register int k[100*1024*1024];” vào
để chiếm register xem chương trình xem nó có ngỏm không ^.^

Zero start with video coding

Hoàn cảnh éo le.
Ra trường giữa 2009, tham gia 1 dự án làm về DTV (Digital Television) đến tận đầu 2012, vậy là 2 năm rưỡi, biết được một chút về video decoding do đàn anh  chỉ dạy cho lúc bắt đầu dự án, còn lại OT vật vã cho kịp deadline. Sau này ngẫm lại thấy đa phần kiến thức cơ bản về video coding bị sai → quá nguy hiểm.
Vào 7/2013 sang Nhật làm việc đến 4/2017 trong dòng dự án về 4k – Television và Camera recorder. Trong thời gian đó có tầm 1 năm rưỡi cuối làm về video encoding. Về công việc thì test nhiều hơn là code. Do  rảnh rỗi không biết làm gì, nên học thêm chút về video encoding cho qua ngày.
Rút ra được vài ba kinh nghiệm quí báu, khi làm việc trong cái mảng việc này (mảng khác chắc cũng thế).
– ① Nên theo từ lúc đang đi học, như thế sẽ được chỉ dạy kiến thức cơ bản bởi những người biết về kiến thức cơ bản.
– ➁ Nếu ko có cơ hội ①, khi vào dự án chiến luôn mà nghe trong team chém gió (sợ nhất là các thánh phán), phải điều tra cẩn thận trước khi coi đó là chân lý.
– ③ Cái số ➁ cực kì nguy hiểm, nếu không may mắn có cái ① thì nên tìm sư huynh cho thật ngon mà học, vừa nhanh vừa an toàn.
Mình là nạn nhân con số ➁, sau nhiều năm mới sửa sai được một số vấn đề cơ bản, mà suýt bị nó làm cho tẩu hỏa nhập ma. Ngoài ra công việc gián đoạn không làm video coding nữa, nên giờ ngồi viết lại, lỡ sau này may mắn có cơ hội làm tiếp về video coding thì có chỗ mà tra cứu cho nhanh :-).

Một vài điểm bắt đầu.
Có mail hỏi 1 ông anh, chuyên gia video coding ở Đức, thì đại ka gợi ý đọc quyển này. Mình nhai mãi chưa xong (vẫn tiếp tục nhai khi có thời gian), toàn kiến thức nền tảng, phù hợp cho ai thích đi theo hướng nghiên cứu. Còn giờ đang cần tiếp cận theo hướng ứng dụng.
Bắt đầu một vài gạch đầu dòng cơ bản.
– Video thực ra là một chuỗi ảnh(picture) sắp xếp liên tục theo thời gian (có lẽ ai cũng biết?), vì vậy nén video chẳng qua là nén lần lượt các ảnh này.
– Nén video cơ bản là nén mất dữ liệu (lossly), nhưng bất kì chuẩn nén video nào cũng chứa 2 giai đoạn. Giai đoạn 1 nén lossly (transform + quantization) và giai đoạn 2 nén lossless (entropy coding). Một số chuẩn nén như AVC/H.264, HEVC/H.265… cho phép chỉ nén lossless bằng cách cho phép bỏ quá trình nén lossly.
– Xét ở đặc điểm sau khi nén của picture, có thể chia thành 2 loại kỹ thuật nén là INTRA (nén trong ảnh) & INTER (nén liên ảnh). INTRA là cách nén picture chỉ dựa vào thông tin chứa trong bản thân picture đó, ngược lại INTER có sử dụng thông tin các picture lân cận để thực hiện nén picture đó.
– Quá trình nén picture được thực hiện theo từng block có kích thước vuông giống nhau. Đối với chuẩn nén H.264 trở về trước gọi là Macroblock kích thước 16×16 pixel, còn đối với H.265 gọi là Coding Tree Unit và nó cho phép chọn từ 16×16 ~ 64×64 pixel. Với các chuẩn nén không phải của ISO or ITU-T, ví dụ VP8, VP9 của google, tên gọi các block đó có thể khác nhưng cơ bản đều mang ý nghĩa giống nhau.

Sẽ có rất nhiều thắc mắc, ví dụ INTRA & INTER được thực thi cụ thể thế nào? nó thể hiện thế nào trong quá trình xử lý các block trong từng picture?, google phát toàn ra 1 đống như motion vector nó nằm ở đâu trong khái niệm trên? vân vân và vân vân. Với một cơ số khái niệm, cùng với đống tài liệu trên mạng (rác cũng nhiều), vội vàng dễ bị chết đuối cho nên đừng vội tham lam, đi từ từ rồi sẽ đến.
P/S: Kiến thức mình chia sẻ thuộc trường hợp số ➁, cẩn thận khi có ý định sử dụng nhé!

Code refactoring

Từ 1 comment status của thằng bạn trên facebook, nhớ lại về nguyên lý thứ hai của nhiệt động lực học. À, mà sao lại nguyên lý thứ hai nhỉ, có nguyên lý thứ nhất không nhỉ?

Học từ hồi cấp 3, bắt đầu từ câu chuyện không thể chế tạo động cơ vĩnh cửu loại 2, tức là động cơ dùng 2 nguồn nhiệt để có thể biến nhiệt hoàn toàn thành công. Tuy không vi phạm định luật bảo toàn năng lượng, nhưng vẫn không làm được.

Hiệu suất lý tưởng để chuyển nhiệt biến thành công chỉ đạt được = (T2- T1)/T2; T1,T2 là nhiệt độ nguồn nóng & lạnh tính theo nhiệt giai Kelvin.
Tại sao lại thế? qui luật gì đã chi phối cái đó. Trong quá trình giải quyết cái đó thì ông cụ Rudolf Clausius đã mang đến một khái niệm gọi là Entropy (kí hiệu là S), bản chất là đặc trưng cho tính hỗn loạn của hệ thống. Chốt lại là qui luật trên sẽ tương đương với việc phát biểu: Entropy của hệ cô lập không thể giảm. Tức là tính hỗn loạn của hệ thống cô lập sẽ tăng hoặc ít nhất là giữ nguyên.
Câu chuyện tưởng đến đây là hết, thì hóa ra mới bắt đầu. Trong lý thuyết thông tin, Shannon định nghĩa lượng thông tin của hệ thống thông qua Entropy, xem ở đây. Hệ thống càng hỗn loạn thì càng chứa nhiều thông tin, hệ thống càng trật tự thì lượng thông tin chứa trong đó càng ít, nghe cũng có lý đấy chứ nhỉ!. Vậy là từ cái máy nhiệt lại liên hệ mật thiết với chủ đề nóng, vấn đề thông tin.

Cái nghề làm phần mềm chắc chắn cũng không ngoài cuộc, vì nguyên lý phổ quát mà, bao trùm từ vũ trụ bao la bát ngát ( xem Lược sử thời gian – Stephen Hawking, thì biết) đến trà đá vỉa hè (cục đá đang tan dần trong cốc trà, chính là biểu hiện của việc Entropy đang tăng). Vậy phần mềm có xu hướng càng ngày càng lộn xộn, và thực tế đúng như thế. Lúc bắt đầu dự án, nếu là dự án tử tế thì basic design, detail design chuẩn mực thật. Nhưng theo tháng năm, ta thêm tí mắm, lâu lâu cho chút muối vào, thi thoảng thêm tí tính năng để phần mềm lúc đầu đặc tính giống Chó nhưng lại muốn bắt đc cả Chuột, và dần dần nó trở thành 1 đống lộn xộn. Và đâu đó cũng có Sofware entropy thật.
Nhưng chú ý, điều đó chỉ đúng với hệ cô lập, và để phần mềm không trở nên thành 1 đống hổ lốn theo tháng năm thì phải tìm cánh phá vỡ tính “cô lập” của nó, một trong những cách đó là REFACTORING, quyển sách cần đọc, nó nằm ở đây.  Mặc dù  không khuyến khích, nhưng trước khi mua sách tử tế thì có thể xem tạm ở đây

Tóm lại từ nguyên lý thứ hai của nhiệt động lực học, ta rút ra rằng để software không bị xuống cấp, ta phải học cách refactoring?

Zero start with USB

1. Lược sử USB.
Viết tắt của chữ Universal Serial Bus, cái tên nói lên tất cả. Và không hổ danh với tên gọi, đây là chuẩn kết nổi phổ biến nhất, đã và đang thay thế dần các kết nối ngoại vi khác.
Đồ sộ về cả đặc tả lẫn mã nguồn (ví dụ USB stack trong Linux), nếu không phải thánh thì nên tiếp cận từ từ, tránh “đột-thức” (đột quị vì nhiều kiến thức cần nắm).

Lịch sử tóm gọn, có vẻ khi ứng dụng cách mạng nhất là USB Mass Storage(usb flash, thường gọi tắt là USB) bị lấn lướt bởi việc phát triển mạnh mẽ của điện toán đám mây(cloud) và các dịch vụ trực tuyến, thì USB 3.0 lại vùng lên, tuy chưa rõ có nên cơm cháo gì không.
– Khai sinh 1/1996: USB 1.0, Low Speed 1.5Mbit/s, Full Speed 12Mbit/s
– 04/2000: USB 2.0, High Speed 480Mbit/s
– 11/2008: USB 3.0, SuperSpeed 5Gbit/s
– 07/2013: USB 3.1, SuperSpeed+ 10Gbit/s
– 09/2017: USB 3.2, SuperSpeed+ 20Gbit/s

2. Lướt qua đặc tả USB.
Đầu tiên cần nắm đặc tả, vào [1] để lấy đặc tả. Đặt tả là một ma trận chữ nghĩa và khái niệm, nên đọc [3] để biết tiếp cận đặc tả, nếu lười đọc tiếng Anh thì đọc nội dung ở [4], của 1 blog rất có tâm. Ở đây chỉ giới thiệu các tiếp cập đặc tả phiên bản USB 2.0[2], tuy nhiên các chắc các phiên bản tiếp theo cũng có thể tiếp cận theo cách tương tự.
Đồ sộ quá nên cần nắm vững những câu thần chú cơ bản. Đây là chân lý, không bao giờ được buông. NHƯNG nó chỉ đảm bảo cho các phiên bản trước USB 2.0, về sau có thể có thay đổi, sử dụng cần tỉnh táo.
●USB là chuẩn giao tiếp đơn, chỉ có HOST(chủ) và DEVICE(thiết bị, client-khách) giao tiếp với nhau, không phải là giao thức mạng.
●DEVICE chỉ nghe và trả lời khi có lệnh từ HOST, kể cả trường hợp giao tiếp theo interrupt.

Về mặt đặc tính truyền dữ liệu, thì có 4 loại cơ bản:
– Control Transfers: Sử dụng để truyền command (lệnh) hoặc status (trạng thái), ở quá trình DEVICE bắt đầu kết nối với HOST.
– Bulk Data Transfers: Dùng kết nối truyền lượng dữ liệu lớn, không cho phép mất mát, ex: usb flash.
– Interrupt Data Transfers: Dùng cho kết nối đảm bảo tính phản ứng nhanh, ví dụ chuột, bàn phím…
– Isochronous Data Transfers: Dùng cho các kết nối có tính realtime, tốc độ ổn định, ex: Camera, Audio …

Song song với sự phát triển của các thiết bị nhúng (embedded, lai tạp) thì sinh ra USB OTG (On-The-GO), cho phép nó có thể đóng vai trò HOST hoặc DEVICE. Việc lựa chọn vai trò quyết định trong quá trình đàm phán lúc kết nối.

3.Tài liệu tham khảo.
[1]http://www.usb.org/
[2]http://www.usb.org/developers/docs/usb20_docs/
[3]https://www.beyondlogic.org/usbnutshell/usb3.shtml#USBProtocols
[4]https://lazytrick.wordpress.com/category/usb/