Diffuser de la vidéo pendant le téléchargement iOS
Je suis en utilisant iOS 7 et j'ai un .mp4 vidéo que j'ai besoin de télécharger dans mon application. La vidéo est importante (environ 1 GO) c'est pourquoi il n'est pas inclus dans le cadre de l'application. Je veux que l'utilisateur soit en mesure de commencer à regarder la vidéo dès que commence le téléchargement. Je veux aussi la vidéo pour pouvoir être mis en cache sur l'appareil iOS, de sorte que l'utilisateur n'a pas besoin de le télécharger à nouveau plus tard. À la fois les méthodes normales de la lecture de vidéos (téléchargement progressif et la diffusion en direct) ne semblent pas laisser de la cache de la vidéo, j'ai donc fait mon propre service web que les morceaux de mon fichier vidéo et le flux d'octets vers le client. J'ai commencer le streaming HTTP appel à l'aide de NSURLConnection:
self.request = [[NSMutableURLRequest alloc] initWithURL:self.url];
[self.request setTimeoutInterval:10]; //Expect data at least every 10 seconds
[self.request setHTTPMethod:@"GET"];
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:YES];
Lorsque je reçois un segment de données, je l'ajouter à la fin de la copie locale du fichier:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:[self videoFilePath]];
[handle truncateFileAtOffset:[handle seekToEndOfFile]];
[handle writeData:data];
}
Si je laisse l'appareil exécuté, le fichier est téléchargé avec succès et je peux lire à l'aide d'MPMoviePlayerViewController:
NSURL *url=[NSURL fileURLWithPath:self.videoFilePath];
MPMoviePlayerViewController *controller = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
controller.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[self presentMoviePlayerViewControllerAnimated:controller];
Cependant, si je commence le joueur avant que le fichier est téléchargé complètement, la vidéo commence à jouer très bien. Il a même la bonne longueur de la vidéo affichée en haut barre de défilement. Mais quand l'utilisateur arrive à la position dans la vidéo que j'avais terminé le téléchargement avant de lancé la vidéo, la vidéo se bloque. Si j'ai fermer et rouvrir le MPMoviePlayerViewController, puis la vidéo, jusqu'à ce qu'il arrive à l'emplacement que j'étais alors à quand j'ai lancé le MPMoviePlayerViewController de nouveau. Si j'attends jusqu'à ce que l'intégralité de la vidéo est téléchargée, la vidéo est lue sans problème.
Je ne reçois pas les événements déclenchés, ou des messages d'erreur affichés dans la console lorsque cela se produit (MPMoviePlayerPlaybackStateDidChangenotification et MPMoviePlayerPlaybackDidFinishNotification ne sont jamais envoyés après la vidéo commence). Il semble qu'il y est autre chose, c'est de dire que le contrôleur de ce que la longueur de la vidéo est autre que ce que le pointeur est à l'aide de...
Personne ne sait ce qui pourrait être à l'origine de ce problème? Je ne suis pas lié à l'utilisation de MPMoviePlayerViewController, donc si une autre vidéo de la lecture de la méthode de travail dans cette situation, je suis tout à fait pour.
Liées À Des Questions Non Résolues:
AVPlayer et Progressive des Téléchargements de Vidéo avec AVURLAssets
Progressive de Téléchargement de Vidéo sur iOS
Comment jouer une progression du téléchargement du fichier vidéo dans IOS
Mise à JOUR de 1
J'ai trouvé que la vidéo de décrochage est en effet en raison de la taille du fichier de la vidéo commence à jouer. Je peux contourner ce problème en créant un zéro-ed fichier avant de commencer le téléchargement et sur l'écraser comme je l'aller. Depuis que j'ai le contrôle sur le serveur vidéo en continu, j'ai ajouté un en-tête personnalisé donc je sais que la taille du fichier est transmis (par défaut la taille du fichier d'en-tête d'un fichier de transmission en continu est de -1). Je suis entrain de créer le fichier dans mon didReceiveResponse méthode comme suit:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//Retrieve the size of the file being streamed.
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSDictionary *headers = httpResponse.allHeaderFields;
NSNumberFormatter * formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
self.streamingFileSize = [formatter numberFromString:[headers objectForKey:@"StreamingFileSize"]];
//Check if we need to initialize the download file
if (![[NSFileManager defaultManager] fileExistsAtPath:self.path])
{
//Create the file being downloaded
[[NSData data] writeToFile:self.path atomically:YES];
//Allocate the size of the file we are going to download.
const char *cString = [self.path cStringUsingEncoding:NSASCIIStringEncoding];
int success = truncate(cString, self.streamingFileSize.longLongValue);
if (success != 0)
{
/* TODO: handle errors here. Probably not enough space... See 'man truncate' */
}
}
}
Cela fonctionne très bien, sauf que tronquer entraîne l'application à bloquer pendant environ 10 secondes et il crée l' ~1 GO de fichiers sur le disque (sur le simulateur, il est instantanée, que d'un véritable dispositif a ce problème). C'est là que je suis coincé maintenant personne ne sait de façon à allouer un fichier de manière plus efficace, ou une autre méthode pour obtenir le lecteur vidéo de reconnaître la taille du fichier sans avoir besoin de réellement les attribuer? Je sais que certains systèmes de fichiers de support "taille du fichier" et "taille sur le disque" comme deux propriétés différentes... ne sais pas si iOS a quelque chose comme ça?
- ne pourrais pas travailler avec moi
Vous devez vous connecter pour publier un commentaire.
J'ai trouvé comment faire cela, et il est beaucoup plus simple que mon idée de départ.
Tout d'abord, depuis ma vidéo est en .mp4, le MPMoviePlayerViewController ou AVPlayer classe peut jouer directement à partir d'un serveur web - je n'ai pas besoin de mettre en œuvre quelque chose de spécial et ils peuvent toujours chercher à n'importe quel point dans la vidéo. Ceci doit faire partie de la façon dont l' .mp4 encodage fonctionne avec les lecteurs vidéo. Alors, je viens d'avoir le fichier raw disponible sur le serveur - n ° spécial-têtes nécessaires.
Ensuite, lorsque l'utilisateur décide de jouer la vidéo, j'ai immédiatement commencer à lire la vidéo à partir de l'URL du serveur:
Ce qu'il fait en sorte que l'utilisateur peut regarder la vidéo et demander à n'importe quel endroit qu'ils veulent. Ensuite, j'ai commencer à télécharger le fichier manuellement à l'aide de NSURLConnection comme si j'avais fait ci-dessus, sauf que maintenant je ne suis pas streaming le fichier, je viens de le télécharger directement. De cette façon, je n'ai pas besoin de l'en-tête personnalisé depuis la taille du fichier est inclus dans la réponse HTTP.
Quand mon fond de téléchargement est terminé, je passe à la lecture de l'élément à partir de l'URL du serveur de fichier local. Ceci est important pour les performances du réseau, car le film les joueurs de télécharger quelques secondes à l'avance de ce que l'utilisateur regarde. Être en mesure de basculer vers le fichier local dès que possible est la clé pour éviter le téléchargement de trop de données en double:
Cette méthode a l'utilisateur de télécharger deux fichiers vidéo en même temps au début, mais les premiers tests sur le réseau des vitesses de mes utilisateurs vont utiliser le montre augmente le temps de téléchargement en quelques secondes. Fonctionne pour moi!
Tu dois créer un serveur qui agit comme un proxy! Ensuite, réglez votre lecteur pour lire le film à partir de l'hôte local.
Lors de l'utilisation du protocole HTTP pour lire une vidéo avec MPMoviePlayerViewController, la première chose que le joueur n'est à demander de l'octet-0-1 (les 2 premiers octets) juste pour obtenir le longueur du fichier. Ensuite, le joueur demande "morceaux" de la vidéo à l'aide de la "byte-range" HTTP commande (le but est d'économiser de la batterie).
Ce que vous avez à faire est de mettre en place ce serveur interne qui fournit de la vidéo pour le joueur, mais votre "proxy" doit tenir compte de la longueur de votre vidéo comme sur toute la longueur du fichier, même si le fichier n'a pas été complètement téléchargé à partir d'internet.
Alors vous vous définissez votre lecteur pour lire un film depuis " http://localhost : someport "
Ce que j'ai fait avant... ça fonctionne parfaitement!
Bonne chance!
CocoaHTTPServer
mais mes vidéos désordre jusqu'au bout de 3 secondes de lecture. Cheers!Je ne peux que supposer que le MPMoviePlayerViewController des caches de la longueur de fichier du fichier lorsque vous avez commencé.
La façon de fix (juste) cette question est de déterminer comment grand que le fichier est. Puis créer un fichier de cette longueur. En gardant un décalage du pointeur, comme les téléchargements de fichiers, vous pouvez remplacer la valeur "null" les valeurs dans le fichier avec les données réelles.
Donc, vous arrivez à un point spécifique dans le téléchargement, lancez le MPMoviePlayerViewController, et le laisser courir. Je voudrais aussi vous suggère d'utiliser le "F_NOCACHE" drapeau (avec fcntl()) afin de contourner le blocage des fichiers de cache (ce qui signifie que vous réduisez votre empreinte mémoire).
L'inconvénient de cette architecture est que si vous obtenez une impasse, et le lecteur vidéo prend de l'avance de vous, eh bien, l'utilisateur va avoir une très mauvaise expérience. Vous ne savez pas si il existe un moyen pour vous de surveiller et de prendre des mesures préventives.
EDIT: son tout à fait possible que la vidéo n'est pas lu séquentiellement, mais certaines informations nécessite le lecteur de l'essentiel de regarder vers l'avenir pour quelque chose. Si oui, alors c'est vouée à l'échec. La seule autre solution possible est d'utiliser un logiciel de l'outil de façon séquentielle afin le fichier (je ne suis pas un expert vidéo ne peut donc pas comment à partir de l'expérience sur l'un des ci-dessus).
Pour tester cela, vous pouvez construire une "endommagé" vidéo de longueurs variables, et de tester pour voir ce qui fonctionne et ce qui ne l'est pas. Par exemple, supposons que vous avez un 100Meg fichier. Écrire un petit programme utilitaire, et que l'écriture de la dernière 50Megs de données avec des zéros. Maintenant lire cette vidéo. Son échec 1/2 à travers. Si elle ne parvient pas tout de suite, eh bien, vous savez maintenant que sa recherche dans le dossier.
Si non séquentiel, son possible que sa en regardant le dernier 1000 octets ou alors, dans ce cas si vous n'écrasez pas que les choses fonctionnent comme vous le souhaitez. Si vous avez de la chance et c'est le cas, vous auriez éventuellement télécharger la dernière 1000 octets, puis démarrez à partir du début du fichier.
Ça devient vraiment vers le bas pour trouver le moyen avant l'introduction de réseautage réel dans l'image, de jouer un fichier partiel. Vous trouverez sûrement plus facile à introduire artificiellement les conditions réseau sans vraiment le faire en temps réel.