示例#1
0
def encode(infile, options):
    """Encode infile (a MultimediaFile) with ffmpeg, using the given options."""
    cmd = 'ffmpeg'
    cmd += ' -i "%s"' % infile.filename
    if options['format'] in ['vcd', 'svcd', 'dvd']:
        cmd += ' -tvstd %s' % options['tvsys']
        cmd += ' -target %s-%s' % (options['format'], options['tvsys'])
    cmd += ' -r %g' % options['fps']
    cmd += ' -ar %s' % options['samprate']

    # Convert scale/expand to ffmpeg's padding system
    if options['scale']:
        cmd += ' -s %sx%s' % options['scale']
    if options['expand']:
        e_width, e_height = options['expand']
        s_width, s_height = options['scale']
        h_pad = (e_width - s_width) / 2
        v_pad = (e_height - s_height) / 2
        if h_pad > 0:
            cmd += ' -padleft %s -padright %s' % (h_pad, h_pad)
        if v_pad > 0:
            cmd += ' -padtop %s -padbottom %s' % (v_pad, v_pad)
    if options['widescreen']:
        cmd += ' -aspect 16:9'
    else:
        cmd += ' -aspect 4:3'

    cmd += ' -o "%s"' % options['out']

    script = Script('ffmpeg_encode')
    script.append(cmd)
    script.run()
    return script
示例#2
0
def encode(infile, options):
    """Encode infile (a MultimediaFile) with ffmpeg, using the given options."""
    cmd = 'ffmpeg'
    cmd += ' -i "%s"' % infile.filename
    if options['format'] in ['vcd', 'svcd', 'dvd']:
        cmd += ' -tvstd %s' % options['tvsys']
        cmd += ' -target %s-%s' % (options['format'], options['tvsys'])
    cmd += ' -r %g' % options['fps']
    cmd += ' -ar %s' % options['samprate']

    # Convert scale/expand to ffmpeg's padding system
    if options['scale']:
        cmd += ' -s %sx%s' % options['scale']
    if options['expand']:
        e_width, e_height = options['expand']
        s_width, s_height = options['scale']
        h_pad = (e_width - s_width) / 2
        v_pad = (e_height - s_height) / 2
        if h_pad > 0:
            cmd += ' -padleft %s -padright %s' % (h_pad, h_pad)
        if v_pad > 0:
            cmd += ' -padtop %s -padbottom %s' % (v_pad, v_pad)
    if options['widescreen']:
        cmd += ' -aspect 16:9'
    else:
        cmd += ' -aspect 4:3'

    cmd += ' -o "%s"' % options['out']

    script = Script('ffmpeg_encode')
    script.append(cmd)
    script.run()
    return script
示例#3
0
def encode(infile, options):
    """Encode infile with mpeg2enc, using the given options.
    infile is a MultimediaFile; options is a dictionary."""
    log.warn("This encoder is very experimental, and may not work.")

    outname = options['out']
    # YUV raw video FIFO, for piping video from mplayer to mpeg2enc
    yuvfile = '%s.yuv' % outname
    try:
        os.remove(yuvfile)
    except:
        pass
    os.mkfifo(yuvfile)
    
    # Filenames for intermediate streams (ac3/m2v etc.)
    # Appropriate suffix for audio stream
    if options['format'] in ['vcd', 'svcd']:
        audiofile = '%s.mpa' % outname
    else:
        audiofile = '%s.ac3' % outname
    # Appropriate suffix for video stream
    if options['format'] == 'vcd':
        videofile = '%s.m1v' % outname
    else:
        videofile = '%s.m2v' % outname
    script = Script('mpeg2enc_encode')
    # Do audio
    script.append(encode_audio(infile, audiofile, options))
    # Do video
    script.append(rip_video(infile, yuvfile, options))
    script.append(encode_video(infile, yuvfile, videofile, options))
    # Combine audio and video
    script.append(mplex_streams(videofile, audiofile, outname, options))
    script.run()
    return script
