Kodi Community Forum

Full Version: Add-on to transcode and stream library items and files over slow vpn connections
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
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
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
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.
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 ?
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.
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.
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