2012-05-18 11 views
9

Có thể và được hỗ trợ để sử dụng API giải mã h.264 được tăng tốc phần cứng iOS để giải mã tệp video cục bộ (không được phát trực tiếp) và sau đó soạn đối tượng trên đầu trang của nó?Giải mã h.264 được tăng tốc phần cứng thành kết cấu, lớp phủ hoặc tương tự trong iOS

Tôi muốn tạo một ứng dụng liên quan đến việc vẽ các đối tượng đồ họa trước video và sử dụng bộ hẹn giờ phát lại để đồng bộ hóa nội dung tôi đang vẽ ở trên cùng với nội dung đang được phát trên video. Sau đó, dựa trên hành động của người dùng, thay đổi nội dung tôi đang vẽ lên trên đầu (nhưng không phải là video)

Đến từ DirectX, OpenGL và OpenGL ES cho Android, tôi đang hình dung một thứ như kết xuất video thành kết cấu và sử dụng kết cấu đó để vẽ một quad toàn màn hình, sau đó sử dụng các sprites khác để vẽ phần còn lại của các đối tượng; hoặc có thể viết một bộ lọc trung gian ngay trước trình kết xuất đồ họa, vì vậy tôi có thể thao tác các khung hình đầu ra riêng lẻ và vẽ các công cụ của mình; hoặc có thể vẽ lên một lớp 2D ở đầu video. Có vẻ như AV Foundation, hoặc Core Media có thể giúp tôi làm những gì tôi đang làm, nhưng trước khi tôi tìm hiểu chi tiết, tôi muốn biết liệu có thể làm được điều tôi muốn làm hay không. và các tuyến đường chính của tôi là gì để tiếp cận vấn đề.

Vui lòng không "quá nâng cao cho bạn, hãy thử lời chào" đầu tiên trên thế giới. Tôi biết công cụ của mình và chỉ muốn biết liệu tôi có muốn làm gì không (và quan trọng nhất là được hỗ trợ, vì vậy ứng dụng sẽ không bị từ chối), trước khi tôi tự nghiên cứu chi tiết.

chỉnh sửa:

Tôi không có kiến ​​thức trong việc phát triển iOS, nhưng chuyên nghiệp làm DirectX, OpenGL và OpenGL ES dành cho Android. Tôi đang xem xét việc tạo một phiên bản iOS của ứng dụng Android mà tôi hiện có và tôi chỉ muốn biết liệu điều này có thể thực hiện được hay không. Nếu vậy, tôi có đủ thời gian để bắt đầu phát triển iOS từ đầu, để làm những gì tôi muốn làm. Nếu không thể, thì tôi sẽ không đầu tư thời gian học toàn bộ nền tảng vào lúc này.

Do đó, đây là câu hỏi về tính khả thi về mặt kỹ thuật. Tôi không yêu cầu mã. Tôi đang tìm câu trả lời của loại "Có, bạn có thể làm điều đó. Chỉ cần sử dụng A và B, sử dụng C để kết xuất thành D và vẽ nội dung của bạn bằng E" hoặc "Không, bạn không thể. không có sẵn cho các ứng dụng của bên thứ ba "(đó là điều mà một người bạn đã nói với tôi). Chỉ cái này, và tôi sẽ trên đường.

