Visits heute: 177
Aktuelle Seite: Segmentation Fault: Der Algorithmus der Gier - Part 2
Das Programm nimmt Gestalt an! Elara steht nun kurz vor dem Ziel, aber wie sie bereits vermutet hat, schleichen sich bei echten DVDs oft tückische Fehler ein.
Hier ist das Update für das Programm. Wir integrieren nun die Validierung der Spielzeit, um "Geister-Titel" auszufiltern, und sorgen dafür, dass die index.html korrekt generiert wird.
Kapitel 20: Die Spreu vom Weizen trennen (Sanity Checks)
Die Story: Elara lässt das Programm über eine DVD laufen, die angeblich 99 Titel hat. Nach einer Stunde stellt sie fest, dass 90 davon nur 10 Sekunden lang sind – ein klassischer Kopierschutz-Trick, um Ripper in die Irre zu führen. Sie erkennt: Ihr Programm braucht Urteilsvermögen. Sie schreibt eine Funktion, die die Metadaten vor dem Schreiben in das HTML-Menü prüft. Nur was länger als 2 Minuten ist, gilt als echter Inhalt.
Das vollständige, bereinigte Programm
Ich habe die HTML-Generierung in die main integriert und einen Check eingebaut, der verhindert, dass leere oder ultrakurze Titel deine Galerie verstopfen.
// Compile: gcc dvd2web_final.c -lpthread -o dvd2web_final
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#define MAX_THREADS 1
#define MIN_DURATION 120.0 // Titel unter 2 Minuten ignorieren
typedef struct {
double duration;
int width;
int height;
} VideoMeta;
typedef struct {
int next_title;
int total_titles;
char device[256];
pthread_mutex_t lock;
} TaskQueue;
typedef struct {
int id;
TaskQueue* queue;
} WorkerData;
VideoMeta get_metadata(const char* filename) {
VideoMeta meta = {0, 0, 0};
char cmd[512], buffer[128];
sprintf(cmd, "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 %s", filename);
FILE* fp = popen(cmd, "r");
if (fp && fgets(buffer, sizeof(buffer), fp)) {
meta.duration = atof(buffer);
pclose(fp);
}
sprintf(cmd, "ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of default=noprint_wrappers=1:nokey=1 %s", filename);
fp = popen(cmd, "r");
if (fp && fgets(buffer, sizeof(buffer), fp)) {
sscanf(buffer, "%d", &meta.width);
if (fgets(buffer, sizeof(buffer), fp)) sscanf(buffer, "%d", &meta.height);
pclose(fp);
}
return meta;
}
void* worker_func(void* arg) {
WorkerData* w_data = (WorkerData*)arg;
TaskQueue* q = w_data->queue;
while (1) {
int title = -1;
pthread_mutex_lock(&q->lock);
if (q->next_title <= q->total_titles) {
title = q->next_title++;
}
pthread_mutex_unlock(&q->lock);
if (title == -1) break;
char cmd[2048], mp4_name[256];
sprintf(mp4_name, "title_%02d.mp4", title);
printf("[Worker %d] Extrahiere & Konvertiere Titel %d...\n", w_data->id, title);
// Kombinierter Befehl: vobcopy -> ffmpeg -> rm
// Wir nutzen eine Subshell, um sicherzustellen, dass die VOBs pro Titel isoliert sind
sprintf(cmd, "mkdir -p temp_%d && "
"vobcopy -n %d -i %s -o ./temp_%d/ 2>/dev/null && "
"ffmpeg -i ./temp_%d/*.vob -c:v libx264 -crf 22 -preset fast "
"-map 0:v:0 -map 0:a? -c:a aac -movflags +faststart %s -y -loglevel error && "
"rm -rf ./temp_%d/",
title, title, q->device, title, title, mp4_name, title);
system(cmd);
}
free(w_data);
return NULL;
}
int main(int argc, char** argv) {
if (argc < 3) {
printf("Usage: %s <device> <num_titles>\n", argv[0]);
return 1;
}
TaskQueue q;
q.next_title = 1;
q.total_titles = atoi(argv[2]);
strncpy(q.device, argv[1], 255);
pthread_mutex_init(&q.lock, NULL);
// Threads starten
pthread_t workers[MAX_THREADS];
for (int i = 0; i < MAX_THREADS; i++) {
WorkerData* d = malloc(sizeof(WorkerData));
d->id = i; d->queue = &q;
pthread_create(&workers[i], NULL, worker_func, d);
}
for (int i = 0; i < MAX_THREADS; i++) pthread_join(workers[i], NULL);
// HTML Generierung am Ende
FILE* html = fopen("index.html", "w");
fprintf(html, "<html><head><style>body{background:#1a1a1a;color:#eee;font-family:sans-serif;} .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:20px;padding:20px;} .item{background:#333;padding:15px;border-radius:8px;} video{width:100%%;}</style></head><body>");
fprintf(html, "<h1>DVD Library</h1><div class='grid'>");
for (int i = 1; i <= q.total_titles; i++) {
char path[64];
sprintf(path, "title_%02d.mp4", i);
if (access(path, F_OK) == 0) { // Prüfen ob Datei existiert
VideoMeta m = get_metadata(path);
if (m.duration >= MIN_DURATION) {
fprintf(html, "<div class='item'><h3>Titel %d</h3><p>%02d:%02d | %dx%d</p>",
i, (int)m.duration/60, (int)m.duration%60, m.width, m.height);
fprintf(html, "<video controls preload='none'><source src='%s' type='video/mp4'></video></div>", path);
} else {
printf("Überspringe Titel %d (zu kurz: %.1fs)\n", i, m.duration);
}
}
}
fprintf(html, "</div></body></html>");
fclose(html);
pthread_mutex_destroy(&q.lock);
printf("Archivierung abgeschlossen.\n");
return 0;
}
Was Elara in diesem Kapitel gelernt hat:
- Isolation der Ressourcen: Durch
mkdir -p temp_%dhat jeder Titel sein eigenes Verzeichnis. Das ist wichtig, falls du späterMAX_THREADSwieder erhöhst – so löscht Thread A nicht versehentlich die VOB-Datei, die Thread B gerade noch liest. - Dateisystem-Interaktion (
access): Bevor sie blind HTML-Code schreibt, prüft sie mitaccess(path, F_OK), ob die Konvertierung überhaupt erfolgreich war. - Filtern von Metadaten: Der
MIN_DURATIONCheck ist die "Intelligenz" des Programms. Er verhindert, dass 2-sekündige Menü-Loops oder Warnhinweise als "Film" in der Galerie landen.
Elaras Finale
Das Programm läuft. Die DVD ist digitalisiert. Aber Elara merkt: Die MP4-Dateien liegen einfach nur im Ordner. Wenn sie diese über das Internet freigeben will, braucht sie einen Server. Thorne hat alle Standard-Ports in ihrem Viertel gesperrt.
Soll Elara im nächsten Kapitel lernen, wie sie einen eigenen Webserver in C schreibt, der auf einem unkonventionellen Port läuft und die Videos per HTTP-Streaming direkt an ihren Browser liefert?