Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apple iDevices do not display mp4 videos when served without special treatment #304

Closed
LaurentMarquet opened this issue Feb 15, 2017 · 51 comments
Assignees
Labels

Comments

@LaurentMarquet
Copy link
Contributor

Q A
Bug? no
New Feature? no
Version Used Not related to
FFmpeg Version Not related to
OS Not related to

According to http://www.iphonefaq.org/archives/97961 (I've also found this information in other places) videos have to be encoded with H.264 video up to 720p, 30 fps and audio: AAC-LC up to 160 Kbps, 48kHz, stereo, to be viewable from iDevices.

When I encode the video using x264 + libfdk_aac and gets information from VLC, I have for audio MPEG AAC Audio (mp4a) / 44100 Hz / 16 bits / 1411 kbit/s.

It seems that MPEG AAC and AAC-LC are the same but it looks like the KiloBitrate is too much.
I've found an option to set it when saving only the audio, but can't find one when saving the video.
Is there one ?

Is there a specific configuration to be able to save video to be viewable from iDevices ?
Thanks

@Romain
Copy link
Member

Romain commented Feb 15, 2017

Hi @Laurent3170,
There is currently no option to set the KiloBitrate option in the X264 interface.
Have you tried to use another format, like MP3 instead of AAC?

$format->setAudioCodec("libmp3lame");

As I'm not an expert on iOS formats, I don't know any specific configuration for these devices.

@LaurentMarquet
Copy link
Contributor Author

Hi @Romain, thanks for your answer.
Yes I was using libmp3lame before libfdk_aac. I have changed to libfdk_aac because of the link above specifying that AAC-LC is needed.
I have also tried with aac, libvo_aacenc and libfaac, but the result is the same: not able to view the video on iDevices...

@LaurentMarquet
Copy link
Contributor Author

Do you know where I can find help ? I don't think I'm alone to have the need to have videos viewable on iDevices ;-)

@Romain
Copy link
Member

Romain commented Feb 16, 2017

I'm unsure.
The best way would be for you to figure out what the ffmpeg command should be and post it here, so we can consider modifying the bundle to either create a new feature for iDevices or simply give you the ability to change the KiloBitrate for videos.

@LaurentMarquet
Copy link
Contributor Author

Thanks. The thing is that I don't know what my command line should look like... But I'll make investigations.

@LaurentMarquet
Copy link
Contributor Author

I have found https://korben.info/ffmpeg-pour-les-nuls.html (in french) that specifies the command line for iPhone ffmpeg -i source_video.avi input -acodec aac -ab 128kb -vcodec mpeg4 -b 1200kb -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -s 320x180 -title X final_video.mp4. I have tested it but I have errors and for the time being it's still chinese for me as I have to know what means every flag...
If it talks to you... I will continue investigations but next week, as children are on holidays.

@Romain
Copy link
Member

Romain commented Feb 20, 2017

Hi @Laurent3170,
Thanks for this follow-up.
Feel free to paste any error here, so we can help with it.

@LaurentMarquet
Copy link
Contributor Author

LaurentMarquet commented Feb 28, 2017

I've made some tests. By using ffmpeg -i video-master.mov -c:v mpeg4 -r 24 -b:v 160k -c:a aac -b:a 160k -strict -2 -s 640x400 video.mp4 I should have an audio bitrate of 160kbps but I obtain 1411kbps...
I've tried to lower the -b:a value but the result is still 1411 kbps.
Maybe it's due to the fact that I use a quite old version ffmpeg version 2.8.11-0ubuntu0.16.04.1. I'm trying to install a newer version but its' not that easy...

@jens1o
Copy link
Member

jens1o commented Feb 28, 2017

Hi @Laurent3170,
There is currently no option to set the KiloBitrate option in the X264 interface.

May I create a PR? I'd searched after this, but didn't found anything.

@Romain
Copy link
Member

Romain commented Mar 1, 2017

The function is present in the DefaultVideo class, but that's right, not in the H264 one.
Yes a PR would be helpful. Thanks!

@jens1o
Copy link
Member

jens1o commented Mar 1, 2017

@Romain But it's used in the examples? And since it's just extending, why it doesn't inherit these functions? https://github.com/PHP-FFMpeg/PHP-FFMpeg#transcoding

@Romain
Copy link
Member

Romain commented Mar 2, 2017

@jens1o You're right, I answered a bit fast.

class X264 extends DefaultVideo

No need to recreate one. The example is valid.

When @Laurent3170 says:

By using ffmpeg -i video-master.mov -c:v mpeg4 -r 24 -b:v 160k -c:a aac -b:a 160k -strict -2 -s 640x400 video.mp4 I should have an audio bitrate of 160kbps but I obtain 1411kbps...

This is not related to the bundle but to FFMPEG itself. I think you should make sure your version of FFMPEG is working properly, @Laurent3170 .

