Jump to content

Unnecessary video transcode


stevewalison

Recommended Posts

stevewalison

Version 3.0.5621.1 on Windows. Using Emby 2.07 Player on a Roku 2.

 

When I am playing matroska files with h264 video and ac3, the server is causing both the video and the audio to be transcoded. Essentially since the audio needs to be converted to aac, the video is being transcoded instead of just copied.

 

  Metadata:
    encoder         : Lavf56.27.100
    Stream #0:0: Video: h264 (libx264), yuv420p, 1280x720, q=-1--1, max. 2594 kb/s, 23.98 fps, 90k tbn, 23.98 tbc (default)
    Metadata:
      encoder         : Lavc56.32.100 libx264
    Stream #0:1: Audio: aac, 48000 Hz, stereo, fltp, 128 kb/s (default)
    Metadata:
      encoder         : Lavc56.32.100 aac
Stream mapping:
  Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
  Stream #0:1 -> #0:1 (ac3 (native) -> aac (native)

 

If I run the following operation on a video file. Essentially just transcoding the audio same as from the server logs and copying the video directly instead of specifying a conversion.

 

ffmpeg.exe -i video.mkv -c:v copy -c:a:0 aac -strict experimental -ac 2 -ab 128000 -af "adelay=1,aresample=async=1,volume=2" out.mkv

 

Now, when I play the out.mkv file using the media browser with the Roku 2, the content is just a direct copy and doesn't take any noticable amount of the computer resources.

 

Is there a switch I can use which tells ffmpeg to attempt to copy when the h264 is supported and only transcode the audio through the server?

Link to comment
Share on other sites

with Roku we use HLS segmentation, where we slice the video into 3 second segments. Segments are cut based on keyframe locations. When we transcode the video, we can achieve near exact segment lengths by placing keyframes wherever we need. When we do stream copy, the segments are sliced based on the keyframe locations of the source video. This leads to wild fluctuations. We give the player a prediction ahead of time of X number of segments 3 seconds each, and then we end up with something drastically different. This causes other side effects such as stuttering, and "looping" at the end of the video. By looping I mean the number of segments are incorrect and this causes the player to suddenly rewind near the end of the video. 

 

you can read all about that issue here:

 

http://emby.media/community/index.php?/topic/19362-loading-please-wait-at-the-end-of-playback/

 

In order to do stream copy with HLS, we first need to know where the key frames are so that we can present an accurate playlist based on that rather than a fixed 3 seconds. so if you would like to join in that effort please feel free.

Link to comment
Share on other sites

stevewalison

Thanks Luke,

 

I understand the rational behind the transcoding now, thanks for explaining that. From your explanation, my "fix" to prevent the transcoding, is essentially just tricking the server to direct copy, and as a result hoping that the key interval roughly match the servers estimation. If they are way off, the issues described kick in?

 

I have never looked into HLS, and don't know any of it's specifications. Although, I am a bit confused on your explanation. Initially you describe it as slicing the video in 3 second segments. But later you describe it as slicing the segments based upon the location of the keyframe of the source which is not bound to a 3 second window. Or are you describing a signalling method to the client that tells the player that the next keyframe will occur in some number of segments in the future?

 

I guess, assuming the latter explanation where all content is split into 3 second segments regardless of keyframes. So if you had a description of the program clock location of all of the keyframes, you could accurately tell the client how many HLS segments there will be before the next keyframe? Therefore, the client won't get confused and start replaying current buffered content in a loop waiting for content that won't arrive.

 

My focus on other projects is primarily focused on delivery of data and transport protocols, and less on the video specifics, so I apologize if anything is blatantly wrong.

Link to comment
Share on other sites

h264 video is always segmented based on the locations of keyframes. when we transcode we can put keyframes anywhere we like, so that's why we can segment at exactly 3 seconds. when we stream copy, we need to segment on keyframe locations. this would result in a completely random pattern of segment lengths, as they are not usually spaced out consistently with videos. But it would be fine because we'd know exactly where they are. What we were doing up until recently is giving the video player a predicted playlist of 3 seconds per segment, and then just letting the chips fall where they may and whatever happens happens. But during playback it causes all kinds of problems because the player gets confused as the segments don't match the playlist we gave it before-hand.

 