Tôi đã đọc tổng quan về công nghệ video ở trang 32 của ios technology overview. Nó khá nhiều nói rằng tôi có thể sử dụng Media Player cho các chức năng phát lại đơn giản nhất (không phải những gì tôi đang tìm kiếm), UIKit cho nhúng video với một chút kiểm soát nhiều hơn nhúng, nhưng không phải trên thực tế phát lại (không phải những gì tôi ' m tìm kiếm), AVFoundation để kiểm soát phát lại nhiều hơn (có thể là những gì tôi cần, nhưng hầu hết các tài nguyên tôi tìm thấy trực tuyến nói về cách sử dụng máy ảnh), hoặc Core Media để có toàn quyền kiểm soát mức độ thấp trên video (có thể là những gì tôi cần, nhưng extremely poorly documented và thậm chí thiếu tài nguyên khi phát lại hơn cả AVFoundation).

Tôi lo ngại rằng tôi có thể dành 6 tháng tiếp theo để học lập trình iOS toàn thời gian, chỉ để tìm ra ở cuối API không có sẵn cho nhà phát triển bên thứ ba và điều tôi muốn làm là không thể chấp nhận đối với iTunes triển khai cửa hàng. Đây là những gì bạn tôi nói với tôi, nhưng tôi dường như không thể tìm thấy bất cứ điều gì có liên quan trong hướng dẫn phát triển ứng dụng. Do đó, tôi đến đây để hỏi những người có nhiều kinh nghiệm trong lĩnh vực này, cho dù tôi có muốn làm gì hay không. Không còn nữa.

Tôi coi đây là một câu hỏi cấp cao hợp lệ, có thể bị hiểu lầm là câu hỏi tôi không làm bài tập về nhà-plz-give-me-teh-codez.Nếu sự phán xét của tôi ở đây bị nhầm lẫn, hãy xóa bỏ, hoặc từ chối câu hỏi này cho sự khinh miệt của trái tim bạn.

+1

SO là về việc cung cấp câu trả lời dựa trên nội dung của câu hỏi. Nếu chúng tôi cảm thấy muốn nói "bắt đầu với thế giới hello", chúng tôi sẽ nói điều đó. Đối với câu hỏi của bạn, bạn đã xem [AVComposition] chưa (https://developer.apple.com/library/mac/documentation/AVFoundation/Reference/AVComposition_Class/Reference/Reference.html)? – CodaFi

Trả lời

24

Có, bạn có thể làm điều này và tôi nghĩ câu hỏi của bạn đủ cụ thể để thuộc về đây. Bạn không phải là người duy nhất muốn làm điều này, và phải mất một chút đào để tìm ra những gì bạn có thể và không thể làm được.

AV Foundation cho phép bạn thực hiện giải mã video H.264 tăng tốc phần cứng bằng AVAssetReader, tại thời điểm đó bạn được chuyển khung hình được giải mã thô của video ở định dạng BGRA. Chúng có thể được tải lên kết cấu bằng cách sử dụng glTexImage2D() hoặc bộ đệm kết cấu hiệu quả hơn trong iOS 5.0. Từ đó, bạn có thể xử lý để hiển thị hoặc truy xuất khung từ OpenGL ES và sử dụng AVAssetWriter để thực hiện mã hóa H.264 tăng tốc phần cứng của kết quả. Tất cả điều này sử dụng các API công cộng, do đó, không có lúc nào bạn nhận được bất cứ nơi nào gần một cái gì đó mà sẽ dẫn đến một từ chối từ App Store.

Tuy nhiên, bạn không phải thực hiện việc này. Khung nguồn mở được cấp phép BSD của tôi GPUImage đóng gói các hoạt động này và xử lý tất cả các thao tác này cho bạn. Bạn tạo một cá thể GPUImageMovie cho phim H.264 đầu vào của bạn, đính kèm các bộ lọc vào nó (chẳng hạn như các lớp phủ hỗn hợp hoặc hoạt động khóa chroma), và sau đó đính kèm các bộ lọc này vào GPUImageView để hiển thị và/hoặc GPUImageMovieWriter để mã hóa lại H. 264 phim từ video đã xử lý.

Vấn đề mà tôi hiện có là tôi không tuân theo dấu thời gian trong video để phát lại, vì vậy khung được xử lý nhanh chóng khi chúng được giải mã từ phim. Để lọc và mã hóa lại video, đây không phải là vấn đề, vì dấu thời gian được chuyển tới máy ghi âm, nhưng để hiển thị trực tiếp màn hình, điều này có nghĩa là video có thể được tăng tốc lên tới 2-4X . Tôi hoan nghênh mọi đóng góp sẽ cho phép bạn đồng bộ hóa tốc độ phát lại với các dấu thời gian thực của video.

Tôi hiện có thể phát lại, lọc và mã hóa lại video 640x480 ở mức trên 30 FPS trên iPhone 4 và 720p ở ~ 20-25 FPS, với iPhone 4S có khả năng lọc và mã hóa 1080p đáng kể cao hơn 30 FPS. Một số bộ lọc đắt tiền hơn có thể đánh thuế GPU và làm chậm tốc độ này xuống một chút, nhưng hầu hết các bộ lọc đều hoạt động ở các phạm vi tốc độ khung hình này.

Nếu bạn muốn, bạn có thể kiểm tra các lớp GPUImageMovie để xem làm thế nào nó tải lên này để OpenGL ES, nhưng mã có liên quan như sau:

- (void)startProcessing; 
{ 
    NSDictionary *inputOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:AVURLAssetPreferPreciseDurationAndTimingKey]; 
    AVURLAsset *inputAsset = [[AVURLAsset alloc] initWithURL:self.url options:inputOptions]; 

    [inputAsset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"tracks"] completionHandler: ^{ 
     NSError *error = nil; 
     AVKeyValueStatus tracksStatus = [inputAsset statusOfValueForKey:@"tracks" error:&error]; 
     if (!tracksStatus == AVKeyValueStatusLoaded) 
     { 
      return; 
     } 
     reader = [AVAssetReader assetReaderWithAsset:inputAsset error:&error]; 

     NSMutableDictionary *outputSettings = [NSMutableDictionary dictionary]; 
     [outputSettings setObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey: (NSString*)kCVPixelBufferPixelFormatTypeKey]; 
     // Maybe set alwaysCopiesSampleData to NO on iOS 5.0 for faster video decoding 
     AVAssetReaderTrackOutput *readerVideoTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:[[inputAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] outputSettings:outputSettings]; 
     [reader addOutput:readerVideoTrackOutput]; 

     NSArray *audioTracks = [inputAsset tracksWithMediaType:AVMediaTypeAudio]; 
     BOOL shouldRecordAudioTrack = (([audioTracks count] > 0) && (self.audioEncodingTarget != nil)); 
     AVAssetReaderTrackOutput *readerAudioTrackOutput = nil; 

     if (shouldRecordAudioTrack) 
     {    
      audioEncodingIsFinished = NO; 

      // This might need to be extended to handle movies with more than one audio track 
      AVAssetTrack* audioTrack = [audioTracks objectAtIndex:0]; 
      readerAudioTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:nil]; 
      [reader addOutput:readerAudioTrackOutput]; 
     } 

     if ([reader startReading] == NO) 
     { 
      NSLog(@"Error reading from file at URL: %@", self.url); 
      return; 
     } 

     if (synchronizedMovieWriter != nil) 
     { 
      __unsafe_unretained GPUImageMovie *weakSelf = self; 

      [synchronizedMovieWriter setVideoInputReadyCallback:^{ 
       [weakSelf readNextVideoFrameFromOutput:readerVideoTrackOutput]; 
      }]; 

      [synchronizedMovieWriter setAudioInputReadyCallback:^{ 
       [weakSelf readNextAudioSampleFromOutput:readerAudioTrackOutput]; 
      }]; 

      [synchronizedMovieWriter enableSynchronizationCallbacks]; 
     } 
     else 
     { 
      while (reader.status == AVAssetReaderStatusReading) 
      { 
       [self readNextVideoFrameFromOutput:readerVideoTrackOutput]; 

       if ((shouldRecordAudioTrack) && (!audioEncodingIsFinished)) 
       { 
        [self readNextAudioSampleFromOutput:readerAudioTrackOutput]; 
       } 

      }    

      if (reader.status == AVAssetWriterStatusCompleted) { 
       [self endProcessing]; 
      } 
     } 
    }]; 
} 

