2011-08-15 24 views
5

Próbuję parsować klatki H.264 z pliku .mov. Myślę, że doszedłem do wniosku, że mov.c z AVFormat - część FFMPEG jest drogą do zrobienia. Ale mov.c ma ~ 2600 linii obok nieskomentowanego kodu. Szukam przykładów użycia FFMPEG, szczególnie analizowania struktury dowolnego typu pliku. nie ma znaczenia, czy jest to MPEG4 czy Quicktime Movie, ponieważ są one dość podobne pod względem struktury.Parsowanie klatka po klatce od .mov przy użyciu ffmpeg

jeśli nie ma istniejących przykładów (nie mogę znaleźć) może ktoś go użył i może podać kilka linii kodu lub wyjaśnić, jak zacząć?

Co usiłuję zrobić: używam AVCaptureSession uchwycić próbki z kamerą, próbki te są następnie kodowane w H264 i zapisane do pliku za pomocą AVAssetsWriter, AVAssetsWriterInput i AVAssetsWriterInputPixelBufferAdaptor. Powodem jest to, że nie mogę uzyskać dostępu do sprzętu kodowania H264 bezpośrednio, ponieważ jabłko nie pozwoli na to. Co ja teraz trzeba zrobić (Nie sądzę pewien) jest analizować out:

The „MDAT” -atom (dane film, nie może być więcej niż jeden chyba) z pliku .mov. następnie "-atom, a następnie w komórce wideo (próbka danych wideo może być więcej niż jedna). Myślę, że będzie kilka atomów, które uważam za ramy. będą one typu "avc1" (to jest typ dla H264). Proszę, popraw mnie w tym, ponieważ jestem całkiem pewien, że nie dostałem tego wszystkiego poprawnie, ale.

moje pytanie brzmi, jak mam zamiar parsować się pojedynczych klatek. Czytałem the documentation i patrzyłem na iFrameExtractor (co nie jest bardzo pomocne, ponieważ dekoduje ramki). Myślę, że zrozumiałem to poprawnie, kiedy mam używać mov.c z FFMPEG-AVFormat, ale nie jestem pewien.

Edit: Jestem teraz próbuje tak:

  1. uruchomić nieznacznie obniżonej funkcji init i iFrameExtractor który znajdzie się videostream w .mov pliku.

  2. uzyskać dane na ramie tak:

    AVPacket packet; 
    av_read_frame(pFormatCtx, &packet); 
    NSData *frame; 
    if(packet.stream_index == videoStream){ 
        frame = [NSData dataWithBytes:packet.data length:packet.size]; 
    } 
    videoStream++; 
    av_free_packet(&packet); 
    return frame; 
    

i następnie przekazać go do podklasy NSOperation gdzie jest utrzymywany w oczekiwaniu na przesłaniu. , ale otrzymuję EXC_BAD_ACC, czy robię coś nie tak podczas kopiowania danych z ramki? jakieś pomysły. otrzymuję EXC _... kiedy próbuję ustawić zmienną klasy NSData* frame używając jej (nonatomic, retain) -property. (Mówi EXC_BAD_ACC w wierszu syntetyzować)

+0

