-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e0372aa
commit 382ec29
Showing
64 changed files
with
49,156 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,381 @@ | ||
/* -*- C++ -*- | ||
* | ||
* AVIWrapper.cpp - avifile library wrapper class to play AVI video & audio stream | ||
* | ||
* Copyright (c) 2001-2010 Ogapee. All rights reserved. | ||
* | ||
* [email protected] | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
*/ | ||
|
||
#include "AVIWrapper.h" | ||
#include <SDL_mixer.h> | ||
#include <audiodecoder.h> | ||
#include <avm_cpuinfo.h> | ||
#include <avm_output.h> | ||
#include <avifile.h> | ||
#include <videodecoder.h> | ||
#include <image.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#define DEFAULT_AUDIOBUF 4096 | ||
#define AVI_FINISH_EVENT 12345 | ||
|
||
#define AVIFILE_VERSION 747 | ||
|
||
AVIWrapper::AVIWrapper() | ||
{ | ||
screen_overlay = NULL; | ||
i_avi = NULL; | ||
v_stream = NULL; | ||
a_stream = NULL; | ||
remaining_buffer = new char[DEFAULT_AUDIOBUF*4]; | ||
remaining_count = 0; | ||
} | ||
|
||
AVIWrapper::~AVIWrapper() | ||
{ | ||
if ( v_stream ) | ||
v_stream->StopStreaming(); | ||
if ( a_stream ) | ||
a_stream->StopStreaming(); | ||
if ( i_avi ) delete i_avi; | ||
if ( screen_overlay ) SDL_FreeYUVOverlay( screen_overlay ); | ||
if ( remaining_buffer ) delete[] remaining_buffer; | ||
} | ||
|
||
int AVIWrapper::init( char *filename, bool debug_flag ) | ||
{ | ||
this->debug_flag = debug_flag; | ||
#if AVIFILE_VERSION >= 747 | ||
if ( !debug_flag ) avm::AvmOutput::singleton()->resetDebugLevels(-1); | ||
#else | ||
if ( !debug_flag ) avm::out.resetDebugLevels(-1); | ||
#endif | ||
|
||
i_avi = CreateIAviReadFile( filename ); | ||
if ( i_avi == NULL || i_avi->IsValid() == false ){ | ||
fprintf( stderr, "can't CreateIAviReadFile from %s\n", filename ); | ||
return -1; | ||
} | ||
|
||
v_stream = i_avi->GetStream(0, AviStream::Video ); | ||
if ( v_stream == NULL ){ | ||
fprintf( stderr, "Video Stream is NULL\n" ); | ||
return -1; | ||
} | ||
|
||
width = v_stream->GetStreamInfo()->GetVideoWidth(); | ||
height = v_stream->GetStreamInfo()->GetVideoHeight(); | ||
if ( debug_flag ) fprintf( stderr, "width %d height %d\n", width, height ); | ||
|
||
return 0; | ||
} | ||
|
||
int AVIWrapper::initAV( SDL_Surface *surface, bool audio_open_flag ) | ||
{ | ||
screen_rect.x = screen_rect.y = 0; | ||
screen_rect.w = surface->w; | ||
screen_rect.h = surface->h; | ||
|
||
v_stream->StartStreaming(); | ||
if ( v_stream->GetVideoDecoder() == NULL ){ | ||
if ( debug_flag ) fprintf( stderr, "GetVideoDecoder() return 0.\n" ); | ||
return -1; | ||
} | ||
avm::IVideoDecoder::CAPS cap = v_stream->GetVideoDecoder()->GetCapabilities(); | ||
if ( debug_flag ) printf("cap %x\n", cap ); | ||
|
||
if ( cap & avm::IVideoDecoder::CAP_YV12 ){ | ||
v_stream->GetVideoDecoder()->SetDestFmt( 0, fccYV12 ); | ||
screen_overlay = SDL_CreateYUVOverlay( width, height, SDL_YV12_OVERLAY, surface ); | ||
} | ||
else if ( cap & avm::IVideoDecoder::CAP_YUY2 ){ | ||
v_stream->GetVideoDecoder()->SetDestFmt( 0, fccYUY2 ); | ||
screen_overlay = SDL_CreateYUVOverlay( width, height, SDL_YUY2_OVERLAY, surface ); | ||
} | ||
else{ | ||
screen_overlay = SDL_CreateYUVOverlay( width, height, SDL_YV12_OVERLAY, surface ); | ||
} | ||
|
||
if ( !audio_open_flag ) return 0; | ||
a_stream = i_avi->GetStream(0, AviStream::Audio); | ||
if ( a_stream == NULL ){ | ||
if ( debug_flag ) fprintf( stderr, "Audio Stream is NULL\n" ); | ||
return 0; | ||
} | ||
|
||
a_stream->StartStreaming(); | ||
WAVEFORMATEX wave_fmt; | ||
a_stream->GetAudioDecoder()->GetOutputFormat( &wave_fmt ); | ||
if ( debug_flag ) printf(" format %d ch %d sample %d bit %d avg Bps %d\n", wave_fmt.wFormatTag, wave_fmt.nChannels, wave_fmt.nSamplesPerSec, wave_fmt.wBitsPerSample, wave_fmt.nAvgBytesPerSec ); | ||
|
||
if ( Mix_OpenAudio( wave_fmt.nSamplesPerSec, MIX_DEFAULT_FORMAT, wave_fmt.nChannels, DEFAULT_AUDIOBUF ) < 0 ){ | ||
fprintf( stderr, "can't open audio device\n" ); | ||
a_stream->StopStreaming(); | ||
delete a_stream; | ||
a_stream = NULL; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static void audioCallback( void *userdata, Uint8 *stream, int len ) | ||
{ | ||
AVIWrapper &avi = *(AVIWrapper*)userdata; | ||
avi.audioCallback( userdata, stream, len ); | ||
} | ||
|
||
void AVIWrapper::audioCallback( void *userdata, Uint8 *stream, int len ) | ||
{ | ||
if ( len == 0 ) | ||
status = AVI_STOP; | ||
|
||
size_t ocnt; | ||
size_t samples; | ||
size_t count = 0; | ||
|
||
if ( remaining_count > 0 ){ | ||
if ( remaining_count <= len ){ | ||
memcpy( stream, remaining_buffer, remaining_count ); | ||
count = remaining_count; | ||
len -= remaining_count; | ||
remaining_count = 0; | ||
} | ||
else{ | ||
memmove( stream, remaining_buffer, len ); | ||
count = len; | ||
len = 0; | ||
remaining_count -= len; | ||
return; | ||
} | ||
} | ||
|
||
while ( len > 0 && !a_stream->Eof() ){ | ||
a_stream->ReadFrames( remaining_buffer, (size_t)len, (size_t)len, samples, ocnt ); | ||
if ( ocnt <= len ){ | ||
memcpy( stream+count, remaining_buffer, ocnt ); | ||
len -= ocnt; | ||
} | ||
else{ | ||
memcpy( stream+count, remaining_buffer, len ); | ||
if ( ocnt-len < DEFAULT_AUDIOBUF*4 - len ){ | ||
memmove( remaining_buffer, remaining_buffer+len, ocnt-len ); | ||
remaining_count = ocnt-len; | ||
} | ||
else{ | ||
remaining_count = 0; | ||
} | ||
len = 0; | ||
} | ||
count += ocnt; | ||
} | ||
} | ||
|
||
double AVIWrapper::getAudioTime() | ||
{ | ||
if ( time_start == 0 ) | ||
{ | ||
frame_start = (v_stream) ? v_stream->GetTime() : 0.; | ||
#if AVIFILE_VERSION >= 747 | ||
time_start = avm_get_time_us(); | ||
#else | ||
time_start = longcount(); | ||
#endif | ||
} | ||
|
||
if ( a_stream ) | ||
return a_stream->GetTime(); | ||
else | ||
#if AVIFILE_VERSION >= 747 | ||
return frame_start + to_float(avm_get_time_us(), time_start); | ||
#else | ||
return frame_start + to_float(longcount(), time_start); | ||
#endif | ||
} | ||
|
||
static int playVideo( void *userdata ) | ||
{ | ||
AVIWrapper &avi = *(AVIWrapper*)userdata; | ||
return avi.playVideo( userdata ); | ||
} | ||
|
||
#define NUM_CACHES 3 | ||
int AVIWrapper::playVideo( void *userdata ) | ||
{ | ||
int i; | ||
|
||
struct{ | ||
bool valid; | ||
avm::CImage *image; | ||
double time; | ||
} cache[NUM_CACHES]; | ||
for ( i=0 ; i<NUM_CACHES ; i++ ){ | ||
cache[i].valid = false; | ||
} | ||
int remaining_cache = NUM_CACHES; | ||
|
||
while ( status == AVI_PLAYING && !v_stream->Eof() ){ | ||
avm::CImage *image = v_stream->GetFrame( true ); | ||
if ( image == NULL ) break; | ||
|
||
double current_time = v_stream->GetTime(); | ||
double minimum_time = current_time; | ||
|
||
// look for the nearest in the cache | ||
int nearest_cache=-1; | ||
for ( i=0 ; i<NUM_CACHES ; i++ ){ | ||
if ( cache[i].valid ){ | ||
if ( nearest_cache == -1 || | ||
(nearest_cache >= 0 && | ||
cache[i].time < cache[nearest_cache].time) ){ | ||
nearest_cache = i; | ||
if ( minimum_time > cache[nearest_cache].time ) | ||
minimum_time = cache[nearest_cache].time; | ||
} | ||
} | ||
} | ||
|
||
double async = getAudioTime() - minimum_time; | ||
//printf("audio %f (%f - %f) minimum %d %f cur %f\n", async, getAudioTime(), minimum_time, nearest_cache, minimum_time, current_time ); | ||
if ( async < -0.01 ){ | ||
if ( remaining_cache == 0 ){ | ||
//printf("sync0 %f %f %f %f\n", async, (a_stream)?a_stream->GetTime():0.0, v_stream->GetTime(), minimum_time ); | ||
SDL_Delay( (int)(-async*1000) ); | ||
} | ||
} | ||
if ( (async < -0.01 && remaining_cache > 0) || | ||
nearest_cache >= 0 ){ | ||
// add cache | ||
for ( i=0 ; i<NUM_CACHES ; i++ ){ | ||
if ( cache[i].valid == false ){ | ||
cache[i].valid = true; | ||
cache[i].image = new avm::CImage(image); | ||
cache[i].time = current_time; | ||
remaining_cache--; | ||
break; | ||
} | ||
} | ||
if ( async < -0.01 ){ | ||
image->Release(); | ||
continue; | ||
} | ||
} | ||
|
||
if ( nearest_cache >= 0 && minimum_time == cache[nearest_cache].time ){ | ||
//printf("draw cache %d %f\n", nearest_cache, cache[nearest_cache].time ); | ||
//if ( async <= 0.033 ) // drop frame if necessary | ||
drawFrame( cache[nearest_cache].image ); | ||
cache[nearest_cache].image->Release(); | ||
cache[nearest_cache].valid = false; | ||
remaining_cache++; | ||
} | ||
else{ | ||
//printf("draw real %f\n", current_time ); | ||
//if ( async <= 0.033 ) // drop frame if necessary | ||
drawFrame( image ); | ||
} | ||
image->Release(); | ||
} | ||
status = AVI_STOP; | ||
|
||
for ( i=0 ; i<NUM_CACHES ; i++ ) | ||
if ( cache[i].valid ) cache[i].image->Release(); | ||
|
||
return 0; | ||
} | ||
|
||
int AVIWrapper::play( bool click_flag ) | ||
{ | ||
int ret = 0; | ||
time_start = 0; | ||
status = AVI_PLAYING; | ||
if ( v_stream ) | ||
thread_id = SDL_CreateThread( ::playVideo, this ); | ||
if ( a_stream ) | ||
Mix_HookMusic( ::audioCallback, this ); | ||
|
||
bool done_flag = false; | ||
while( !(done_flag & click_flag) && status == AVI_PLAYING ){ | ||
SDL_Event event; | ||
|
||
while( SDL_PollEvent( &event ) ){ | ||
switch (event.type){ | ||
case SDL_KEYUP: | ||
if ( ((SDL_KeyboardEvent *)&event)->keysym.sym == SDLK_RETURN || | ||
((SDL_KeyboardEvent *)&event)->keysym.sym == SDLK_KP_ENTER || | ||
((SDL_KeyboardEvent *)&event)->keysym.sym == SDLK_SPACE || | ||
((SDL_KeyboardEvent *)&event)->keysym.sym == SDLK_ESCAPE ) | ||
done_flag = true; | ||
break; | ||
case SDL_QUIT: | ||
ret = 1; | ||
case SDL_MOUSEBUTTONUP: | ||
done_flag = true; | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
SDL_Delay( 10 ); | ||
} | ||
|
||
status = AVI_STOP; | ||
if ( v_stream ) | ||
SDL_WaitThread( thread_id, NULL ); | ||
if ( a_stream ) | ||
Mix_HookMusic( NULL, NULL ); | ||
|
||
return ret; | ||
} | ||
|
||
int AVIWrapper::drawFrame( avm::CImage *image ) | ||
{ | ||
if ( image == NULL ) return -1; | ||
|
||
unsigned int i, j; | ||
uint32_t comp = image->GetFmt()->biCompression; | ||
|
||
unsigned char *buf = image->Data(); | ||
SDL_LockYUVOverlay( screen_overlay ); | ||
Uint8 *dst_y = screen_overlay->pixels[0]; | ||
Uint8 *dst_u = screen_overlay->pixels[1]; | ||
Uint8 *dst_v = screen_overlay->pixels[2]; | ||
|
||
if ( comp == 0 ){ // BGR | ||
image->ToYUV(); | ||
for ( i=0 ; i<width*height ; i++ ) | ||
*dst_y++ = buf[i*3]; | ||
|
||
for ( i=0 ; i<height/2 ; i++ ){ | ||
for ( j=0 ; j<width/2 ; j++ ){ | ||
*dst_v++ = buf[(i*width*2+j*2)*3+1]; | ||
*dst_u++ = buf[(i*width*2+j*2)*3+2]; | ||
} | ||
} | ||
} | ||
else if ( comp == IMG_FMT_YUY2 ){ | ||
memcpy( dst_y, buf, width*height*2 ); | ||
} | ||
else if ( comp == IMG_FMT_YV12 ){ | ||
memcpy( dst_y, buf, width*height+width*height/2 ); | ||
} | ||
SDL_UnlockYUVOverlay( screen_overlay ); | ||
SDL_DisplayYUVOverlay( screen_overlay, &screen_rect ); | ||
|
||
return 0; | ||
} |
Oops, something went wrong.