so we need to find out how to get this info. the server already bundles ffmpeg which can do it, but it takes a long time, as in 10 minutes or more per video, which won't be acceptable during the library scan. this is because it collects a ton of other information throughout the process and it takes a long time. it can be done in a matter of seconds with a utility designed for that purposes, we just need to find or create one.

Link to comment
Share on other sites

stevewalison

A pretty easy way to get this information with mkv's is to use MKVToolNix with the mkvinfo application. Although, doesn't really do anything to help with any other container formats.

 

$ mkvinfo file.mkv -t | grep "key, track number 1"

 

Would output the following:

 

| + SimpleBlock (key, track number 1, 1 frame(s), timecode 0.000s = 00:00:00.000)
| + SimpleBlock (key, track number 1, 1 frame(s), timecode 10.427s = 00:00:10.427)
| + SimpleBlock (key, track number 1, 1 frame(s), timecode 20.812s = 00:00:20.812)
| + SimpleBlock (key, track number 1, 1 frame(s), timecode 31.240s = 00:00:31.240)
| + SimpleBlock (key, track number 1, 1 frame(s), timecode 34.535s = 00:00:34.535)
| + SimpleBlock (key, track number 1, 1 frame(s), timecode 44.962s = 00:00:44.962)
| + SimpleBlock (key, track number 1, 1 frame(s), timecode 51.343s = 00:00:51.343)
| + SimpleBlock (key, track number 1, 1 frame(s), timecode 55.305s = 00:00:55.305)
| + SimpleBlock (key, track number 1, 1 frame(s), timecode 58.016s = 00:00:58.016)
| + SimpleBlock (key, track number 1, 1 frame(s), timecode 67.317s = 00:01:07.317)
...
 
By running mkvinfo at the start without -t, it will let you know what track number contains video as well, so you can look for the correct content.

 

I tested using the following utility with a 4GB, 1080p file, and time gave me the following output reporting about 14 seconds to analyze.

 

Top reported about 60% CPU utilization, which is nothing compared to using ffprobe. This was tested on my laptop with i5-3340 @ 2.7GHz, Linux Mint with content on an SSD.

 
real 0m13.951s
user 0m5.674s
sys 0m3.454s
 
I assume if you could summarize just mkv and mp4 container types, you would probably cover the majority of content you are trying to direct play.
 
Cheers,
Steve
Link to comment
Share on other sites

that's interesting. good stuff. so it doesn't handle mp4? if we were to demux the raw h264 stream using ffmpeg, could that be fed into mkvtoolnix via a pipe over stdin?

Link to comment
Share on other sites

stevewalison

Unfortunately, the tool is just for mkv's. If the file doesn't look like an mkv, it complains.

 

I don't think it would work directly off of an h264 stream, I took a quick peek at the matroska spec ( section 6.1 ), and I think the tool is getting the keyframe indication from the matroska simple blocks. There is a 'Flag' value which indicates whether the block is a keyframe, lacing or discardable.

 

So it looks like the information is available in the matroska container without having to decode the h264 stream which is why it's running quickly.

 

The tool is open source, but I haven't looked at the code to verify my assumption, so I may be wrong. But looking at the unfiltered output, mkvinfo seems to just be dumping container information.

 