示例#4
0
class TextMenu:
    """Simple menu with selectable text titles. For now, basically a clone
    of the classic 'makemenu' output."""
    def __init__(self, options):
        self.options = options
        self.script = Script('TextMenu')
        basename = self.options['out']
        # TODO: Store intermediate images in a temp folder
        self.bg_canvas = basename + '.bg_canvas.png'
        self.fg_canvas = basename + '.fg_canvas.png'
        self.fg_highlight = basename + '.fg_highlight.png'
        self.fg_selection = basename + '.fg_selection.png'
        print "Creating a menu with %s titles" % len(options['titles'])
        self.build_reusables()
        self.draw_background_canvas()
        self.draw_background_layer()
        self.draw_highlight_layer()
        self.draw_selection_layer()
        self.gen_video()
        self.gen_audio()
        self.gen_mpeg()
        self.script.run()

    def build_reusables(self):
        """Assemble some re-usable ImageMagick command snippets.
        For now, just create an ImageMagick draw command for all title text
        for placing labels and highlights.
        Corresponds to lines 343-377 of makemenu"""
        spacing = 30  # Pixel spacing between lines
        y = 0  # Current y-coordinate
        y2 = spacing  # Next y-coordinate
        titlenum = 1  # Title number (for labeling)
        labels = ''
        buttons = ''
        for title in self.options['titles']:
            print "Adding '%s'" % title
            # TODO: Escape special characters in title

            # For VCD, number the titles
            if self.options['format'] == 'vcd':
                labels += " text 0,%s '%s. %s' " % (y, titlenum, title)
            # Others use title string alone
            else:
                labels += " text 15,%s '%s'" % (y, title)
                buttons += " text 0,%s '>'" % y

            # Increment y-coordinates and title number
            y = y2
            y2 += spacing
            titlenum += 1
        self.im_labels = labels
        self.im_buttons = buttons

    def draw_background_canvas(self):
        """Draw background canvas.
        Corresp. to lines 400-411 of makemenu."""
        # Generate default blue-black gradient background
        # TODO: Implement -background
        cmd = 'convert'
        cmd += ' -size %sx%s ' % self.options['expand']
        cmd += ' gradient:blue-black'
        cmd += ' -gravity center -matte'
        cmd += self.bg_canvas
        self.script.append(cmd)

    def draw_background_layer(self):
        """Draw the background layer for the menu, including static title text.
        Corresp. to lines 438-447, 471-477 of makemenu."""
        # Draw text titles on a transparent background.
        cmd = 'convert'
        cmd += ' -size %sx%s' % self.options['scale']
        cmd += ' xc:none -antialias -font "%s"' % self.options['font']
        cmd += ' -pointsize %s' % self.options['fontsize']
        cmd += ' -fill "%s"' % self.options['textcolor']
        cmd += ' -stroke black -strokewidth 3 '
        cmd += ' -draw "gravity %s %s"' % \
                   (self.options['align'], self.im_labels)
        cmd += ' -stroke none -draw "gravity %s %s" ' % \
                   (self.options['align'], self.im_labels)
        cmd += self.fg_canvas
        self.script.append(cmd)
        # Composite text over background
        cmd = 'composite'
        cmd += ' -compose Over -gravity center'
        cmd += ' %s %s' % (self.fg_canvas, self.bg_canvas)
        cmd += ' -depth 8 "%s.ppm"' % self.options['out']
        self.script.append(cmd)

    def draw_highlight_layer(self):
        """Draw menu highlight layer, suitable for multiplexing.
        Corresp. to lines 449-458, 479-485 of makemenu."""
        # Create text layer (at safe-area size)
        cmd = 'convert -size %sx%s ' % self.options['scale']
        cmd += ' xc:none -antialias -font "%s" ' % self.options['font']
        cmd += ' -weight bold '
        cmd += ' -pointsize %s ' % self.options['fontsize']
        cmd += ' -fill "%s" ' % self.options['highlightcolor']
        cmd += ' -draw "gravity %s %s" ' % (self.options['align'],
                                            self.im_buttons)
        cmd += ' -type Palette -colors 3 '
        cmd += ' png8:%s' % self.fg_highlight
        self.script.append(cmd)
        # Pseudo-composite, to expand layer to target size
        cmd = 'composite -compose Src -gravity center '
        cmd += ' %s %s ' % (self.fg_highlight, self.bg_canvas)
        cmd += ' png8:"%s.hi.png"' % self.options['out']
        self.script.append(cmd)

    def draw_selection_layer(self):
        """Draw menu selections on a transparent background.
        Corresp. to lines 460-469, 487-493 of makemenu."""
        # Create text layer (at safe-area size)
        cmd = 'convert -size %sx%s ' % self.options['scale']
        cmd += ' xc:none -antialias -font "%s" ' % self.options['font']
        cmd += ' -weight bold '
        cmd += ' -pointsize %s ' % self.options['fontsize']
        cmd += ' -fill "%s" ' % self.options['selectcolor']
        cmd += ' -draw "gravity %s %s" ' % (self.options['align'],
                                            self.im_buttons)
        cmd += ' -type Palette -colors 3 '
        cmd += ' png8:%s' % self.fg_selection
        self.script.append(cmd)
        # Pseudo-composite, to expand layer to target size
        cmd = 'composite -compose Src -gravity center '
        cmd += ' %s %s ' % (self.fg_selection, self.bg_canvas)
        cmd += ' png8:"%s.sel.png"' % self.options['out']
        self.script.append(cmd)

    def gen_video(self):
        """Generate a video stream (mpeg1/2) from the menu background image.
        Corresp. to lines 495-502 of makemenu."""

        # ppmtoy4m part
        cmd = 'ppmtoy4m -S 420mpeg2 '
        if self.options['tvsys'] == 'ntsc':
            cmd += ' -A 10:11 -F 30000:1001 '
        else:
            cmd += ' -A 59:54 -F 25:1 '
        # TODO: Remove hardcoded frame count
        cmd += ' -n 90 '
        cmd += ' -r "%s.ppm" ' % self.options['out']
        # mpeg2enc part
        cmd += ' | mpeg2enc -a 2 '
        # PAL/NTSC
        if self.options['tvsys'] == 'ntsc':
            cmd += ' -F 4 -n n '
        else:
            cmd += ' -F 3 -n p '
        # Use correct format flags and filename extension
        if self.options['format'] == 'vcd':
            self.vstream = '%s.m1v' % self.options['out']
            cmd += ' -f 1 '
        else:
            self.vstream = '%s.m2v' % self.options['out']
            if self.options['format'] == 'dvd':
                cmd += ' -f 8 '
            elif self.options['format'] == 'svcd':
                cmd += ' -f 4 '
        cmd += ' -o "%s" ' % self.vstream
        self.script.append(cmd)

    def gen_audio(self):
        """Generate an audio stream (mp2/ac3) from the given audio file
        (or generate silence instead).
        Corresp. to lines 413-430, 504-518 of makemenu."""
        if self.options['format'] in ['vcd', 'svcd']:
            acodec = 'mp2'
            self.astream = "%s.mp2" % self.options['out']
        else:
            acodec = 'ac3'
            self.astream = "%s.ac3" % self.options['out']
        cmd = 'ffmpeg '
        # If audio file was provided, encode it
        if self.options['audio']:
            cmd += ' -i "%s"' % self.options['audio']
        # Otherwise, generate 4-second silence
        else:
            cmd += ' -f s16le -i /dev/zero -t 4'
        cmd += ' -ac 2 -ab 224'
        cmd += ' -ar %s' % (self.options['samprate'])
        cmd += ' -acodec %s' % acodec
        cmd += ' -y "%s"' % self.astream
        self.script.append(cmd)

    def gen_mpeg(self):
        """Multiplex audio and video streams to create an mpeg.
        Corresp. to lines 520-526 of makemenu."""
        cmd = 'mplex -o "%s.temp.mpg" ' % self.options['out']
        # Format flags
        if self.options['format'] == 'vcd':
            cmd += ' -f 1 '
        else:
            # Variable bitrate
            cmd += ' -V '
            if self.options['format'] == 'dvd':
                cmd += ' -f 8 '
            elif self.options['format'] == 'svcd':
                cmd += ' -f 4 '
        cmd += ' "%s" "%s" ' % (self.astream, self.vstream)
        self.script.append(cmd)

    def mux_subtitles(self):
        """Multiplex the output video with highlight and selection
        subtitles, so the resulting menu can be navigated.
        Corresp. to lines 528-548 of makemenu."""

        xml = '<subpictures>\n'
        xml += '  <stream>\n'
        xml += '  <spu force="yes" start="00:00:00.00"\n'
        xml += '       highlight="%s.hi.png"\n' % self.options['out']
        xml += '       select="%s.sel.png"\n' % self.options['out']
        xml += '       autooutline="infer">\n'
        xml += '  </spu>\n'
        xml += '  </stream>\n'
        xml += '</subpictures>\n'
        try:
            xmlfile = open('%s.xml' % self.options['out'], 'w')
        except:
            log.error('Could not open file "%s.xml"' % self.options['out'])
        else:
            xmlfile.write(xml)
            xmlfile.close()

        cmd = 'spumux "%s.xml" < "%s.temp.mpg" > "%s.mpg"' % \
                (self.options['out'], self.options['out'], self.options['out'])
        self.script.append(cmd)