@LaurentMarquet
Copy link
Contributor Author

I have updated to ffmpeg version 3.2.4-1~16.04.york1 on my Ubuntu computer but the result with the ffmpeg command line is till the same... Don't know how to do better.

I have also tried

$video
    ->filters()
    ...
    ->setKiloBitrate(128)
    ...

$video
    ->save(new X264('libfdk_aac'), 'filenameMp4');

But I receive this error message Attempted to call an undefined method named "setKiloBitrate" of class "FFMpeg\Filters\Video\VideoFilters"

@Romain
Copy link
Member

Romain commented Mar 2, 2017

@Laurent3170, the setKiloBitrate function must be applied on a format, not on the video... ;)
See the example provided by @jens1o

@LaurentMarquet
Copy link
Contributor Author

Ah... It works better like this! I still can't read the video on iDevices, but now I can search and "play" with data. I'll let you know.
Additionally, I've found extra-spaces in the examples, I've made a PR (#318) to correct them.

@Romain
Copy link
Member

Romain commented Mar 3, 2017

Hehe, sure it's better... ;o)
Thanks for the PR, I'll look at it!

@LaurentMarquet
Copy link
Contributor Author

So, I'm still fighting with videos...
I have a video that works on iPhone (but not made by me): https://edlo.eu/images/participatif.mp4
and the other one still not working: https://edlo.eu/6gkfwk/img/video.mp4.
They are coded both using video: H264 - MPEG-4 AVC (part 10) (avc1) and audio: MPEG AAC Audio (mp4a) 44100 Hz.
I don't know what to do...
If you can help, it would be greatly appreciated!

@Romain
Copy link
Member

Romain commented Mar 20, 2017

Hi @Laurent3170,
I'm not an expert on iPhone encoding...
I'll check if I can find some help on this...

@Romain
Copy link
Member

Romain commented Mar 20, 2017

I asked @FranckHAEGELI, who thinks that it may come from number of frame per seconds (30 when iOS only accept 23,976 , 25 or 29,97, but it's not sure that this is the problem...

@LaurentMarquet
Copy link
Contributor Author

Thanks for the search. Unfortunately it doesn't work. My framerate was 24. I have tested with 23,976, 25 and 29,97, but it's the same, I can't read on a iPhone. I think it's related to the sound codec, but apart with VLC, I don't know how to have all the encoding's details of a video, to check the difference between the two videos I've cited above. I've googled a while ago but it was not giving a "good" result, I'll try again later.
Thanks again

@LaurentMarquet
Copy link
Contributor Author

I've found mediainfo which gave me information about the two video above that I have compared. Here is the result (just the meaningful differences):

Video Ok = not encoded via this library
Video Nok = video encoded via this library with this code:

$framerate = new FrameRate(25);
$gop = 48;
$maximumTime = 90;
//Calculation to define $finalWidth, $finalHeight and $angle
$dimensions = new Dimension($finalWidth, $finalHeight);

$ffmpeg = FFMpeg::create(array(
    'timeout' => $timeOut - 10,
    'ffmpeg.threads' => 5,
));
$video = $ffmpeg->open($file);

//Resizes video
$video
    ->filters()
    ->clip(TimeCode::fromSeconds(0), TimeCode::fromSeconds($maximumTime))
    ->framerate($framerate, $gop)
    ->addMetadata(['title' => 'Title'])
    ->rotate($angle)
    ->resize($dimensions, $resizeMode, true)
    ->watermark(__DIR__.'/../XXX.png', array(
        'position' => 'relative',
        'bottom' => 50,
        'right' => 50,
        ))
    ->synchronize();

//Defines format
$format = new X264();
$format
    ->setAudioCodec('aac')
    ->setKiloBitrate(1200)
    ->setAudioChannels(2)
    ->setAudioKiloBitrate(126);

//Saves video
$video->save($format, $filenameMp4);

Result
Object | Video Ok | Video Nok
isStreamable | Yes | No
Codec profile | Main@L4 | High@L2.1
Codec settings | CABAC / 1 Ref Frames | CABAC / 6 Ref Frames

So, the main thing is that the video seems to be NOT streamable which may explain why I can't read, and the other about the Codec don't talk to me.
I'll investigate about the streamable part.

@Romain
Copy link
Member

Romain commented Mar 24, 2017

This definitely looks like an encoding problem.
Thanks for investigating on this and sharing your results, it's really nice to you.

@jens1o
Copy link
Member

jens1o commented Mar 28, 2017

I think I'd might got it.

https://trac.ffmpeg.org/wiki/Encode/H.264#Compatibility

Use simple filters to get what you want 😉

But I'm already making a pr for this.

@jens1o jens1o mentioned this issue Mar 28, 2017
3 tasks
@LaurentMarquet
Copy link
Contributor Author

LaurentMarquet commented Mar 28, 2017

Really thanks for your work!
I have tested with the default value (main & 3.1) which should be ok for iPhone 4s but it still doesn't work :-(
The format seems to be well coded as, when I do a dump, I obtain

FFMpeg\Format\Video\X264 {#1138
  -bframesSupport: true
  -passes: 2
  #videoCodec: "libx264"
  #kiloBitrate: 1200
  #modulus: 16
  -profile: "main"
  -level: 3.1
  #additionalParamaters: null
  #audioCodec: "aac"
  #audioKiloBitrate: 126
  #audioChannels: 2
  #listeners: []
}

I have also tried

$format
    ->setAudioCodec('aac')
    ->setKiloBitrate(1200)
    ->setAudioChannels(2)
    ->setAudioKiloBitrate(126)
    ->setProfile(Profile::HIGH)
    ->setLevel(4.1);

But then I obtain this error (but here it may be due to a bad copy/paste, event if I've checked)

[Symfony\Component\Debug\Exception\FatalThrowableError]  
Call to a member function setLevel() on null 

@jens1o
Copy link
Member

jens1o commented Mar 29, 2017

Oh. Thanks for searching this, there is a mistake... You'd found a bug!

@LaurentMarquet
Copy link
Contributor Author

Finding a bug (without knowing it) is not the main part of the problem ;-)
Thanks to you!

@Romain
Copy link
Member

Romain commented Apr 12, 2017

@Laurent3170 could you paste here the complete logs so we can figure out where the error resides?

@LaurentMarquet
Copy link
Contributor Author

@Romain The only logs I can produce are the ones produced by the Command I use to resize the videos, which are not useful as they only bring the error mentioned above. Can you explain me how to produce the logs you wanna see?

@Romain
Copy link
Member

Romain commented Apr 25, 2017

Ok, I misunderstood your previous message, you already pasted the logs you had.

Can you confirm that you're using your PR to do this:

$format
    ->setProfile(Profile::HIGH)
    ->setLevel(4.1);

?

Why is $format NULL? Haven't you this before?
$format = new X264();

@LaurentMarquet
Copy link
Contributor Author

@Romain Yes I have copied/pasted the changes from #335 on my computer and then run the script.
When I don't use setProfile and setLevel the video is encoded but not working on iDevices and when I use those functions the format is set as null, but There are some changes on #335 since my message above, that I haven't tested yet.

@LaurentMarquet
Copy link
Contributor Author

The changes were just about the test. So the result is still the same for me, not being displayed oniPhone 4s and $format = null

@dmanthing
Copy link

You must create format first then set level and profile last. Also i use baseline and level 3.0

@LaurentMarquet
Copy link
Contributor Author

LaurentMarquet commented Apr 27, 2017

Yes, I do

$format = new X264();
$format
    ->setAudioCodec('aac')
    ->setKiloBitrate(1200)
    ->setAudioChannels(2)
    ->setAudioKiloBitrate(126)
    ->setProfile(Profile::HIGH)
    ->setLevel(4.1);

But like this I have an error

@jens1o
Copy link
Member

jens1o commented Apr 27, 2017

he uses baseline and level 3.0, you're using profile high and level 4.1

@LaurentMarquet
Copy link
Contributor Author

Yes this was due to a bad copy/paste from the code above. But I have tried with baseline and 3.0, the $format is not null so this error has disappeared :-) the video is well encoded, but I still can't read the video https://edlo.eu/6gkfwk/img/video.mp4.

@LaurentMarquet
Copy link
Contributor Author

Any news ? I've tried with an iPhone 6 and still the video is not readable...

@jens1o
Copy link
Member

jens1o commented Jul 2, 2017

I'm afraid, but I do not have any experiences with iOS. :/

@discoveryjames
Copy link

I resolved this issue. I have attached the code for you.

$format = new FFMpeg\Format\Video\X264();
if ($format instanceof VideoInterface) {
	if (null !== $format->getVideoCodec()) {
		$filters->add(new SimpleFilter(['-vprofile', 'baseline']));
		$filters->add(new SimpleFilter(['-level', 3.0]));
	}
}

@LaurentMarquet
Copy link
Contributor Author

@discoveryjames thanks for your answer but what is the value of $filters?

@discoveryjames
Copy link

I would also recommend using the faststart flag too, so that the whole file doesn't have to download before it starts playing.

$format = new FFMpeg\Format\Video\X264();
if ($format instanceof VideoInterface) {
	if (null !== $format->getVideoCodec()) {
		 $filters->add(new SimpleFilter(['-vprofile', 'baseline']));
		$filters->add(new SimpleFilter(['-level', 3.0]));
		$filters->add(new SimpleFilter(['-movflags', '+faststart']));
	}
}

@LaurentMarquet
Copy link
Contributor Author

Thanks for that adding (didn't know about faststart) but what about $filters value (see my question above)?

@discoveryjames
Copy link

Sorry for the confusion. I have rewritten the code anyway, I'm using the line below. This is using a php7 array, but if you're not using php7 you can just use array() instead of []

$format = new FFMpeg\Format\Video\X264();
$format->setAdditionalParameters(['-vprofile', 'baseline', '-level', 3.0, '-movflags', '+faststart']);

@LaurentMarquet
Copy link
Contributor Author

So, I have tried using your code but I have an encoding failed error

'/usr/bin/avconv' '-y' '-i' '/.../video-master.mov' '-metadata:s:v:0' 'rotate=0' '-async' '1' '-metadata:s:v:0' 'start_time=0' '-metadata' 'title=Title' '-ss' '0  
  0:00:00.00' '-t' '00:01:30.00' '-r' '29.97' '-b_strategy' '1' '-bf' '3' '-g' '48' '-threads' '4' '-vcodec' 'libx264' '-acodec' 'libfaac' '-b:v' '1000k' '-refs' '6' '-coder' '1' '-sc_threshold' '40' '-flags' '+loop' '-me_range' '16' '-subq' '7' '-i_qfactor' '0.71' '-  
  qcomp' '0.6' '-qdiff' '4' '-trellis' '1' '-b:a' '128k' '-vprofile' 'baseline' '-level' '3' '-movflags' '+faststart' '-vf' 'movie=/.../edlo.eu/src/AppBundle/Command/../../../web/images/stamp.png [watermark];[in]transpose=1[p0];[p0]scale=854:480 [p1];[p1]  
  [watermark] overlay=main_w - 50 - overlay_w:main_h - 50 - overlay_h [out]' '-pass' '1' '-passlogfile' '/tmp/ffmpeg-passes598c7977ac09554hoy/pass-598c7977ac17a' '/.../video.mp4'

So, I have also tried with

$format = new X264();
$format
    ->setPasses(1)
    ->setAudioCodec('aac')
    ->setKiloBitrate(1200)
    ->setAudioChannels(2)
    ->setAudioKiloBitrate(126)
    ->setAdditionalParameters(['-vprofile', 'baseline', '-level', 3.0, '-movflags', '+faststart'])
;

This one works and is currently in https://edlo.eu/6gkfwk/img/video.mp4, but it doesn't display on my iPhone 6... But well displayed on Firefox.

@remyzv
Copy link

remyzv commented May 20, 2019

Hi @LaurentMarquet

Did you find a way to encode your videos for iOS support ? I absolutly need it.

Thanks !

@LaurentMarquet
Copy link
Contributor Author

No I didn't find a way...

@j-dawg
Copy link

j-dawg commented May 21, 2019

I got it working using the following

$ffmpeg = FFMpeg\FFMpeg::create([
		    'ffmpeg.binaries'  => '/usr/bin/ffmpeg',
		    'ffprobe.binaries' => '/usr/bin/ffprobe',
		    'timeout'          => 3600,
		    'ffmpeg.threads'   => 12,
		]);
		
		$format = new FFMpeg\Format\Video\X264();
		$format->setAdditionalParameters(['-vprofile', 'baseline', '-level', 3.0, '-movflags', '+faststart']);

@jens1o jens1o added the bug label May 25, 2019
@jens1o jens1o self-assigned this May 25, 2019
@LaurentMarquet
Copy link
Contributor Author

Thanks for your answer! I have tried (after a long-time not working on the project) and I can display it on iPhone! The settings are the same as the ones I've tried with in August 2017, but now it works. But maybe it was already the case in 2017, because (today) I saw that when I access the file directly, I can see it, but when I use a Symfony controller that return the file content + mime type it doesn't show, so maybe the problem was from here... I don't know.
Thanks to all for your time, trials, etc. !
I can now close this issue :)

@LaurentMarquet
Copy link
Contributor Author

So it's confirmed... The problem was not only with the settings of the saved video but also with the Response sent by Symfony (in fact even with PHP directly it doesn't work).
Using Response + file_get_contents() doesn't work on iDevice, but using Symfony\Component\HttpFoundation\BinaryFileResponse works and displays the video correctly.

We still have to use special settings to format videos for iDevices.

@jens1o jens1o changed the title iDevices does not display mp4 videos Apple iDevices do not display mp4 videos when served without special treatment Jan 4, 2020
@jens1o
Copy link
Member

jens1o commented Jan 4, 2020

updated the title for more clarity

@atastycookie
Copy link

still actual :)

@Romain
Copy link
Member

Romain commented Jan 4, 2021

@atastycookie could you open a new issue for you own one?
Just to keep it posted somewhere, I recently solved a similar issue for a client that couldn't read H264 videos on her iPhone by converting the video to H264 AND WebM and serving both files with VideoJS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants