Angular.js: ngSrc doesn't work properly with HTML5 Video Source tag

Created on 9 Sep 2012  ·  78Comments  ·  Source: angular/angular.js

In firefox, ngSrc directive on video elements' tag doesn't work at all, and results in unsupported video format error. In chrome, updating ngSrc with data-binding doesn't update video, as it only loads video on page-load. i.e.

<video controls>
     <source ng-src="{{src}}">
</video>

doesn't work in Firefox at all, and in chrome it only works on first loading.

However,

<video ng-src="{{src}} controls></video>

works in both browsers, and it can be updated dynamically with no problems.

This is a problem, when having multiple video formats in order to support all browsers.

Related: #339

misc core moderate investigation broken expected use bug

Most helpful comment

Fixed with a custom directive

<source ng-repeat="source in sources" vsrc="{{ source.path }}" type="{{ source.type }}" html5vfix>


//Html5 video fix
eshop.directive('html5vfix', function() {
    return {
        restrict: 'A',
        link: function(scope, element, attr) {
            attr.$set('src', attr.vsrc);
        }
    }
});

All 78 comments

Im having an issue as well, however I feel that the issue resides in Angulars ng-repeat.
If i remove the video from that scope it plays fine, otherwise none of the controls will even load.

me too.

me too! help me

+1

I'm using ng-repeat to set ng-src sources and they load correctly on page load, but if the video gets hidden via ng-show and then shown again the sources don't reload (in Chrome).

I can confirm sourcec0de's observation - if I remove the ng-repeat and use a single ng-src source (or multiple ng-src sources) the video reloads correctly upon re-showing.

I'm having the same problem. Is anyone currently looking into this?

With or without ng-src, with or without ng-repeat, chrome repeatedly fails to reload an HTML5 video after navigating away then coming back to the page.

From a clean cache, I see 2 requests (first one Pending), second with 206 Partial Content. If I refresh, I see one request with 304 Not Modified. When I navigate away from the video view then return (either through a link or using back button), I get two requests for the video with just a Pending status. One of these will have a mime-type video/mp4 (correct), the other just says Pending.

Is anyone even looking at this issue???

Can you provide a running example of this issue?
Does it work on some browsers at all?
Have you checked whether browsers support changing the src attribute on HTML5 video elements after rendering?

Sorry, the example app is not published yet.
Works fine in Safari.
As indicated above, I used both ng-src directive and a hard-coded src attribute with the same results.

What happens if you change the src with straight jQuery?
I wonder if this is actually a bug in the browser rather than AngularJS?

On 16 July 2013 22:31, Paul Grenier [email protected] wrote:

Sorry, the example app is not published yet.
Works fine in Safari.
As indicated above, I used both ng-src directive and a hard-coded src
attribute with the same results.


Reply to this email directly or view it on GitHubhttps://github.com/angular/angular.js/issues/1352#issuecomment-21075654
.

Even if I'm not changing the src (e.g., hard-code the video src attribute), the issue occurs. I don't have a way to simulate the problem away from Angular since Angular controls the content. I agree, it may be simply a problem in Chrome but I'm not sure how to test that.

What do you mean that Angular controls the content?

The problem occurs when, using Angular's controller, the content changes, loading another view. Using the back button or a link to the previous route displays the former content but the video fails to load--every time. If it's an Angular issue, it's probably only in Chrome. My guess is that Chrome's aggressive caching and Angular's method for changing views can't agree on what should be loaded.

@AutoSponge my test case was:
page 1 with a button to go to page 2
page 2 with a videojs player and a button to go back to page 1

navigate to page 1, then to page 2, video is loaded correctly.
click on button to go back to page 1, then go to page 2, google chrome stops using videojs. this happens because the videojs object was not removed when angular removed the element from the DOM, as expected. this isn't an angularjs (yay!)

Remove it before initializing the videojs player. I do this because I have a directive that adds a player. For example, if used in a directive:

