Ah the PVR:// was what I needed. Thanks Robert.

I have spent the morning hacking the file and updating it for freevo 1.5.2, the result is attached.

The result is usable but I have two three issues before I would be perfectly happy:
- startup is shaky. The view starts either:
- not at all, no sound either. Need to stop and restart playing (note the timeshift buffer is filled so xine is doing something, xine bug maybe?)
- veeeryyyyy sloooowwww, need to press pause twice to kick it into gear
- channel flipping takes from 2 to 5 seconds


BTW: I am using xine v0.99.2 and xinelib 1.0.0. with EPIA xxmc support.

Paul



Robert Winder wrote:
On Thursday, December 9, 2004, 09:53, Paul wrote:


All,


my freevo box is an EPIA MII6000 using a PVR350 for capture and baseboard
video for TV out. So I have mpeg compression on the TV card and mpeg
hardware decompression on the motherboard using xine. The machine is too
slow to play a TV mpeg stream over the CPU using mplayer.


So I need to hack together a plugin that does TV playing over xine.
Looking at the existing xine and mplayer tv plugins that should be doable.
I can do the quick fix allowing TV playing using a named pipe from
/dev/vide0 to xine.


However when I am going to make the effort I would like to add
timeshifting in it as well. I have plenty of disk space to play with so I
can cat /dev/video to a bufferfile and have xine play from that.


Did some tests from the commandline and this works UNTIL one tries to fast
forward beyond the end of the bufferfile. At that point xine stops the
playback. Are there any ideas on how to stop this from happening?


Well xine already got a input_pvr plugin built in especially for the
pvr150/250/350 cards and it includes timeshifting aswell ;-)

Got a working plugin although i don't think it works with the latest
freevo releases. You probably have to hack it a bit. xine_tv_plugin
attached.


/Robert


--
Paul Sijben             mailto:[EMAIL PROTECTED]
Amersfoort, NL          http://www.sijben.net
tel:+31 334557522       fax:+31 33 4557523
# adapted for Freevo 1.5.2 from code by Robert Winder(?) by Paul Sijben
#TODO: xine sometimes does not start right, no image at all or slow moving image
# the first case is remedied for the time being by pressing stop and then OK on 
the 
# remote control. The second case is remedied by pressing pause twice
# to activate this plugin put the following into you local_conf.py:
#plugin.remove('tv.mplayer')
#plugin.activate('tv.ivtv_xine_tv')
#
#and set TIMESHIFT_BUFFER to a filename on a partition with a few GB to spare
#


import config
import time, os
import util    # Various utilities
from event import *
import osd     # The OSD class, used to communicate with the OSD daemon
import rc      # The RemoteControl class.
import childapp # Handle child applications
import tv.epg_xmltv as epg # The Electronic Program Guide
import tv.ivtv as ivtv
import plugin

#import event as em 
from tv.channels import FreevoChannels


from event import *


# Set to 1 for debug output
DEBUG = config.DEBUG

TRUE = 1
FALSE = 0


# Create the OSD object
osd = osd.get_singleton()

