AVIStream - zapis zmian.

0

Poskładałem mały programik - ma otwierać avi, w każdej klatce zrobić jakieś zmiany i każdą klatkę zapisać z powrotem do tego pliku. Problem w tym, że nie widać owych zmian, w klatce są jakieś dane, ale chyba nie są one zapisywane. Filmik działa cały czas tak samo, nieskompresowany, otwiera się bez problemu. Czyżby funkcja AVIStreamWrite nie służyła do zapisu klatki, coś namieszałem z parametrami, czy przyczyna może być jeszcze inna?

void modifyFrame1(int frame, cMovie *film)
{
    film->grabAviFrame(frame);
    film->saveAviFrame(frame);
    printf("frame %i",frame);
}

int main()
{
    cMovie *film1= new cMovie;
    film1->openAvi("movies/bf.avi");
    for(int i=5;i<200;i++)
    {
        modifyFrame1(i,film1);
    }
    film1->end();
    getch();
    return 0;
}

.h

class cMovie
{
private:
    AVISTREAMINFO        psi; 
    PGETFRAME            pgf;
Decoding
    long                lastframe;
    int                    mpf;
    size_t                size;                            
    char                *pdata;
public:
    PAVISTREAM            pavi; 
    char                *pdataGPU;
    int                    width;  
    int                    height; 
    cMovie();
    void openAvi(LPCSTR szFile);
    void grabAviFrame(int frame);
    void saveAviFrame(int frame);
    void end();
};

.cpp

cMovie::cMovie()
{
}

void cMovie::openAvi(LPCSTR szFile)
{
    AVIFileInit();
    if (AVIStreamOpenFromFile(&pavi, szFile, streamtypeVIDEO, 0, OF_WRITE, NULL) !=0)
    {
        printf("Error - cannot open file or wtf!\n");
    }
    AVIStreamInfo(pavi, &psi, sizeof(psi));                     
    width=psi.rcFrame.right-psi.rcFrame.left;                
    height=psi.rcFrame.bottom-psi.rcFrame.top;             

    lastframe=AVIStreamLength(pavi);                      

    mpf=AVIStreamSampleToTime(pavi,lastframe)/lastframe;  

    pgf=AVIStreamGetFrameOpen(pavi, NULL);                   
    if (pgf==NULL)
    {
        printf("Failed To Open The AVI Frame!\n");
    }
    printf("Movie info:\n\tw: %i\n\th: %i\n\tlast: %i\n",width,height,lastframe);
}

void cMovie::grabAviFrame(int frame)
{
    LPBITMAPINFOHEADER lpbi;                                 
    lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(pgf, frame);  
    pdata=(char *)lpbi+lpbi->biSize+lpbi->biClrUsed * sizeof(RGBQUAD);
}

void cMovie::saveAviFrame(int frame)
{
    for(int i=0;i<100000;i++)
        pdata[i]=0;
    AVIStreamWrite(pavi, frame, 1, pdata, size, AVIIF_KEYFRAME, NULL, NULL);//brak zapisu?
}

void cMovie::end()
{
     AVIStreamRelease(pavi);
}
0

AVIStreamWrite zwraca jakąś wartość, zapewne HRESULT, i nie widzę, żebyś w ogóle był nią zainteresowany (ogólnie obsługa błędów u Ciebie leży). To po pierwsze. Po drugie, jaką wartość ma size? Może ślepy jestem, ale nigdzie nie widzę, żebyś ustawiał tą zmienną.

0

Dorzuciłem

hr = AVIStreamWrite(pavi, frame, 1, pdata, size, AVIIF_KEYFRAME, NULL, NULL );
    if(SUCCEEDED(hr))
    {
        printf("success!");
    }
    else if(FAILED(hr))
        printf("fail!");

i jednak wychodzi fail.

Co do rozmiaru - sprawdzałem parę wariantów:

size = width*height*sizeof(char)*3;
size = lpbi->biSizeImage;
size = lpbi->biSize;

ale efekt jest ciągle ten sam. Zmieniałem jeszcze parametr OF_WRITE na OF_READWRITE - też nic.

0
if(SUCCEEDED(hr)) { ... }
else if(FAILED(hr)) ...

No proszę Cię. Po co ten drugi warunek, przecież on wynika z pierwszego?

i jednak wychodzi fail.

No, a jaki konkretnie fail? :> hr zawiera numer błędu, który jest zdefiniowany w winerror.h lub, w przypadku strumieniu AVI, w vfw.h (AVIERR_xxx).

Co do rozmiaru - sprawdzałem parę wariantów:

Tak na przyszłość dobrze by było, jakbyś któryś z tych wariantów zawierał w kodzie.

Wprawdzie nie znam strumieni avi, ale na 99,9% błąd tkwi w tym, że zapisujesz do strumienia pusty bufor, który, jak sądzę, powinien zawierać klatkę w formacie strumienia (skompresowaną, jeśli trzeba).

0

No, a jaki konkretnie fail? :> hr zawiera numer błędu, który jest zdefiniowany w winerror.h lub, w przypadku strumieniu AVI, w vfw.h (AVIERR_xxx).