... directive('mydir', ... {
    var vp;
    return {
        link: ..{ 
            if (vp) vp.dispose();
            vp = videojs("mp4video");
        }
    };`

Just like the OP, I'm not using a wrapper object. The video is loaded through an HTML5 video tag. Are you suggesting that should be removed separately?

well, videoJS keeps track of players added to the DOM but it looks like it is not watching changes. If you remove the element from the DOM (e.g. because you navigate to another route), videoJS won't know and it won't initialize it again. see line 17: https://github.com/videojs/video.js/blob/master/src/js/core.js
if you remove it outside of videoJS, I guess you should call the dispose() method of the player. I guess this works like when you wrap a jquery plugin (?). This is what makes me think that it isn't an angular issue.

having this problems as well.
loading AV files to src or ng-src doesn`t do the job. need to write directives or factories for it to work.
http://stackoverflow.com/questions/15485768/changing-html5s-source-src-attribute-takes-no-effect-wtih-angularjs

I'm having this problem as well.

Video fails to load in Chrome, Firefox and Safari. It works, however, in IE.

Here's a work around:http://stackoverflow.com/questions/15728424/html5-video-is-not-working-with-angularjs-ng-src-tag

It creates a messy flicker on page load, but it works.

+1

a workaround is as follows:

in your controller set a function in my case is like this:

    $scope.play = function(who) {
        var name = who.id.split('.')[0];
        $scope.audio.mp3 = name + '.mp3';
        $scope.audio.ogg = name + '.ogg';
        $scope.audio.play = 'views/audio.html?'+name;
    };

then in my case in my template i had the following:

<div ng-include src="audio.play"></div>

my template in views/audio.html looks as follows:

<audio ng-model="audio" controls autoplay >
    <source ng-src="{{audio.mp3}}" type='audio/mp3'></source>
    <source ng-src="{{audio.ogg}}" type='audio/ogg'></source>
</audio>

if $scope.audio.play does not change then no reload will happen, so i'm using the name of the file, this could just be a random number instead

@caitp I thought you had a PR for this, is my memory failing me?

@IgorMinar I was going to work on it, but I realized that I couldn't reproduce the problem (see http://jsfiddle.net/J77gE/). I thought it would be fun to build a media helper module instead outside of core with more binding options.

But as far as I can tell, ng-src works fine for source tags in modern browsers and modern angular

The problem I mentioned was specifically due to changing views and using the back button. I'm not sure how that jsfiddle attempts to recreate the problem.

It doesn't, the point is that we don't have an issue interpolating the source tags, I think this is not the issue. I haven't been able to reproduce your particular issue at all

@AutoSponge can you provide a demo app and instructions on how to reproduce this please?

http://jsfiddle.net/AutoSponge/Yh9en/

Click the Video link. It will load the first time. Click Home and then Video again. Within 1 or 2 clicks it will probably stop loading in Chrome.

(and don't judge, I threw this together based on an app from 8 months ago!)

@AutoSponge this isn't an Angular problem. It's a weird behaviour of Chrome, but it is still interpolating your source file and getting the right src. It seems to be an issue with caching, which is a bit unfortunate. (you have this issue with ngIf as well, but this can be circumvented by using ngShow + setting the volume to 0)

I agree it's an unfortunate issue with Chrome. I think that's the main reason this issue stayed open for 9 months with little work on it. Since Chrome is not likely to fix it, do you think it's unreasonable to create a cache-busting option? I'm thinking it would just append a random string to the src.

I also thought it might have something to do with pushState--either the implementation in Chrome or the use of it in Angular. But I honestly thought it would have been fixed by now.

I feel like this particular issue probably belongs on the chromium issue tracker (I think there are a couple of HTMLMediaElement cache-related issues already open, so it would be worth talking about it there).

I can't recall, but it's possible that this is the expected behaviour defined in the spec (and it wouldn't be the first time that the spec asked for weird/unfortunate behaviours)

This may be separate from the issue I've been having, but I had an issue where

Hope that was somewhat intelligible and related!

Just found out some workaround. It works for chrome, but not for another browsers (in some cases, time after time, etc) :)
Last comment in
http://stackoverflow.com/questions/16137381/html5-video-element-request-stay-pending-forever-on-chrome

As another note, the HTML5 spec notes that dynamically modifying the src attribute of a tag will have no effect, but doing the same on the src attribute of an

After a bit of googling I found that this is most probably related to chrome waiting for available sockets not to do angular, and that adding preload="none" to the video tag seemed to fix the issue.

workaround

in controller

$scope.mp3 = "http://download.jw.org/audio.mp3"

$scope.$watch('mp3', function() {
       $("audio").attr("src",$scope.mp3)
   });

html:

<audio id="mejs" type="audio/mp3" controls="controls"></audio>

@agliottone your suggested workaround uses bad practices. You should never be writing to the DOM from a controller like that. Consider using a custom directive.

@btford I agree but..
Extremis malis, extrema remedia

+1

I think each time a new source has been set, you must call .load() on the native video element again to make the new video source show up.

Hi I have a problem with angularJS on Google Chrome when I load the page in first time the HTML 5 Video load but when I change the view with ng-view it doesn't load but in firefox an internet explorer it's works very well.

I don't know what I have to do about it!

This look like a similar issue to <embed> and ngSrc. See #339

This neeeeeeds to be fixedddddddd :(

I have written a plnkr
click the video, the video loads. Click other link then back to video. The video is pending. You can see the requests in chrome network. Two request, one is the previous, still getting. Another is new one, pending.

@kaiqigong I got it working last week by setting the URL as trusted for the video since the video was not local to the website.

http://plnkr.co/edit/CL0Lh6VGMy0M3mR1SvVA?p=preview

I forked your plnkr and modified it to set the URL and trust it:

   .controller('VideoController', function($scope, $sce, $routeParams){
        $scope.name = "VideoController";
        $scope.params = {
          videoUrl: $sce.trustAsResourceUrl("http://www.videogular.com/assets/videos/videogular.mp4")
        };
   })

There's a delay on the video but it's working for me.

@phillip-haydon thanks for you reply.
I tried your plnkr, but it is still not working properly. (Try quickly switch between links.)
What I found is, when we remove the video tag(by changing route or some other DOM operations), the browser is still pulling the video source. When we switch back to the video. Another video request is made which makes the network traffic jammed.
If I set the src of video to '' before removing the video tag, the browser will stop pulling, which solve the problem. Please try this codepen: http://codepen.io/cagegong/pen/bJHAz

So my final solution:

$scope.$on '$destroy', () ->
  angular.element('video').attr 'src', ''

Yeah mine works, just not 100%, why the browser doesn't kill the request itself when the dom element requesting it is destroyed I have no idea. I don't believe this is something Angular itself should be required to handle.

It's interesting to see that setting it to empty string does kill the request tho, I might have to setup something to add that in my project. Thanks.

@jharaujoads
Hope this solves your problem

:+1:

Wow, two years and nothing. Is angular being maintained at all?

Angular isn't worth the time and effort. They already said "fuck you" to the community with Angular 2.0.

@IDontEatSoap I'm not sure why I you bringing this question, just check the list of commits and releases. Given a new release every week and few commits _a day_, including weekends, it looks like pretty much maintained to me...

@phillip-haydon please keep the conversational professional and focused on technical matters, you are violating our code of conduct.

@IDontEatSoap @phillip-haydon if you truly interested in this issue being addressed the best way of moving forward is to send a pull request with code changes that make the future work in all browsers.

And yet a simple video src tag isn't supported?

I've managed to get it working by exposing $sde and using src="{{$sce.trustAsResourceUrl(item.VideoUrl)}}"

Don't get me wrong, I'm really liking angular. But small things like video src and SVG tags not being supported and requiring workarounds is unexpected.

Keep up the good work :) :+1:

+1, impossible to add a livestream webcam with dynamic data.

@pkozlowski-opensource I'm no longer interested because I don't care for Angular now that you've ditched the community for V2. There's 0 point in wasting effort on it.

@phillip-haydon Thank you for that information. I was just looking into Frontend Frameworks to use with node.js. I had not heard about the V2 issue before. Me and my Team have decided to go with React.js due to how they are breaking compatibility between V1 and V2. You think the huge rift this type of thinking caused in the Python community would have taught everyone a lesson.

:disappointed:

@phillip-haydon and @StevenDStanton - I am sorry to hear that you have chosen to go with another framework but of course you must choose the tools that work best for your own development.

Just to be clear though, AngularJS has not ditched the community, in fact the community has been involved even more than ever in the on-going development of Angular 1.x. We are close to releasing AngularJS 1.4.0, which will be the shortest time between big version changes so far and has had more community members working on the core team that ever before. Once this version is released we will begin the development towards AngularJS 1.5, which should (all things being equal) be released well before the end of 2015. Remember also that Google, itself has literally hundreds of production use AngularJS 1.x applications in operation right now. It is in Google's interests to ensure that these applications, just the same as your applications, continue to have a life or a migration path.

Regarding Angular 2 - this development effort is precisely driven by feedback from the community. The aim is to provide a faster, smaller, more robust framework that will be able to support bigger more complex applications; to perform better on mobile devices; and to continue to embrace the evolution of web browsers with transparent support for Web Components.

To achieve this, it was not possible to continue to bolt things onto AngularJS 1.x, since we needed to rethink some of the fundamental building blocks, such as how the injector and compiler work. The result is a completely new framework that continues to push the same high level goals that made AngularJS as popular and reliable as it has become but also be able to provide a future proof path for years to come.

There may not be a turn-key upgrade solution to migrating from AngularJS 1.x to Angular 2, that is true. But it will certainly be easier to migrate from AngularJS 1.x to Angular 2 than it would be to migrate from AngularJS 1.x to a completely different framework.

It seems to me that the browser is not very good at watching the <source> element for changes to its src attribute. The Original Poster's issue can be worked around by using ng-if.

See http://plnkr.co/edit/rpiEg1ki7KXgD40zy8qV

I'm just using

        $timeout(function () {
            $("video source").attr("src", 'https:' + file.url);
            $("video").attr("src", 'https:' + file.url);
        }, 500);

@tot-ra - I believe your version works because the browser does watch the change to the src attribute on the <video> element as can be seen here: http://plnkr.co/edit/6dNmNjAvZ8b6t09mUE65

From the HTML5 spec: http://www.w3.org/TR/2014/REC-html5-20141028/embedded-content-0.html#the-source-element

Dynamically modifying a source element and its attribute when the element is already inserted in a video or audio element will have no effect. To change what is playing, just use the src attribute on the media element directly, possibly making use of the canPlayType() method to pick from amongst available resources. Generally, manipulating source elements manually after the document has been parsed is an unnecessarily complicated approach.

What this means is that you cannot expect the browser to cope with AngularJS (or anything else for that matter) changing the source elements dynamically. Instead, what we need is some media specific directives that will deal with updating the <video> tag's src property when appropriate. Perhaps something along the lines of @caitp's ng-media library.


Regarding the problem with the request for a video not being cancelled, I suggest that we could implement something like @kaiqigong's idea in https://github.com/angular/angular.js/issues/1352#issuecomment-58865425. Perhaps we could add a video element directive that sets its src to "" when it's element is being destroyed?

Other potential libraries that could be tried:

Unsubscribe. Leaving AngularJS for React. Sorry guys but thanks for your hard work.

Yeah good idea, I'm unsubscribing too. I've ditched AngularJS for http://aurelia.io/

So back to the technical issue at hand:

There are actually two problems being discussed in this issue:

  • The <video> element does not monitor nor react to changes to the src attribute on <source> tags. This is part of the HTML5 spec and so is unlikely to change in the near future. This means that we cannot use the idiomatic AngularJS way of dynamically specifying <source> elements, with or without ngSrc. In other words, we simply cannot use interpolation in the <source> tag's src attribute because the <video> element will not pick up the change.
  • In some cases (browsers) the <video> element does not cancel its request to download a video correctly then the element is removed from the DOM. The effect of this is that if you application changes the DOM quickly enough, adding and removing <video> tags, so that the video has not had time to download, you can get stuck in a situation where a new <video> element is trying to access a video that is still pending from a previously destroyed <video> element. It seems that the browser will in fact cancel the video request if the src (or currentSrc?) attribute is set to the empty string ("") before the element is destroyed.

Neither of these issues is a bug in AngularJS per-se. But that doesn't mean that we can't do something about it.

Regarding the first problem:
The only way to fix this, in Angular or any other framework, is to write JavaScript to deal with the dynamically changing sources, as described in the HTML5 spec. I have not seen a solution to this in any of the other major frameworks but in AngularJS the most attractive way is by building a set of directives. This is effectively what the people in the following projects have tried to do:

Depending upon community interest, we could look at develop something similar to this for Angular 1.5 and package it as its own module (similar to how ngCookies is packaged). Let's revisit this when doing the 1.5 planning. In the meantime, the best solution is to implement one of the libraries above and help them to make their library work for you.

Regarding the second problem:
I don't seem to be able to reproduce this personally at the moment. In my Chrome, it does indeed continue to stream the previous <video> elements video even after we have destroyed the element, which after some switching back and forth results in numerous requests running in parallel, but these requests do not seem to prevent the new <video> element's video from loading.

screen shot 2015-02-26 at 11 14 02

Here, you can see the multiple simultaneous video downloads.

I believe that this is really a bug in the browser, but in the meantime, you can workaround this by providing your own video directive (see http://plnkr.co/edit/QLMJd24rxvklr638e57Q?p=preview):

  .directive('video', function() {
    return {
      restrict: 'E',
      link: function(scope, element) {
        scope.$on('$destroy', function() {
          element.prop('src', '');
        });
      }
    };
  })

screen shot 2015-02-26 at 11 17 32

Here, you can see that the video download now stops when we navigate away from the video view.

For the time being, this is the recommended workaround for this problem.

@petebacondarwin ... From looking at the w3c spec, it seems that a user agent abort on the document will cause the stream load to abort also. Calling a window.stop() cancelled any further network streaming for me.

Ultimately, it's not the most ideal thing if you want other resources, but it provides some level of workaround.

@daleyjem thanks for this idea.
We can't do this in a core directive as it would stop other videos from streaming.

Just Create a Filter:
app.filter("trustUrl", ['$sce', function ($sce) {
return function (recordingUrl) {
return $sce.trustAsResourceUrl(recordingUrl);
};
}]);

In View File:
< audio src="{{Your_URL | trustUrl}}" audioplayer controls>

NOTE: mind the space in audio tag

Hi, I'm the creator of Videogular.

As @petebacondarwin explained, this is not an issue with AngularJS but mostly a problem of how video works on HTML5 and how the browsers deal with video requests. And it gets even more weird when you need to have it working on mobile devices.

I fought a lot with video and HTML5 with Videogular and I must say that is hard to get it working seamlessly across all browsers, but not impossible.

So, if anyone at the AngularJS team wants help on this I would be glad to share my humble knowledge on this and contribute to fix it. This is something that really annoys me and in Angular 2 I'm not having problems with this so it would be great to have a similar approach in both frameworks.

+1

If you have a directive on your video player, you can watch the src url variable and update it manually:

link: function (scope, element, attrs) {
    var video = element.find('video')[0];
    scope.$watch('source.url', function (val) {
        video.src = val;
    });
}

After a ton of trial and error I finally have video playing back reliably on Android 4.2.2, 4.3, 4.4.4, 5.0.0, and 5.1.0. Install Crosswalk and use Videogular

Fixed with a custom directive

<source ng-repeat="source in sources" vsrc="{{ source.path }}" type="{{ source.type }}" html5vfix>


//Html5 video fix
eshop.directive('html5vfix', function() {
    return {
        restrict: 'A',
        link: function(scope, element, attr) {
            attr.$set('src', attr.vsrc);
        }
    }
});

İ parse url video in db with json , but not work((( please help

when i click my play button the video displays upside down and when pressing fullscreen its getting to its position any suggestion to this issue

+1 there's no parsing the url be it with src or ngSrc.
Has this error been abandonned ?

I am having this same problem has there been a solution yet

Was this page helpful?
0 / 5 - 0 ratings