BP-Stream-AOE: SRS, Flask và pipeline video FFmpeg

Kiến trúc BP-Stream-AOE: React/Vite, SRS, Flask, MySQL và FFmpeg; pipeline FLV sang HLS, job queue, named lock, recovery và YouTube sync.
BP-Stream-AOE có hai luồng dữ liệu khác nhau: xem livestream và xử lý bản ghi sau phát sóng. FE React/Vite trình bày player, thư viện replay và quản trị; SRS xử lý media ingest; backend Python/Flask điều phối dữ liệu và tác vụ FFmpeg. Phân tích kiến trúc cần tách control plane khỏi media plane, vì request quản trị, hàng SQL và những tệp video lớn có đặc tính tài nguyên, trạng thái lỗi và cách phục hồi rất khác nhau.
Media plane và control plane
| Thành phần | Luồng chính | Vai trò |
|---|---|---|
| SRS | Luồng media | Nhận nguồn RTMP, cung cấp media/recording theo cấu hình |
| React / Vite | Trải nghiệm xem | Player, tám vị trí stream, chọn ngày replay và quản trị |
| Flask API | Điều khiển | Nhận yêu cầu, truy vấn trạng thái và điều phối thao tác |
| Python workers | Xử lý nền | Ghép bản ghi, cập nhật tiến độ và đồng bộ video |
| FFmpeg | Chuyển đổi media | Chuẩn bị segment, concat và tạo đầu ra HLS |
| MySQL | Trạng thái nghiệp vụ | Sessions, admin jobs, settings và audit |
| Filesystem / metadata | Nội dung video | Bản ghi nguồn, tệp tạm, manifest và segment replay |
Nguồn phát → SRS → live media → trình phát React
→ recordings / FLV
Quản trị → Flask → admin_jobs (MySQL) → worker
→ FFmpeg → HLS playlist + TS segments → replay player
→ YouTube sync khi đã cấu hình quyềnPlayer mở được một URL HLS không có nghĩa tác vụ ghép đã hoàn tất hoặc metadata chính xác. Ngược lại, một job completed trong SQL cũng cần được đối chiếu với file đầu ra. Thiết kế phải theo dõi cả trạng thái điều khiển lẫn tài nguyên media mà trạng thái đó tham chiếu.
Cấu trúc React FE và Python backend
web/src/
App.jsx # routes và layout
components/LiveView.jsx
components/PlaybackView.jsx
components/VideoPlayer...
components/AdminView...
worker/
database.py
services/video_service.py # pipeline xử lý recording
services/admin_jobs.py # queue, progress, recovery
services/admin_store.py # SQL schema và truy vấn
services/admin_storage.py # kiểm tra media / dung lượng
services/youtube_service.py # tích hợp nền tảng videoReact Router giữ trải nghiệm chuyển giữa live, replay và quản trị trong FE. Backend dùng Flask cùng các service Python, không phải Express chỉ vì một phần README/giao diện cũ từng ghi tên đó. Dữ liệu được truy cập bằng mysql-connector-python và SQL trực tiếp; dự án không có ORM TypeORM hay Lucid ở lớp backend này.
Docker và reverse proxy tổ chức cách các dịch vụ được đưa ra ngoài, nhưng không biến mọi thao tác thành stateless. Worker vẫn phụ thuộc recording, tệp tạm và metadata trên storage. Khi nhân bản container xử lý, phải xem lại khóa, volume chia sẻ và cách phát hiện job đang chạy, thay vì chỉ tăng số replica.
Pipeline recording: giữ thứ tự, đổi segment và tạo HLS
Video service gom bản ghi hợp lệ theo ngày/stream, chuẩn bị danh sách segment và dùng ThreadPoolExecutor cho phần chuyển đổi. Công việc có thể kết thúc không theo thứ tự do chạy song song, nhưng kết quả được đặt lại vào vị trí theo index trước khi tạo danh sách concat. Nếu chỉ append theo as_completed, video đầu ra có thể sai trình tự thời gian dù mọi lệnh FFmpeg đều chạy thành công.
Source kiểm tra trường hợp một segment không convert được và báo lỗi thay vì âm thầm ghép phần còn lại. Sau đó service ghi concat.txt với các đường dẫn TS theo thứ tự đã xác định, rồi gọi FFmpeg để tạo playlist cùng segment HLS. Tham số -c copy ở bước này là stream copy; nó không bảo đảm có thể nối mọi file codec/stream layout không tương thích.
# Rút gọn phần tạo HLS trong source; đường dẫn là ví dụ
ffmpeg -f concat -safe 0 -i concat.txt \
-c copy \
-hls_time 5 -hls_list_size 0 \
-hls_flags independent_segments \
-hls_segment_filename replay/segment_%d.ts \
replay/index.m3u8HLS gồm playlist và các segment mà playlist tham chiếu. Cần kiểm tra cả hai, thời lượng, audio/video stream và khả năng seek trước khi xem replay là sẵn sàng. Hls_time là mục tiêu phân đoạn; biên thực tế chịu ảnh hưởng cấu trúc stream và keyframe. Bước cleanup tệp tạm phải nằm sau xác nhận đầu ra phù hợp; giữ nguồn khi lỗi giúp điều tra và thử lại mà không cần lấy lại buổi phát đã mất.
MySQL job queue, named lock và trạng thái tiến trình
Admin store định nghĩa admin_jobs với id, kind, payload, status, progress, message, error_code và timestamps, cùng index trên status/created_at. Job loại merge hoặc youtube được tiếp nhận vào trạng thái queued. Hệ thống kiểm tra target, dung lượng và công việc đang hoạt động trước khi gửi yêu cầu xử lý; UI theo dõi job thay vì giữ một HTTP request dài chờ FFmpeg.
Admin jobs dùng MySQL GET_LOCK cho khóa đặt tên khi điều phối tác vụ video, kết hợp kiểm tra hàng đợi hoạt động. Đây là named lock gắn với connection, khác row lock trong một transaction InnoDB. Helper gate/release phải dùng đúng connection và đảm bảo release trong nhánh kết thúc; database có một hàng job không tự ngăn hai worker chạy trên cùng recording.
submit → validate target + space
→ acquire named lock → kiểm tra active job
→ INSERT admin_jobs(status=queued) → release lock
worker → running + heartbeat
→ completed / failed
khởi động lại → job running cũ được đánh dấu interruptedWorker chạy bằng thread nền trong service, có wake event và heartbeat cập nhật timestamp. Đây là queue dựa trên database và vòng xử lý của ứng dụng; source không cần RabbitMQ để mô tả luồng hiện có. Nếu mở rộng sang nhiều tiến trình, phải kiểm tra đầy đủ cơ chế claim và khóa chứ không giả định một queue SQL luôn hỗ trợ cạnh tranh an toàn.
Recovery, dung lượng và consistency giữa SQL, JSON, filesystem
Khi khởi động lại, recover đánh dấu các admin job còn running thành interrupted với mã worker_restarted, đồng thời rà metadata đang processing để giữ lại dấu vết. Việc đánh dấu gián đoạn trung thực hơn tự chuyển mọi job sang completed hoặc lập tức chạy lại mà không kiểm tra tệp đầu ra. Một job bị ngắt có thể đã ghi được một phần manifest, tệp tạm hoặc video đích.
Source dùng metadata file và lock trong quá trình cập nhật tiến độ. Lock trong bộ nhớ chỉ điều phối thread của cùng tiến trình; nó khác khóa database giữa nhiều tiến trình. SQL transaction cũng không bao được việc FFmpeg đang tạo file. Bởi vậy recovery cần đối chiếu admin_jobs, metadata và filesystem để quyết định dọn tệp tạm, giữ nguồn hay chạy lại một bước cụ thể.
Kiểm tra dung lượng trước job là cần thiết vì một pipeline có thể đồng thời giữ FLV gốc, TS trung gian và đầu ra. Dung lượng source không phải dung lượng đỉnh của tác vụ. Giới hạn số worker cho segment/stream giúp kiểm soát CPU, I/O và file descriptor, nhưng giá trị phù hợp cần được đo theo máy và codec; không có cơ sở đưa ra một mức concurrency tối ưu chung từ tên ThreadPoolExecutor.
YouTube sync và các trạng thái xác nhận khác nhau
Luồng YouTube phụ thuộc cấu hình OAuth, quyền tài khoản và quota bên ngoài. Admin jobs lưu error_code để phân biệt lỗi token/quota và lỗi xử lý video. Một file đã có trong replay không đồng nghĩa đã đồng bộ lên YouTube; một yêu cầu upload đã được tạo cũng chưa chứng minh video đã sẵn sàng công khai trên nền tảng đích.
Khi retry, cần biết công việc đã dừng trước khi tạo upload hay sau khi phía nền tảng đã nhận tài nguyên. Nếu không có định danh đối chiếu, retry toàn bộ có thể tạo bản trùng. Hệ thống phải xem trạng thái local và kết quả bên ngoài cùng nhau. Nội dung bài không công bố OAuth token, stream key hay URL nội bộ để minh họa luồng này.
Các màn hình live và replay bên dưới được render từ source thật trong phiên không có stream/bản ghi. Chúng thể hiện cấu trúc player và thư viện, không phải phép đo tám luồng đồng thời, độ trễ dưới một ngưỡng hay chất lượng video đã được kiểm thử.
Các tiêu chí kiểm thử pipeline thay vì chỉ nhìn giao diện
- Chạy song song các segment kết thúc lệch thứ tự nhưng concat vẫn theo trình tự nguồn.
- Một segment hỏng phải làm job báo lỗi phù hợp và giữ đủ nguồn để thử lại.
- Ngắt worker giữa bước ghi HLS và cập nhật SQL để kiểm tra recovery interrupted.
- Hai yêu cầu merge cùng target phải tuân theo chính sách khóa, không ghi chồng đầu ra.
- Đối chiếu playlist, các segment, thời lượng, audio và seek sau khi job báo completed.
- Giả lập lỗi quota/token YouTube mà không làm mất replay local đã hoàn tất.
Những phép thử này đánh giá invariant ở đúng ranh giới video, storage, database và nền tảng ngoài. Không có số liệu benchmark hoặc kết quả thử nghiệm giả định trong bài; phần mô tả triển khai dựa trên source Python, React và cấu hình service đã đối chiếu.
Giao diện ứng dụng và dữ liệu minh họa
Các ảnh dưới đây được chụp trực tiếp từ FE hoặc trang quản trị của ứng dụng. Với các màn hình chạy trong phiên preview, dữ liệu mẫu được tách khỏi hệ thống vận hành; bố cục và thành phần giao diện do chính source của dự án render.
Bố cục theo dõi tám luồng

Thư viện replay theo ngày

Tài liệu kỹ thuật đối chiếu
Xem thêm các dự án khác hoặc trao đổi về kiến trúc và phạm vi triển khai.