Wychodzi -2147205006 - nie wiem, czy takie wartości powinny w ogóle wychodzić, ale debugger pokazuje to samo. W vfw.h wprawdzie są jakieś identyfikatory błędów, ale 3-cyfrowe.

Co do rozmiaru - sprawdzałem parę wariantów:

Tak na przyszłość dobrze by było, jakbyś któryś z tych wariantów zawierał w kodzie.
</quote>
W kodzie jeden z wariantów już był - po prostu wrzucając kod na forum, zrobiłem mały porządek i usunąłem to przez przypadek.

0

(...) ale 3-cyfrowe.

Przyjrzyj się MAKE_AVIERR().

Co do błędu, to jest to wartość AVIERR_READONLY.

0

No tak - namieszałem i zostawiłem OF_READ. Zmieniłem na OF_READWRITE i pojawił się już "właściwy" błąd - AVIERR_UNSUPPORTED. Gdzieś znalazłem info, że kompresja nie jest wspierana przez ten rodzaj danych. Próbowałem różnych plików - bez kompresji, z kompresją - ciągle to samo.

0

Według dokumentacji domyślna obsługa plików avi nie obsługuje zapisu do środka pliku, jedynie dopisywanie jest możliwe (w pewnym sensie jest to oczywiste, jeśli brać pod uwagę kompresję). Więc jeśli chcesz dokonać zmian na poszczególnych klatkach strumienia avi, musisz przepisać cały plik, klatka po klatce.

0

Widzę, że będzie trochę więcej zabawy.
Tak przy okazji, spróbowałem jakoś zapisać jakąś klatkę do bmp.

void cMovie::createBitmap()
{
    FILE *hndl;
    unsigned char d;
    hndl = fopen("param_images/1280_720.bmp", "rb");
    unsigned char header[54];
    for(int i=0;i<54;i++)
        fread(&header[i], 1, sizeof(unsigned char), hndl);
    fclose(hndl);

    FILE *hndl2;
    hndl2 = fopen("param_images/final.bmp", "wb");
    fwrite(header, 1, 54, hndl2);
    int i=0;
    char pdata2[]={5,255,255};
    i=0;
    for(int j=0;j<height;j++)
    {
        for(int k=0;k<width;k++)
        {
            if((j==360)||(k==640)||(j==k))
            {
                fwrite(&pdata2, 1, sizeof(unsigned char)*3,hndl2);
                i=i+3;
            }
            else
            {
                fwrite(&pdata[i], 1, sizeof(char)*3,hndl2);
                i+=3;        
            }
        }
    }
    fclose(hndl2);
}

Skopiowałem nagłówek już istniejącej bitmapy, więc teoretycznie, do nowego pliku trzeba jeszcze tylko wrzucić dane. Jednak efekt jest zupełnie inny od zawartości filmu - nieważne, którą klatkę się przechwyci.
Na filmie wygląda to mniej więcej tak: http://img412.imageshack.us/img412/9810/img1qc.jpg
A tak wygląda utworzony obrazek: http://img301.imageshack.us/img301/9738/img2yt.jpg Niby widać podobieństwo części zawartości - obraz został jakby podzielony na 3 powtarzające się fragmenty, ale kolory już są inne.
Żółte linie to efekt działania

 if((j==360)||(k==640)||(j==k))
{
fwrite(&pdata2, 1, sizeof(unsigned char)*3,hndl2);
i=i+3;
}
  • miał wyjść krzyżyk i ukośna linia - i tak wyszło - nie wiem tylko, dlaczego koloru żółtego, skoro dla {5,255,255} powinien być jakiś bardziej niebieskawy.
0
unsigned char header[54];
    for(int i=0;i<54;i++)
        fread(&header[i], 1, sizeof(unsigned char), hndl);

Heh, bardziej skomplikować się tego nie dało? :>

Jeśli chodzi o bitmapy RGB, każdy wiersz jest dopełniony do 4 bajtów:

int align = (width * bits) & 3;
if(align) align = 4 - align;
	
for(int j = 0; j < height; ++j)
{
	for(int k = 0; k < width; ++k)
	{
		...
	}
	
	i += align;
}

(...) nie wiem tylko, dlaczego koloru żółtego

W windowsie, o ile dobrze pamiętam, kolejność kanałów rgb jest odwrotna.

0

Niestety sytuacja pozostała bez zmian - wychodzi na to, że dane są kopiowane jak trzeba, ale klatka obrazu różni się od pliku bmp - pomijając kolejność kanałów i ew. dopełnienie.

0

Oj, chyba z moją głową coś nie do końca było dobrze wczoraj :-) Pisałem o dopełnieniu a pominąłem go przy zapisie do pliku. Zamiast:

i += align;

daj:

fwrite(pdata + i, align, 1, hndl2);
i += align;

Jeszcze to popraw:

fwrite(&pdata2, ...); //<--- usuń ampersanda

Zastanawia mnie, dlaczego nagłówek bitmapy bierzesz z obcego pliku, zamiast stworzyć go na podstawie informacji, które dostajesz razem z wyciągniętą klatką (BITMAPINFOHEADER)? Masz pewność, że format opisany w nagłówku zgadza się z formatem klatki?

1 użytkowników online, w tym zalogowanych: 0, gości: 1