سؤال

لا أحد يعرف إذا AVQueuePlayer يبدأ في التخزين المؤقت التالي AVPlayerItem عندما يكون العنصر الحالي على وشك الانتهاء من اللعب؟

أعلم أنه لا يوجد شيء في المستندات اقتراحه ، فأنا أسأل في الغالب عما إذا كان أي شخص قد لاحظ هذا النوع من السلوك أم لا.

هل كانت مفيدة؟

المحلول

حسنًا ، لقد بحثت عن هذه المشكلة مرة أخرى وكتبت بعض التعليمات البرمجية للتحقق AVQueuePlayer.

وجهتني إجابة Jollycocoa في الاتجاه الصحيح من خلال اقتراح مراقبة خاصية الحالة على AVPlayerItem. ومع ذلك ، لا يبدو أن الوثائق تشير إلى أن هذه الخاصية (وهي كذلك AVPlayerItemStatusReadyToPlay قد تكون القيمة على وجه الخصوص) مرتبطة بالتخزين المؤقت.

لكن AVPlayerItem's loadedTimeRanges الممتلكات تبدو أكثر ارتباطا بالتخزين المؤقت.

كان القيام بـ KVO على هذه الصفيف أكثر صعوبة بعض الشيء - كائن الصفيف نفسه لا يتغير ، فقط هو العناصر التي لا تفعل ذلك - لذلك لجأت إلى طباعة محتوىها كل ثانية.

ما اكتشفته هو أن بضع ثوان في العنصر الأول في قائمة الانتظار ، loadedTimeRanges بالنسبة للبند الثاني يظهر جديدًا CMTimeRange مع وقت البدء 0 وبعض مدة صغيرة. يمكن أن تزيد المدة إلى 60 ثانية أو نحو ذلك بينما يستمر العنصر السابق في اللعب.

اجابة قصيرة: AVQueuePlayer سوف يخزن العازلة في اليوم التالي AVPlayerItem أثناء لعب واحد الحالي.

نصائح أخرى

اعتبارًا من iOS 5 ، يبدو أن AvqueUeplayer لم يعد قبل الخداع. لقد فعلت مسار المسار التالي في iOS 4.

لست متأكدًا من سبب وجود تغيير أو إذا كان متداولًا من جانب Apple أو مجرد خطأ. في أي حال ، إنه ألم وسأقوم بتقديم خطأ لهم حول هذا الموضوع.

وجدت عمل حولها !!! بعد عدة ساعات من البحث والاختبارات الفاشلة ، وجدت حلًا يعمل على iOS 5 وما فوق.

سيؤدي هذا إلى تشغيل الملفات دون أي تأخير بينهما. ليست مريحة إذا كنت ترغب في التبديل بين الملفات ، ولكن بالتأكيد ستجمع كل شيء معًا. لا يزال من الممكن تتبع المكان الذي يبدأ فيه كل ملف ، عند إضافة Avurlasset ، حافظ على متغير CMTime ، شيء مثل:

NSValue *toJumpTo = [NSValue valueWithCMTime:composition.duration];
[array addObject:toJumpTo];

ثم فقط اذهب من خلال الصفيف ، والتحقق من القيمة الزمنية الحالية ومقارنتها باستخدام CMTIMECOMPARE.

تحرير: تم العثور عليه على هذه الصفحة مرئي من أرشيف الويب (يشير الرابط حاليًا إلى البريد العشوائي).

لقد قمت بتجربة AvqueUeplayer باستخدام الأفلام التي يوفرها خادم. في هذا الاختبار بالذات ، قمت بإعداد لعبة QueUeplayer مع AvplayerItems التي تمت تهيئتها بعنوان URL إلى أصحاب فيديو مختلفة من أنواع مختلفة. أحدهما عبارة عن ملف. mp4 (تنزيل تدريجي) ، والآخر ملف .m3u8 http. إنه يتصرف بشكل غير تقليدي بعض الشيء ، يجب أن أقول.

اعتمادًا على الترتيب الذي أقوم به في طابور الملفات ، أحصل على سلوك مختلف تمامًا. إذا بدأت مع ملف .mp4 ، فإن AvplayerLayer يذهب فارغًا وصامتًا عندما يبدأ NextItem اللاعب (THE .M3U8-File). إذا قمت بتغيير الطلب وبدأت برفع M3U8 ، فإن AvplayerLayer يذهب فارغًا ولكن الصوت من الملف الثاني يلعب بشكل جيد.

انطباعي ، مما رأيته حتى الآن ، هو أن Avqueueplayer يفعل ليس ابدأ في تنزيل AvplayerItem التالي قبل أن ينتهي AvplayerItem الحالي. لكنني قد أكون مخطئا.