mov.c nie pomoże ci w osiągnięciu celu końcowego. Jeśli potrzebujesz pomocy w analizie MOV/MP4, może ci się przydać. Kolejna poręczna biblioteka, w której sprawy stają się brutalne [mp4v2] (http://code.google.com/p/mp4v2/). Zasadniczo będziesz musiał napisać to sam. Żadna biblioteka nie wykona tej pracy z różnych powodów. –

+0

@Steve McFarlin, dziękuję, masz wskazówki dotyczące czytania oprócz dokumentacji qt, aby uchwycić całą strukturę pliku mov, ponieważ naprawdę mam problem z chwytaniem tego wszystkiego, czy to prawda, że ​​atom nie ma nawet być w określonej kolejności? który z nich był najłatwiejszy do pracy z mov lub mp4? –

+0

@Steve McFarlin, domyślam się, że widziałeś projekt iFrameExtractor. jest to zasadniczo ten sam kod, co w tutorialu autorstwa Martina Böhme (na przykład na dranger.com). w następnej funkcji frame używają tylko av_read_frame, a następnie ją dekodują. czy AVPacket modyfikowany przez av_read_frame nie będzie kodowaną ramką H264? –

Odpowiedz

1

używam następujących analizować każdą klatkę z pliku mov.

-(NSData *)nextFrame { 
    AVPacket packet; 
    NSData *frame = nil; 

    while(!frame && av_read_frame(pFormatCtx, &packet)>=0) { 

     if(packet.stream_index == streamNo) { 
      frame = [[[NSData alloc] initWithBytes:packet.data length:packet.size] autorelease]; 
     } 
     av_free_packet(&packet); 
    } 
    return frame; 
} 

chociaż uważaj, ponieważ av_read_frame nie weryfikuje ramek, co odbywa się w etapie dekodowania. oznacza to, że zwrócone "ramki" mogą zawierać dodatkowe informacje, które nie są częścią rzeczywistej ramki.

do init AVFormatContext * pFormatCtx i AVCodecContext * pCodecCtx używam tego kodu (co moim zdaniem jest pochodzący z przykładowym kodem Martina Böhmego):

AVCodec *pCodec; 

    // Register all formats and codecs 
    av_register_all(); 

    // Open video file 
    if(avformat_open_input(&pFormatCtx, [moviePath cStringUsingEncoding:NSASCIIStringEncoding], NULL, NULL)!=0) 
     goto initError; // Couldn't open file 

    // Retrieve stream information 
    if(avformat_find_stream_info(pFormatCtx,NULL)<0) 
     goto initError; // Couldn't find stream information 

    // Find the video stream 
    streamNo = -1; 
    for(int i=0; i<pFormatCtx->nb_streams; i++){ 
     if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) 
     { 
      streamNo = i; 
      break; 
     } 
    } 
    if(streamNo == -1) 
     goto initError; // Didn't find a video stream 

    // Get a pointer to the codec context for the video stream 
    pCodecCtx=pFormatCtx->streams[streamNo]->codec; 

    // Find the decoder for the video stream 
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id); 
    if(pCodec==NULL) 
     goto initError; // Codec not found 

    // Open codec 
    if(avcodec_open2(pCodecCtx, pCodec, NULL)<0) 
     goto initError; // Could not open codec 

    return self; 

initError: 
    NSLog(@"initError in VideoFrameExtractor"); 
    [self release]; 
    return nil; 

nadzieję, że to pomoże komuś w przyszłości.

0

Jest to całkiem dobry tutorial na temat korzystania libavcodec/muxera here. Wygląda na to, że interesuje Cię funkcja DoSomethingWithTheImage(), którą pozostawili niezatwierdzeni.

+0

Chcę surowe dane H.264, więc mogę ponownie złożyć klatki do mov po stronie serwera później. Patrzyłem na ten przykład wcześniej i nie mogłem się dowiedzieć, czy powinienem pominąć krok dekodowania? i po prostu zachować 'rawData = packet.data'? co się dzieje, kiedy dekodowałem? czy mogę przejść od standardu H.264? –

+1

oboje czytasz i piszesz .mov? co więc robisz? –

+0

@yi_H Demontuję plik .mov podczas nagrywania, aby wysłać ramki H264 na serwer, na którym ponownie je składam. to jedyny sposób na strumieniowanie H264 w czasie rzeczywistym za pomocą iOS, ponieważ go rozumiem. –

0

Jeśli przesyłasz strumień H264 na iOS, musisz posegmentować transmisję strumieniową (np. Przesyłanie strumieniowe na żywo w Apple).

Tutaj jest projektem open source: http://code.google.com/p/httpsegmenter/

+1

Będę przesyłać strumieniowo z iOS. użycie AVCaptureSession i AVAssetsWriter do zapisu z kamery do pliku. następnie chcę przeanalizować plik, aby uzyskać ramki H264 i przesłać je do pliku. Mam wszystko, co działa, łącznie z pakietami http do przesyłania. to, czego potrzebuję, to sposób na dostęp do ramek w pliku .mov, dostęp do surowych danych ramki. może zadziała z przykładem zamieszczonym w drugiej odpowiedzi. Próbuję teraz, jeśli masz inną sugestię, jak mogę to zrobić, proszę podziel się nim :) –

+0

chcesz zrzucić kanał audio? chcesz użyć innego kontenera? Nadal go nie rozumiem. –

+0

Zdałem sobie sprawę, że druga odpowiedź nie zadziała, ponieważ dekoduje ramkę, więc nie będzie już zakodowana w H264. muszę wyodrębnić ramki bezpośrednio ze strumienia wideo bez dekodowania –