- (void)readNextVideoFrameFromOutput:(AVAssetReaderTrackOutput *)readerVideoTrackOutput; 
{ 
    if (reader.status == AVAssetReaderStatusReading) 
    { 
     CMSampleBufferRef sampleBufferRef = [readerVideoTrackOutput copyNextSampleBuffer]; 
     if (sampleBufferRef) 
     { 
      runOnMainQueueWithoutDeadlocking(^{ 
       [self processMovieFrame:sampleBufferRef]; 
      }); 

      CMSampleBufferInvalidate(sampleBufferRef); 
      CFRelease(sampleBufferRef); 
     } 
     else 
     { 
      videoEncodingIsFinished = YES; 
      [self endProcessing]; 
     } 
    } 
    else if (synchronizedMovieWriter != nil) 
    { 
     if (reader.status == AVAssetWriterStatusCompleted) 
     { 
      [self endProcessing]; 
     } 
    } 
} 

- (void)processMovieFrame:(CMSampleBufferRef)movieSampleBuffer; 
{ 
    CMTime currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(movieSampleBuffer); 
    CVImageBufferRef movieFrame = CMSampleBufferGetImageBuffer(movieSampleBuffer); 

    int bufferHeight = CVPixelBufferGetHeight(movieFrame); 
    int bufferWidth = CVPixelBufferGetWidth(movieFrame); 

    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); 

    if ([GPUImageOpenGLESContext supportsFastTextureUpload]) 
    { 
     CVPixelBufferLockBaseAddress(movieFrame, 0); 

     [GPUImageOpenGLESContext useImageProcessingContext]; 
     CVOpenGLESTextureRef texture = NULL; 
     CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, coreVideoTextureCache, movieFrame, NULL, GL_TEXTURE_2D, GL_RGBA, bufferWidth, bufferHeight, GL_BGRA, GL_UNSIGNED_BYTE, 0, &texture); 

     if (!texture || err) { 
      NSLog(@"Movie CVOpenGLESTextureCacheCreateTextureFromImage failed (error: %d)", err); 
      return; 
     } 

     outputTexture = CVOpenGLESTextureGetName(texture); 
     //  glBindTexture(CVOpenGLESTextureGetTarget(texture), outputTexture); 
     glBindTexture(GL_TEXTURE_2D, outputTexture); 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 

     for (id<GPUImageInput> currentTarget in targets) 
     {    
      NSInteger indexOfObject = [targets indexOfObject:currentTarget]; 
      NSInteger targetTextureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue]; 

      [currentTarget setInputSize:CGSizeMake(bufferWidth, bufferHeight) atIndex:targetTextureIndex]; 
      [currentTarget setInputTexture:outputTexture atIndex:targetTextureIndex]; 

      [currentTarget newFrameReadyAtTime:currentSampleTime]; 
     } 

     CVPixelBufferUnlockBaseAddress(movieFrame, 0); 

     // Flush the CVOpenGLESTexture cache and release the texture 
     CVOpenGLESTextureCacheFlush(coreVideoTextureCache, 0); 
     CFRelease(texture); 
     outputTexture = 0;   
    } 
    else 
    { 
     // Upload to texture 
     CVPixelBufferLockBaseAddress(movieFrame, 0); 

     glBindTexture(GL_TEXTURE_2D, outputTexture); 
     // Using BGRA extension to pull in video frame data directly 
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferWidth, bufferHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, CVPixelBufferGetBaseAddress(movieFrame)); 

     CGSize currentSize = CGSizeMake(bufferWidth, bufferHeight); 
     for (id<GPUImageInput> currentTarget in targets) 
     { 
      NSInteger indexOfObject = [targets indexOfObject:currentTarget]; 
      NSInteger targetTextureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue]; 

      [currentTarget setInputSize:currentSize atIndex:targetTextureIndex]; 
      [currentTarget newFrameReadyAtTime:currentSampleTime]; 
     } 
     CVPixelBufferUnlockBaseAddress(movieFrame, 0); 
    } 

    if (_runBenchmark) 
    { 
     CFAbsoluteTime currentFrameTime = (CFAbsoluteTimeGetCurrent() - startTime); 
     NSLog(@"Current frame time : %f ms", 1000.0 * currentFrameTime); 
    } 
} 
+0

Cảm ơn bạn rất nhiều. Tôi sẽ hỏi bạn tôi về điều này. Tôi cũng tìm thấy http://stackoverflow.com/questions/4237538/is-it-possible-using-video-as-texture-for-gl-in-ios, có vẻ khá giống với những gì bạn đang làm. – user1003819

+0

@ user1003819 - Trong trường hợp đó, Tommy mô tả cách gửi khung hình video đến GPU, mà tôi cũng xử lý trong khung được liên kết ở trên (trên thực tế, đó là trọng tâm chính của nó). Có một cách nhanh hơn để tải lên các khung máy ảnh trực tiếp trong iOS 5.0 so với những gì anh ấy mô tả, xuất hiện sau khi anh ấy đăng. Một lần nữa, mã cho điều này là trong khung nguồn mở của tôi. –

+0

Ồ, bạn nói đúng. Tôi thực sự liên kết với câu hỏi sai. Tôi có nghĩa là một trong những điều này: http://stackoverflow.com/questions/5621627/ios4-how-do-i-use-video-file-as-an-opengl-texture – user1003819