示例#5
0
class TextMenu:
    """Simple menu with selectable text titles. For now, basically a clone
    of the classic 'makemenu' output."""

    def __init__(self, options):
        self.options = options
        self.script = Script("TextMenu")
        basename = self.options["out"]
        # TODO: Store intermediate images in a temp folder
        self.bg_canvas = basename + ".bg_canvas.png"
        self.fg_canvas = basename + ".fg_canvas.png"
        self.fg_highlight = basename + ".fg_highlight.png"
        self.fg_selection = basename + ".fg_selection.png"
        print "Creating a menu with %s titles" % len(options["titles"])
        self.build_reusables()
        self.draw_background_canvas()
        self.draw_background_layer()
        self.draw_highlight_layer()
        self.draw_selection_layer()
        self.gen_video()
        self.gen_audio()
        self.gen_mpeg()
        self.script.run()

    def build_reusables(self):
        """Assemble some re-usable ImageMagick command snippets.
        For now, just create an ImageMagick draw command for all title text
        for placing labels and highlights.
        Corresponds to lines 343-377 of makemenu"""
        spacing = 30  # Pixel spacing between lines
        y = 0  # Current y-coordinate
        y2 = spacing  # Next y-coordinate
        titlenum = 1  # Title number (for labeling)
        labels = ""
        buttons = ""
        for title in self.options["titles"]:
            print "Adding '%s'" % title
            # TODO: Escape special characters in title

            # For VCD, number the titles
            if self.options["format"] == "vcd":
                labels += " text 0,%s '%s. %s' " % (y, titlenum, title)
            # Others use title string alone
            else:
                labels += " text 15,%s '%s'" % (y, title)
                buttons += " text 0,%s '>'" % y

            # Increment y-coordinates and title number
            y = y2
            y2 += spacing
            titlenum += 1
        self.im_labels = labels
        self.im_buttons = buttons

    def draw_background_canvas(self):
        """Draw background canvas.
        Corresp. to lines 400-411 of makemenu."""
        # Generate default blue-black gradient background
        # TODO: Implement -background
        cmd = "convert"
        cmd += " -size %sx%s " % self.options["expand"]
        cmd += " gradient:blue-black"
        cmd += " -gravity center -matte"
        cmd += self.bg_canvas
        self.script.append(cmd)

    def draw_background_layer(self):
        """Draw the background layer for the menu, including static title text.
        Corresp. to lines 438-447, 471-477 of makemenu."""
        # Draw text titles on a transparent background.
        cmd = "convert"
        cmd += " -size %sx%s" % self.options["scale"]
        cmd += ' xc:none -antialias -font "%s"' % self.options["font"]
        cmd += " -pointsize %s" % self.options["fontsize"]
        cmd += ' -fill "%s"' % self.options["textcolor"]
        cmd += " -stroke black -strokewidth 3 "
        cmd += ' -draw "gravity %s %s"' % (self.options["align"], self.im_labels)
        cmd += ' -stroke none -draw "gravity %s %s" ' % (self.options["align"], self.im_labels)
        cmd += self.fg_canvas
        self.script.append(cmd)
        # Composite text over background
        cmd = "composite"
        cmd += " -compose Over -gravity center"
        cmd += " %s %s" % (self.fg_canvas, self.bg_canvas)
        cmd += ' -depth 8 "%s.ppm"' % self.options["out"]
        self.script.append(cmd)

    def draw_highlight_layer(self):
        """Draw menu highlight layer, suitable for multiplexing.
        Corresp. to lines 449-458, 479-485 of makemenu."""
        # Create text layer (at safe-area size)
        cmd = "convert -size %sx%s " % self.options["scale"]
        cmd += ' xc:none -antialias -font "%s" ' % self.options["font"]
        cmd += " -weight bold "
        cmd += " -pointsize %s " % self.options["fontsize"]
        cmd += ' -fill "%s" ' % self.options["highlightcolor"]
        cmd += ' -draw "gravity %s %s" ' % (self.options["align"], self.im_buttons)
        cmd += " -type Palette -colors 3 "
        cmd += " png8:%s" % self.fg_highlight
        self.script.append(cmd)
        # Pseudo-composite, to expand layer to target size
        cmd = "composite -compose Src -gravity center "
        cmd += " %s %s " % (self.fg_highlight, self.bg_canvas)
        cmd += ' png8:"%s.hi.png"' % self.options["out"]
        self.script.append(cmd)

    def draw_selection_layer(self):
        """Draw menu selections on a transparent background.
        Corresp. to lines 460-469, 487-493 of makemenu."""
        # Create text layer (at safe-area size)
        cmd = "convert -size %sx%s " % self.options["scale"]
        cmd += ' xc:none -antialias -font "%s" ' % self.options["font"]
        cmd += " -weight bold "
        cmd += " -pointsize %s " % self.options["fontsize"]
        cmd += ' -fill "%s" ' % self.options["selectcolor"]
        cmd += ' -draw "gravity %s %s" ' % (self.options["align"], self.im_buttons)
        cmd += " -type Palette -colors 3 "
        cmd += " png8:%s" % self.fg_selection
        self.script.append(cmd)
        # Pseudo-composite, to expand layer to target size
        cmd = "composite -compose Src -gravity center "
        cmd += " %s %s " % (self.fg_selection, self.bg_canvas)
        cmd += ' png8:"%s.sel.png"' % self.options["out"]
        self.script.append(cmd)

    def gen_video(self):
        """Generate a video stream (mpeg1/2) from the menu background image.
        Corresp. to lines 495-502 of makemenu."""

        # ppmtoy4m part
        cmd = "ppmtoy4m -S 420mpeg2 "
        if self.options["tvsys"] == "ntsc":
            cmd += " -A 10:11 -F 30000:1001 "
        else:
            cmd += " -A 59:54 -F 25:1 "
        # TODO: Remove hardcoded frame count
        cmd += " -n 90 "
        cmd += ' -r "%s.ppm" ' % self.options["out"]
        # mpeg2enc part
        cmd += " | mpeg2enc -a 2 "
        # PAL/NTSC
        if self.options["tvsys"] == "ntsc":
            cmd += " -F 4 -n n "
        else:
            cmd += " -F 3 -n p "
        # Use correct format flags and filename extension
        if self.options["format"] == "vcd":
            self.vstream = "%s.m1v" % self.options["out"]
            cmd += " -f 1 "
        else:
            self.vstream = "%s.m2v" % self.options["out"]
            if self.options["format"] == "dvd":
                cmd += " -f 8 "
            elif self.options["format"] == "svcd":
                cmd += " -f 4 "
        cmd += ' -o "%s" ' % self.vstream
        self.script.append(cmd)

    def gen_audio(self):
        """Generate an audio stream (mp2/ac3) from the given audio file
        (or generate silence instead).
        Corresp. to lines 413-430, 504-518 of makemenu."""
        if self.options["format"] in ["vcd", "svcd"]:
            acodec = "mp2"
            self.astream = "%s.mp2" % self.options["out"]
        else:
            acodec = "ac3"
            self.astream = "%s.ac3" % self.options["out"]
        cmd = "ffmpeg "
        # If audio file was provided, encode it
        if self.options["audio"]:
            cmd += ' -i "%s"' % self.options["audio"]
        # Otherwise, generate 4-second silence
        else:
            cmd += " -f s16le -i /dev/zero -t 4"
        cmd += " -ac 2 -ab 224"
        cmd += " -ar %s" % (self.options["samprate"])
        cmd += " -acodec %s" % acodec
        cmd += ' -y "%s"' % self.astream
        self.script.append(cmd)

    def gen_mpeg(self):
        """Multiplex audio and video streams to create an mpeg.
        Corresp. to lines 520-526 of makemenu."""
        cmd = 'mplex -o "%s.temp.mpg" ' % self.options["out"]
        # Format flags
        if self.options["format"] == "vcd":
            cmd += " -f 1 "
        else:
            # Variable bitrate
            cmd += " -V "
            if self.options["format"] == "dvd":
                cmd += " -f 8 "
            elif self.options["format"] == "svcd":
                cmd += " -f 4 "
        cmd += ' "%s" "%s" ' % (self.astream, self.vstream)
        self.script.append(cmd)

    def mux_subtitles(self):
        """Multiplex the output video with highlight and selection
        subtitles, so the resulting menu can be navigated.
        Corresp. to lines 528-548 of makemenu."""

        xml = "<subpictures>\n"
        xml += "  <stream>\n"
        xml += '  <spu force="yes" start="00:00:00.00"\n'
        xml += '       highlight="%s.hi.png"\n' % self.options["out"]
        xml += '       select="%s.sel.png"\n' % self.options["out"]
        xml += '       autooutline="infer">\n'
        xml += "  </spu>\n"
        xml += "  </stream>\n"
        xml += "</subpictures>\n"
        try:
            xmlfile = open("%s.xml" % self.options["out"], "w")
        except:
            log.error('Could not open file "%s.xml"' % self.options["out"])
        else:
            xmlfile.write(xml)
            xmlfile.close()

        cmd = 'spumux "%s.xml" < "%s.temp.mpg" > "%s.mpg"' % (
            self.options["out"],
            self.options["out"],
            self.options["out"],
        )
        self.script.append(cmd)