| + SimpleBlock (track number 1, 1 frame(s), timecode 106.773s = 00:01:46.773)
|  + Frame with size 103258
| + SimpleBlock (discardable, track number 1, 1 frame(s), timecode 106.690s = 00:01:46.690)
|  + Frame with size 50781
| + SimpleBlock (discardable, track number 1, 1 frame(s), timecode 106.607s = 00:01:46.607)
|  + Frame with size 48421
| + SimpleBlock (track number 1, 1 frame(s), timecode 106.648s = 00:01:46.648)
|  + Frame with size 39816
| + SimpleBlock (track number 1, 1 frame(s), timecode 106.732s = 00:01:46.732)
|  + Frame with size 50434
| + SimpleBlock (key, track number 2, 1 frame(s), timecode 106.976s = 00:01:46.976)
|  + Frame with size 2560
| + SimpleBlock (track number 1, 1 frame(s), timecode 106.982s = 00:01:46.982)
|  + Frame with size 112761
| + SimpleBlock (discardable, track number 1, 1 frame(s), timecode 106.899s = 00:01:46.899)
|  + Frame with size 51763
| + SimpleBlock (discardable, track number 1, 1 frame(s), timecode 106.815s = 00:01:46.815)
|  + Frame with size 49443
|+ Cluster
| + Cluster timecode: 106.857s
| + SimpleBlock (track number 1, 1 frame(s), timecode 106.857s = 00:01:46.857)
|  + Frame with size 49607
| + SimpleBlock (track number 1, 1 frame(s), timecode 106.940s = 00:01:46.940)
|  + Frame with size 51680
| + SimpleBlock (key, track number 2, 8 frame(s), timecode 107.008s = 00:01:47.008)
|  + Frame with size 2560
|  + Frame with size 2560
|  + Frame with size 2560
|  + Frame with size 2560
|  + Frame with size 2560
|  + Frame with size 2560
|  + Frame with size 2560
|  + Frame with size 2560
| + SimpleBlock (track number 1, 1 frame(s), timecode 107.191s = 00:01:47.191)
|  + Frame with size 114338
| + SimpleBlock (discardable, track number 1, 1 frame(s), timecode 107.107s = 00:01:47.107)
|  + Frame with size 57432
| + SimpleBlock (discardable, track number 1, 1 frame(s), timecode 107.024s = 00:01:47.024)

 

Cheers,

Steve

Link to comment
Share on other sites

stevewalison

Ok, I found a better way using ffprobe thanks to this post on Doom 9 (http://forum.doom9.org/archive/index.php/t-163553.html). Works on both mp4 and mkv's, and is just as quick as mkvinfo.

 

Same file as before, but instead of using mkvinfo, I am using the following command:
 
$ ffprobe -show_packets -print_format compact -select_streams v:0 -show_entries packet=flags -show_entries packet=pts_time <video.mkv/video.mp4> | grep -n flags=K
 
1:packet|pts_time=0.000000|flags=K
251:packet|pts_time=10.427000|flags=K
500:packet|pts_time=20.812000|flags=K
750:packet|pts_time=31.240000|flags=K
829:packet|pts_time=34.535000|flags=K
1079:packet|pts_time=44.962000|flags=K
1232:packet|pts_time=51.343000|flags=K
1327:packet|pts_time=55.305000|flags=K
1392:packet|pts_time=58.016000|flags=K
1615:packet|pts_time=67.317000|flags=K
...
 
As you can see, all of the times are the same as above. Also seems to run much faster as well, and use between 40% and 50% of my CPU during execution.
 
Video 1: matroska, 3.9GB, 47 minutes, 1080p
real 0m5.769s
user 0m2.253s
sys 0m1.503s
 
Video 2: mp4, 1,6GB, 1hour 50minutes, 1920x800.
real 0m3.402s
user 0m2.394s
sys 0m0.956s
 
Hopefully that is more helpful and reduces the amount of churn required.
 
Cheers,
Steve
Link to comment
Share on other sites

that's excellent, thanks. only thing to figure out there is grep on windows

Link to comment
Share on other sites

Latchmor

that's excellent, thanks. only thing to figure out there is grep on windows

 

ffprobe -show_packets -print_format compact -select_streams v:0 -show_entries packet=flags -show_entries packet=pts_time video.mkv | findstr "flags=K"

 

Seems to work  :)

Link to comment
Share on other sites

stevewalison

Awesome, glad I could be of help.

 

Let me know if you want any extra help testing the feature, or have any investigations you want to offload.

 

Cheers,

Steve

Link to comment
Share on other sites

will grep always be available on linux and mac? we need to know that before it can be used.

Link to comment
Share on other sites

stevewalison

The tool grep is included in every desktop linux distribution. It's one of the core utilities for all unix and unix like operating systems.

  • Like 1
Link to comment
Share on other sites

  • 3 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...