"A human being should be able to change a diaper, plan an invasion, butcher a hog, conn a ship, design a building, write a sonnet, balance accounts, build a wall, set a bone, comfort the dying, take orders, give orders, cooperate, act alone, solve equations, analyze a new problem, pitch manure, program a computer, cook a tasty meal, fight efficiently, die gallantly. Specialization is for insects." (Robert A. Heinlein)

Tuesday 18 August 2009

Recovering a unfinished DVD using Linux

I spent some of this summer days off converting my old VHS-C videos to DVD format. It's usually only matter of playing the old cassettes on a VHS recorder connected to my Panasonic DVD recorder, I only have to change cassette every 45 min. And finalize the DVD every three cassettes, just a long simple boring task.
Unfortunately last time (about a week ago) the DVD recorder refused to finalize two disks (which means four hours and half of work). I don't know if the problem was because of defective DVDs, recorder overheating or simple, plain bad luck. I so started looking in the 'net for a solution. Things started to become interesting when I found not only a solution but a Linux solution! This post on Rip Linton's Blog sounded a little odd at first but it worked and solved my problem.



Following Rip instructions I so installed DVDsaster from the “Add/Remove ...” tool and extracted an ISO image from the faulty DVD.
 
Then I had to split the resulting ISO file. Instead of following Rip directions I preferred to compile and use the handy C program posted in following comments. I simply had to modify some line line in order to get more than 100 split files. Here are the modified lines that appears twice in the code:
...
if(SubFileNumber++ >9999) exit4error("too many output files generated\n");
snprintf(OutputFileName, 64, "%s%.4dj.vob", argv[1], SubFileNumber);
...
I finally got over 200 small files all but three contained a valid video file. All I have to do, now, is re-join them all.

Thanks a lot to Rip Linton and David Savinkoff (the C program author).

Links

2 comments :

  1. /**
    - Copy this C code into a Text Editor
    - Save as: dvchop.c
    - See C comments on how to compile and use this program

    - Use dvdisaster or readcd to perform the unfinalized DVD recovery
    (note that an unfinalized DVD is not mountable)
    - Use dvchop to extract .vob Titles
    - Pick the .vob files you want to keep.

    The purposes of this program are to:
    - produce clean .vob files for editing recorded video.
    - extract contiguous VOB sectors from an .iso copy of an unfinalized DVD.
    - recover DVD video without re-encoding, decoding or muxing.
    (note that the produced output files may be rejoined to reproduce
    the original input file)

    Extra:
    If you want to make One title from several, use the following type of command:
    cat movie_002.vob movie_005.vob | ffmpeg -i /dev/stdin -target dvd -vcodec copy -acodec copy test.vob
    */

    #include <stdio.h>
    #include <stdlib.h>

    /**
    Compile as follows for > 2GB file access
    gcc -Wall -D_FILE_OFFSET_BITS=64 dvchop.c -o dvchop

    License: GNU LGPL, version 3
    Version: dvchop [November 23 2010 r2]
    Copyright 2008, David Savinkoff

    This program reads concatenated .VOB files or an .iso file
    from stdin and makes less than 402 files using the output
    filename provided on the command line.
    When an output file ends with .vob.j, it is not recognized.
    Note that this program will not recognize anything if the
    file input doesn't start on the first byte of a sector.

    usage: cat filename(s) | dvchop output_filename
    usage: cat VTS_01_?.VOB | dvchop dvd
    usage: cat unfinalizedDVD.iso | dvchop movie
    */

    void exit4error(char *string)
    { fputs(string, stderr); exit(1);
    }

    int main(int argc, char *argv[])
    { FILE *fd1;
    void *buff1;
    char OutputFileName[255];
    unsigned char *chk;
    double SCR, prevSCR;
    int SubFileNumber=0;

    if(argc!=2) exit4error("usage: cat filename(s) | dvchop output_filename\n");
    if((buff1=calloc(512,4)) == NULL) exit4error("calloc error\n");

    chk=buff1;
    snprintf(OutputFileName, 64, "%s_%.3d.vob.j", argv[1], SubFileNumber);
    printf("processing: %s\n",OutputFileName);

    if((fd1=fopen(OutputFileName,"wb")) == NULL)
    exit4error("output file error\n");
    prevSCR=2576980377600.0; /* 1.0 greater than largest value */
    for(;;)
    { if(fread(buff1, 4, 512, stdin) != 512)
    exit(0);

    /* MPEG-2 Program Pack == 0x000001BA */
    if(chk[0]==0x00 && chk[1]==0x00 && chk[2]==0x01 && chk[3]==0xBA)
    { SCR=300.0*((chk[8]>>3)+chk[7]*32.0+(((chk[6]&~7)>>1)+(chk[6]&3))*8192.0+
    chk[5]*1048576.0+(((chk[4]&(8+16+32))>>1)+(chk[4]&3))*268435456.0)
    + (((chk[9]>>1)+(((unsigned)(chk[8]&3))<<7)) % 300);

    /* Note: maximum allowed interval between SCRs is 700ms */
    if((SCR <= prevSCR) || (SCR > prevSCR+18900000.0))
    { fclose(fd1);
    if(SubFileNumber++ >400)
    exit4error("too many output files generated\n");
    snprintf(OutputFileName, 64, "%s_%.3d.vob", argv[1], SubFileNumber);
    printf("processing: %s\n",OutputFileName);
    if( (fd1=fopen(OutputFileName,"wb")) == NULL )
    exit4error("output file error\n");
    }
    prevSCR=SCR;
    }else
    {
    if(prevSCR<2576980377600.0)
    { fclose(fd1);
    if(SubFileNumber++ >400)
    exit4error("too many output files generated\n");
    snprintf(OutputFileName, 64, "%s_%.3d.vob.j", argv[1], SubFileNumber);
    printf("processing: %s\n",OutputFileName);
    if( (fd1=fopen(OutputFileName,"wb")) == NULL )
    exit4error("output file error\n");
    }
    prevSCR=2576980377600.0;
    }

    if(fwrite(buff1, 4, 512, fd1) != 512)
    exit4error("output file error\n");
    }
    return 0;
    }

    ReplyDelete