أن أتابع أعتقد ...

تحرير: حسنًا ، لذلك قمت بإجراء المزيد من الاختبارات ويبدو أن العناصر التالية تبدأ في التنزيل قبل أن ينتهي العنصر الأخير. انظر الوظيفة أدناه. السكتة الدماغية "لا" ...

EDIT2: إضافة توضيحي هنا بدلاً من ذلك ، لأن التعليق تركني بدون خيارات تنسيق. (أفضل الممارسات لذلك هي موضع ترحيب ، إذا كانت هذه طريقة سيئة للتعامل مع تحديثات الإجابة)

على أي حال ، لقد كررت من خلال العناصر الخاصة بي إضافة ملاحظة لخاصية الحالة باستخدام KVO ...

[playerItem addObserver: self forKeyPath:@"status" options: 0 context: NULL];

قمت أيضًا بتعيين وحدة التحكم الخاصة بي كمراقب لإخطار AvplayerItem avplayeritemdidplaytoendtimenotification:

[[NSNotificationCenter defaultCenter] addObserver: self
                                         selector: @selector(playerItemDidReachEnd:) 
                                             name: AVPlayerItemDidPlayToEndTimeNotification 
                                           object: nil];

الآن يمكنني تسجيل تغيير حالة AvplayerItem ، واتضح أن AvplayerItem التالي في قائمة الانتظار يتم تسجيلها مع تغيير الحالة avplayeritemstatusReadyToplay قبل أن ينتهي العنصر الحالي.

استنتاجي هو أن العنصر التالي في السطر يبدأ في التنزيل قبل انتهاء العنصر الحالي.

لكن! أقوم بإنشاء صفيف مع AvplayerItems التي أستخدمها لإنشاء مشغل. عند قراءة مستندات Avfoundation على Avassets ، أحصل على انطباع بأن هذه الأصول تنزيل أصولها بشكل غير متزامن ، لكنني ما زلت غير مؤكد عندما يتم بدء التنزيلات من قبل iAplayerItem. لا يزال لدي بعض السلوك غير التقليدي عندما أضيف ملامح .m3u8 إلى قائمة الانتظار ، مما يجعلني أتساءل عما إذا كانت هذه الأصول تبدأ في التنزيل في وقت الإنشاء (يبدو غريباً بعض الشيء بالنسبة لي).

لقد صنعت تجريبياً بسيطاً لك وللآخرين الذين يرغبون في تقليل وقت التخزين المؤقت لكل مقطع فيديو من عنوان URL.

ملاحظة: لا أعرف أن هذا هو الطريق الصحيح أم لا ولكنه يعمل بشكل مثالي بالنسبة لي دون أي مشكلة. إذا كان أي شخص يعرف أكثر أو يرغب في تحسين التعليمات البرمجية لنتيجة أفضل ، فلا ترحب بتغيير إجابتي.

في الكود أدناه ، يستغرق الفيديو الأول وقت التخزين المؤقت العادي. سيبدأ الفيديو التالي تلقائيًا عند الانتهاء من الفيديو الحالي ويمكنك التمرير اليسار واليمين للتحرك التالي والفيديو السابق.

اتبع الخطوات أدناه.

1) في videoplayervc.h ملف.

#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>

@interface videoPlayerVC : UIViewController
{
    AVPlayerViewController *avPlyrViewController;
    AVPlayerItem *currentAVPlyrItem;

    int currentVideoNO;  // For current video track.
    NSMutableArray *arrOfAVPItems;
    NSMutableArray *arrOfPlyer;
}

2) في videoplayervc.m ملف

هنا يمكنك البدء في تشغيل الفيديو بالنقر فوق الزر. أدناه طريقة عمل زر.