class PluginInterface(plugin.Plugin):
    """
    Plugin to watch tv with xine.
    """
    def __init__(self):
        plugin.Plugin.__init__(self)
        try:
            config.XINE_COMMAND
        except:
            print String(_( 'ERROR' )) + ': ' + \
                  String(_("'XINE_COMMAND' not defined, plugin 'xine' 
deactivated.\n" \
                           'please check the xine section in freevo_config.py' 
))
            return

        if config.XINE_COMMAND.find('fbxine') >= 0:
            type = 'fb'
        else:
            type = 'X'

        if not hasattr(config, 'XINE_VERSION'):
            config.XINE_VERSION = 0
            for data in util.popen3.stdout('%s --version' % 
config.XINE_COMMAND):
                m = re.match('^.* v?([0-9])\.([0-9]+)\.([0-9]*).*', data)
                if m:
                    config.XINE_VERSION = int('%02d%02d%02d' % (int(m.group(1)),
                                                                  
int(m.group(2)),
                                                                  
int(m.group(3))))
                    if data.find('cvs') >= 0:
                        config.XINE_VERSION += 1

            _debug_('detect xine version %s' % config.XINE_VERSION)

        if config.XINE_VERSION < 922:
            print String(_( 'ERROR' )) + ': ' + \
                  String(_( "'xine-ui' version too old, plugin 'xine' 
deactivated" ))
            print String(_( 'You need software %s' )) % 'xine-ui > 0.9.21'
            return

        # register xine as the object to play
        plugin.register(IVTV_TV(type,config.XINE_VERSION), plugin.TV)


class IVTV_TV:

    __muted    = 0
    __igainvol = 0
    
    def __init__(self,type,version):
        self.tuner_chidx = 0    # Current channel, index into config.TV_CHANNELS
        self.app_mode = 'tv'
        self.app       = None
        self.type = type
        self.version=version
        self.fc = FreevoChannels() 
        self.current_vg = None 


        
    def TunerSetChannel(self, tuner_channel):
        for pos in range(len(config.TV_CHANNELS)):
            channel = config.TV_CHANNELS[pos]
            if channel[2] == tuner_channel:
                self.tuner_chidx = pos
                return
        print 'ERROR: Cannot find tuner channel "%s" in the TV channel listing' 
% tuner_channel
        self.tuner_chidx = 0


    def TunerGetChannelInfo(self):
        '''Get program info for the current channel'''
        
        tuner_id = config.TV_CHANNELS[self.tuner_chidx][2]
        chan_name = config.TV_CHANNELS[self.tuner_chidx][1]
        chan_id = config.TV_CHANNELS[self.tuner_chidx][0]

        channels = epg.get_guide().GetPrograms(start=time.time(),
                                               stop=time.time(), 
chanids=[chan_id])

        if channels and channels[0] and channels[0].programs:
            start_s = time.strftime('%H:%M', 
time.localtime(channels[0].programs[0].start))
            stop_s = time.strftime('%H:%M', 
time.localtime(channels[0].programs[0].stop))
            ts = '(%s-%s)' % (start_s, stop_s)
            prog_info = '%s %s' % (ts, channels[0].programs[0].title)
        else:
            prog_info = 'No info'
            
        return tuner_id, chan_name, prog_info


    def TunerGetChannel(self):
        return config.TV_CHANNELS[self.tuner_chidx][2]


    def TunerNextChannel(self):
        self.tuner_chidx = (self.tuner_chidx+1) % len(config.TV_CHANNELS)


    def TunerPrevChannel(self):
        self.tuner_chidx = (self.tuner_chidx-1) % len(config.TV_CHANNELS)

        
    def Play(self, mode, tuner_channel=None):

        print 'PLAY CHAN: %s' % tuner_channel

        if tuner_channel != None:
            try:
                self.TunerSetChannel(tuner_channel)
            except ValueError:
                pass

        if not tuner_channel: 
            tuner_channel = self.fc.getChannel()
            print 'PLAY CHAN: %s' % tuner_channel

        vg = self.current_vg = self.fc.getVideoGroup(tuner_channel) 
        print 'PLAY GROUP: %s' % vg.desc

        if mode == 'tv':                
         if vg.group_type == 'ivtv':
            ivtv_dev = ivtv.IVTV(vg.vdev)
            ivtv_dev.init_settings()
            ivtv_dev.setinput(vg.input_num)
            ivtv_dev.print_settings()
            self.fc.chanSet(tuner_channel)

            command = [ '--prio=%s' % config.MPLAYER_NICE ] + \
                        config.XINE_COMMAND.split(" ") + \
                        [ '--stdctl' , '-V', config.XINE_VO_DEV,\
                        '-A', config.XINE_AO_DEV ] + \
                        config.XINE_ARGS_DEF.split(" ")+\
                         [ ' pvr://%s' % config.TIMESHIFT_BUFFER ]

        else:
            print 'Mode "%s" is not implemented' % mode  # XXX ui.message()
            return

        if not rc.PYLIRC and '--no-lirc' in command:
                command.remove('--no-lirc')

        if self.version < 923:
                for arg in command:
                        if arg.startswith('--post'):
                                command.remove(arg)
                                break


        self.mode = mode

        # XXX Mixer manipulation code.
        # TV is on line in
        # VCR is mic in
        # btaudio (different dsp device) will be added later
        mixer = plugin.getbyname('MIXER')
        
        if mixer and config.MAJOR_AUDIO_CTRL == 'VOL':
            mixer_vol = mixer.getMainVolume()
            mixer.setMainVolume(0)
        elif mixer and config.MAJOR_AUDIO_CTRL == 'PCM':
            mixer_vol = mixer.getPcmVolume()
            mixer.setPcmVolume(0)

        # Start up the TV task
        
        _debug_('Xine.play() Starting cmd=%s' % command)
        self.app=childapp.ChildApp2(command)
        self.prev_app=rc.app()
        rc.app(self)
        
        if osd.focused_app():
            osd.focused_app().hide()

        # Suppress annoying audio clicks
        time.sleep(0.4)
        # XXX Hm.. This is hardcoded and very unflexible.
        if mixer and mode == 'vcr':
            mixer.setMicVolume(config.VCR_IN_VOLUME)
        elif mixer:
            mixer.setLineinVolume(config.TV_IN_VOLUME)
            mixer.setIgainVolume(config.TV_IN_VOLUME)
            
        if mixer and config.MAJOR_AUDIO_CTRL == 'VOL':
            mixer.setMainVolume(mixer_vol)
        elif mixer and config.MAJOR_AUDIO_CTRL == 'PCM':
            mixer.setPcmVolume(mixer_vol)
        if DEBUG: print '%s: started %s app' % (time.time(), self.mode)
 
    def Stop(self, channel_change=0):
        mixer = plugin.getbyname('MIXER')
        if mixer and not channel_change:
                mixer.setLineinVolume(0)
                mixer.setMicVolume(0)
                mixer.setIgainVolume(0) # Input on emu10k cards.

        self.app.stop('quit\n')

        if osd.focused_app() and not channel_change:
           osd.focused_app().show() 
        if os.path.exists('/tmp/freevo.wid'): os.unlink('/tmp/freevo.wid')
        if os.path.exists(config.TIMESHIFT_BUFFER): 
                os.unlink(config.TIMESHIFT_BUFFER)

        print 'stopped %s app' % self.mode
        


    def eventhandler(self, event, menuw=None):
        print '%s: %s app got %s event' % (time.time(), self.mode, event)
        s_event = '%s' % event

        if event == STOP or event == PLAY_END:
            self.Stop()
            rc.post_event(PLAY_END)
            return TRUE
        
        if event == PAUSE or event == PLAY:
            self.app.write('pause\n')
            return TRUE

        if event in [ TV_CHANNEL_UP, TV_CHANNEL_DOWN] or 
s_event.startswith('INPUT_'):
            if event == TV_CHANNEL_UP:
                nextchan = self.fc.getNextChannel()
            elif event == TV_CHANNEL_DOWN:
                nextchan = self.fc.getPrevChannel()
            else:
                chan = int( s_event[6] )
                nextchan = self.fc.getManChannel(chan)
                        
            print 'NEXT CHAN: %s' % nextchan
            nextvg = self.fc.getVideoGroup(nextchan)
            print 'NEXT GROUP: %s' % nextvg.desc

            if self.current_vg != nextvg:
                self.Stop(channel_change=1)
                self.Play('tv', nextchan)
                return TRUE

            if self.mode == 'vcr':
                return   

            elif self.current_vg.group_type == 'ivtv':
                self.fc.chanSet(nextchan)
                #self.app.write('seek 999999 0\n')
            else:
                freq_khz = self.fc.chanSet(nextchan, app=self.app)
                #new_freq = '%1.3f' % (freq_khz / 1000.0)
                #self.app.write('tv_set_freq %s\n' % new_freq)
        
            self.current_vg = self.fc.getVideoGroup(self.fc.getChannel())
            
            # Display a channel changed message  (mplayer ?  api osd xine ?) 
            tuner_id, chan_name, prog_info = self.fc.getChannelInfo()
            now = time.strftime('%H:%M')
            msg = '%s %s (%s): %s' % (now, chan_name, tuner_id, prog_info)
            cmd = 'osd_show_text "%s"\n' % msg
            self.app.write(cmd)
            return TRUE
            

        if event == SEEK:
            pos = int(event.arg)
            if pos < 0:
                action='SeekRelative-'
                pos = 0 - pos
            else:
                action='SeekRelative+'
            if pos <= 15:
                pos = 15
            elif pos <= 30:
                pos = 30
            else:
                pos = 30
            self.app.write('%s%s\n' % (action, pos))
            return TRUE 
        
        if event == TOGGLE_OSD:
            self.app.write('OSDStreamInfos\n')
            return TRUE
        
        if event == VIDEO_TOGGLE_INTERLACE:
                self.app.write('ToggleInterleave\n')
                self.item['deinterlace'] = not self.item['deinterlace']
                return TRUE

        return  FALSE          

Reply via email to