Add-on to transcode and stream library items and files over slow vpn connections
#1
Sometimes I find myself with my laptop on a wifi that's not fast enough to stream my collection of glorious FullHD video, which Kodi accesses on NFS over VPN. This happened often enough so that I did a workaround:
xml:

<playercorefactory>
 <players>
   <player name="Transcode" type="ExternalPlayer" audio="false" video="true">
     <filename>/home/user/mpv.sh</filename>
     <args>"{1}"</args>
     <hidexbmc>false</hidexbmc>
     <playcountminimumtime>600</playcountminimumtime>
   </player>
 </players>
</playercorefactory>

Basically I added a script as an external player. This script does something like this:

bash:

ssh <server> ffprobe "$1" 2> /tmp/mpv.txt
VENC='-c:v libx264 -preset medium -crf 20'
VIDEO=$(egrep '^[ \t]*Stream.*Video' /tmp/mpv.txt | head -1 | sed "s,^[ \\t]*Stream #0:\\([0-9]*\\).*,-map 0:\\1 $VENC,")
AUDIO=$(egrep '^[ \t]*Stream.*Audio' /tmp/mpv.txt | head -1 | sed 's,^[ \t]*Stream #0:\([0-9]*\).*,-map 0:\1 -c:a aac -b 192,')
SUBS=$(egrep '^[ \t]*Stream.*Subtitle' /tmp/mpv.txt | sed 's,^[ \t]*Stream #0:\([0-9]*\).*,-map 0:\1 -cConfused copy,')

ssh <server> ffmpeg -i "$1" -vf scale=1280:-2 $VIDEO $AUDIO $SUBS -f matroska - | mpv -  

And now I can play all my media even over slow connections. However, this approach has a few downsides. The first one is that it doesn't use the native player, so kodi cannot determine how much of the video is actually watched. The other issue is that I don't know how to do something similar on android (this is actually the sole reason I want to do this to be honest).

So I'm thinking there may be a more generic way to fix this by creating a native add-on that does this. Basically I want to add a context item on videos that, when used, connects to another machine, starts the transcoding, and starts playing the resulting stream using the internal player. Sounds simple enough I think.

Now, I've only pondered and investigated this for a few hours, but if anyone has any input on the following questions I would be grateful:
  1. How do I make a context item visible only for video items? The wikis list of boolean conditions doesn't seem to have any "IsVideo" or similar.
  2. How do I determine the full path to the video file from a context plugin? I'm able to get the path via the VideoInfoTag, but I can't get the filename (getFile() returns nothing).
  3. Kodi has support for SFTP, which mean there is some SSH implementation present, but I can't find that there is any way to access it from an add-on. Is there a way, or do I have to make a paramiko module or something?
  4. Is there a way to make the internal player play video from something that is not a regular file? For example, it would be nice to stream via a file-like python object, but otherwise I guess a FIFO or something could work. Any ideas?
  5. Is this a reasonable approach, or should I spend my time elsewhere? Wink
Reply
#2
ad1. there is `isPlayable` and `isFolder`, also if you popoulate `plot` parameter by `setParameter('plot','...')` it should also act as  video, there is also `setContent`
ad2. mayby try `getParameter('path')`
ad3. dunno, sorry
ad4. you could try to force `MIME` on your stream if you really have to or build on top of `xbmc.Player()`
ad5. ain't you have to wait for ffmpeg to finish transcoding your video before you can play it? Also you could write small video-proxy, that would server you files and if needed transcode them and serve them, that would make it more compatible
Reply
#3
Thanks for the feedback, will check it out.

I think the Kodi video player should be able to handle a stream (it can play http streams and similar, right?), so there is no need to wait for the transcode to finish before streaming. I don't want to save the transcodes, just transcode them on the fly to save bandwidth when on data limited networks.
Reply
#4
Are you sure Kodi support half-done files ? Because thats the only issue I see, also what if you download faster than you transcode ? Kodi will hit end of file and close ? (or maybe wait and buffer ?)

kodi handle well streams etc, those with encryption are supported in Leia, but anything else is no problem as from what I understand Kodi use ffmpeg as core.

You made me curious about half-done file because some players refuse to proper handle those.

out of curios, how do you handle multi audio-stream multi subtitle files ? how would you switch between those ?
Reply
#5
I think that if you transcode to a regular file and try to play that file at the same time you will run into trouble, but if you read from a pipe and possibly even a FIFO it will work. For my hack with external player, ffmpeg pipes the data-stream directly to mpv and that works just fine (with the caveat that you cannot seek in the video).

I only transcode one audio track to save bandwidth, and currently I have to edit the mpv.sh script if I want to select a different track. If I manage to create a plugin for this, perhaps you can choose which audio track you want before the transcode starts. I do copy all subtitle streams, and mpv can switch between them as usual. This does not necessarily mean that the Kodi player can handle it, but I don't think it would be unreasonable.
Reply
#6
for fastfowarding you would have to handle it on ffmpeg end, by canceling current process and starting new one with (if I remember correct) -ss to set starting point, thats even quite easy when you make your own player via class inherit.
Im curios how well the piping will work.
Reply
#7
The streaming seems to work at least using a FIFO file (so with this approach it will be restricted to unix-systems). I used the following code to test:
python:

import multiprocessing
import os
import subprocess

import xbmc

FIFO='/tmp/video.fifo'
VID="/path/to/testfile.mkv"

def transcode():
    with open(FIFO, 'w') as f:
        proc = subprocess.Popen(
                [   
                    "/usr/bin/ffmpeg",
                    "-i", VID,
                    "-vf", 'scale=720:-2',
                    '-c:v', 'libx264', '-preset', 'fast', '-crf', '20',
                    '-c:a', 'aac', '-b:a', '192k',
                    '-cConfused', 'copy',
                    '-f', 'matroska', '-' 
                ],  
                stdout=f)
        proc.communicate()


if __name__ == '__main__':
    player = xbmc.Player()

    try:
        os.mkfifo(FIFO)
    except OSError:
        pass
    p = multiprocessing.Process(target=transcode)
    p.start()

    player.play(FIFO)
    p.join()
    os.remove(FIFO)

isPlayable doesn't seem to work, and I couldn't find it in the list of conditionals either. I'm not sure if there are more than one way to define context items, but I have put it in addon.xml:
xml:

<extension point="kodi.context.item">
  <menu id="kodi.core.main">
    <item library="transcode.py">
      <label>Transcode</label>
      <visible>true</visible>
    </item>
  </menu>
</extension>

getParameter('path') unfortunately didn't work either on listitem or VideoInfoTag Sad
Reply

Logout Mark Read Team Forum Stats Members Help
Add-on to transcode and stream library items and files over slow vpn connections0