示例#6
0
def encode(infile, options):
    """Return a Script to encode infile (a MultimediaFile) with mencoder,
    using the given options."""
    cmd = 'mencoder'
    cmd += ' "%s" -o "%s"' % (infile.filename, options['out'])
    cmd += ' -oac lavc -ovc lavc -of mpeg'
    # Format
    if options['format'] in ['vcd', 'svcd']:
        cmd += ' -mpegopts format=x%s' % options['format']
    else:
        cmd += ' -mpegopts format=dvd'
    
    # Audio settings
    # Adjust sampling rate
    # TODO: Don't resample unless needed
    cmd += ' -srate %s' % options['samprate']
    cmd += ' -af lavcresample=%s' % options['samprate']

    # Video codec
    if options['format'] == 'vcd':
        lavcopts = 'vcodec=mpeg1video'
    else:
        lavcopts = 'vcodec=mpeg2video'
    # Audio codec
    if options['format'] in ['vcd', 'svcd']:
        lavcopts += ':acodec=mp2'
    else:
        lavcopts += ':acodec=ac3'
    lavcopts += ':abitrate=%s:vbitrate=%s' % \
            (options['abitrate'], options['vbitrate'])
    # Maximum video bitrate
    lavcopts += ':vrc_maxrate=%s' % options['vbitrate']
    if options['format'] == 'vcd':
        lavcopts += ':vrc_buf_size=327'
    elif options['format'] == 'svcd':
        lavcopts += ':vrc_buf_size=917'
    else:
        lavcopts += ':vrc_buf_size=1835'
    # Set appropriate target aspect
    if options['widescreen']:
        lavcopts += ':aspect=16/9'
    else:
        lavcopts += ':aspect=4/3'
    # Put all lavcopts together
    cmd += ' -lavcopts %s' % lavcopts

    # FPS
    if options['tvsys'] == 'pal':
        cmd += ' -ofps 25/1'
    elif options['tvsys'] == 'ntsc':
        cmd += ' -ofps 30000/1001' # ~= 29.97

    # Scale/expand to fit target frame
    if options['scale']:
        vfilter = 'scale=%s:%s' % options['scale']
        # Expand is not done unless also scaling
        if options['expand']:
            vfilter += ',expand=%s:%s' % options['expand']
        cmd += ' -vf ' + vfilter

    # Create the Script, and add the single command to it
    script = Script('mencoder_encode')
    script.append(cmd)
    script.run()
    return script