-(void)clickOnPlayVideosImage:(UIButton *)sender
{
   // In my App. all video URLs is up to 15 second.
    currentVideoNO = 0;
    NSMutableArray *arrForVideoURL = [[NSMutableArray alloc]initWithObjects:
                                      [NSURL URLWithString:@"http://videos/url1.mov"],
                                      [NSURL URLWithString:@"http://videos/url2.mov"],
                                      [NSURL URLWithString:@"http://videos/url3.mov"],
                                      [NSURL URLWithString:@"http://videos/url3.mov"],
                                      [NSURL URLWithString:@"http://videos/url4.mov"],
                                      [NSURL URLWithString:@"http://videos/url5.mov"], nil];

    AVPlayerItem *thePlayerItemA = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:0]];
    AVPlayerItem *thePlayerItemB = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:1]];
    AVPlayerItem *thePlayerItemC = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:2]];
    AVPlayerItem *thePlayerItemD = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:3]];
    AVPlayerItem *thePlayerItemE = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:4]];
    AVPlayerItem *thePlayerItemF = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:5]];

    if(arrOfAVPItems.count > 0)
       [arrOfAVPItems removeAllObjects];
    arrOfAVPItems = nil;

    if(arrOfPlyer.count > 0)
        [arrOfPlyer removeAllObjects];
    arrOfPlyer = nil;
    arrOfPlyer = [[NSMutableArray alloc] init];

    arrOfAVPItems = [NSMutableArray arrayWithObjects:thePlayerItemA, thePlayerItemB, thePlayerItemC, thePlayerItemD, thePlayerItemE, thePlayerItemF, nil]; // Add All items in the Array

    for(AVPlayerItem *myPlyrItem in arrOfAVPItems)
    {
        AVPlayer *videoPlayerNext = [AVPlayer playerWithPlayerItem:myPlyrItem]; /// Add item in the player
        [videoPlayerNext play]; 
        [videoPlayerNext pause];
        [arrOfPlyer addObject:videoPlayerNext]; /// Make Array of  "AVPlayer" just reduce buffering of each video. 
    }

    avPlyrViewController = [AVPlayerViewController new];
    avPlyrViewController.delegate = self;
    avPlyrViewController.player = (AVPlayer *)arrOfPlyer[0]; // Add first player from the Array.
    avPlyrViewController.showsPlaybackControls = YES;
    avPlyrViewController.allowsPictureInPicturePlayback = YES;
    avPlyrViewController.videoGravity = AVLayerVideoGravityResizeAspect;

    [self presentViewController:avPlyrViewController animated:YES completion:^{
        [self performSelector:@selector(playVideos) withObject:nil afterDelay:1];/// call method after one second for play video.

        UISwipeGestureRecognizer * swipeleft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeleftToNextVideo:)];
        swipeleft.direction=UISwipeGestureRecognizerDirectionLeft;
        [avPlyrViewController.view addGestureRecognizer:swipeleft]; // Add left swipe for move on next video

        UISwipeGestureRecognizer * swiperight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeleftToPerviousVideo:)];
        swiperight.direction=UISwipeGestureRecognizerDirectionRight;
        [avPlyrViewController.view addGestureRecognizer:swiperight]; // Add right swipe for move on previous video
    }];
}

الآن أكتب playVideos رمز الطريقة.

#pragma mark - AVPlayer Methods -

-(void)playVideos
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem]; // remove current "AVPlayerItem" from the notification observe.

    if(arrOfAVPItems.count > 0)
    {
        currentAVPlyrItem = (AVPlayerItem *)arrOfAVPItems[currentVideoNO]; // Add "AVPlayerItem" from the array.

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem]; // Add notification observer to indication current "AVPlayerItem" is finish.

        // pause and nil previous player if available.
        [avPlyrViewController.player pause];
        avPlyrViewController.player = nil;

        avPlyrViewController.player = (AVPlayer *)arrOfPlyer[currentVideoNO]; // add new player from the array
        [avPlyrViewController.player.currentItem seekToTime:kCMTimeZero]; // set for start video on initial position.
        [avPlyrViewController.player play]; // Play video

    }
}

مراقب الإخطار للإشارة إلى أن الفيديو ينتهي.

- (void)playerItemDidReachEnd:(NSNotification *)notification
{
    NSLog(@"IT REACHED THE END");
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem];  // remove current "AVPlayerItem" from the notification observe.

    [self swipeleftToNextVideo:nil]; // Call method for next video.
}

طريقة لتشغيل الفيديو التالي

-(void)swipeleftToNextVideo:(UISwipeGestureRecognizer*)gestureRecognizer
{
    currentVideoNO++;
    if(currentVideoNO > (arrOfAVPItems.count -1))
        currentVideoNO = 0;

    NSLog(@"current - %d and last - %d", currentVideoNO, (int)(arrOfAVPItems.count -1));

    [self playVideos];
}

طريقة لتشغيل الفيديو السابق.

-(void)swipeleftToPerviousVideo:(UISwipeGestureRecognizer*)gestureRecognizer
{
    currentVideoNO--;
    if(currentVideoNO < 0)
        currentVideoNO = (int)(arrOfAVPItems.count -1);

    NSLog(@"current - %d and last - %d", currentVideoNO, (int)(arrOfAVPItems.count -1));

    [self playVideos];
}

اتبع هذه الخطوات. إذا كان أي شك ثم تعليق من فضلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top