読者です 読者をやめる 読者になる 読者になる

動画再生ライブラリ。ベータ版。


今作ってる物にオープニングムービーを再生させたかったので作成してみました。
もっと時間が掛かると想定してたのですが思ったよりも早くビルドが通ってしまいました。


シングル動作(1ライブラリ=1動画再生)と割り切ったことや、秒間30コマ固定としたこと、
サウンド再生は各々で行ってもらう等、機能限定を色々したことで進行が早まったのかもです。
…シングルなので上下同時に動画を再生というのが出来ませんが。
要望があれば対応するかもです。NDSのキャパを超えなければという前提で。


実装案はたろサさんの「30fps割り込みを試してみる」エントリーの説明を参考にしてます。
全体ソースは公開されてなかった気がします。


前もって動画データとして、
NDSスクリーンサイズ(256*192)のNDS画像形式の16bitBMPデータ1つを1シーン(1コマ)とした、
複数のシーンデータを1ファイルにまとめたものが外部ファイルとして必要になります。
変換ツールはありません。
フリーウェアでまかなえるので作る予定もありません(凄く大変な作業が待ち構えてるから。)。


データ構造として単純な分、少しの動画であっても容量は凄いことになります、が気にしません(笑)。
コンピュータサイエンス的なことを求めるのは頭の良い人に任せます。


ベータ版なのは動画データがまだ出来ておらず動作確認取れていないためです。
あと実機でしか動かないと思います。
早いとこ確認を済ませて、いい加減本編作成と不足リソースに取り掛かりたいなーと。


movie library test version 0.01 beta


以下ソース(後で弄る用にコメント多めです。)。


main.c

/*---------------------------------------------------------------------------------
	
	movie library test
	
	version 0.01 beta
	Jun 12, 2010
	
	By REGEKATSU
	
---------------------------------------------------------------------------------*/

#include <stdio.h>
#include <nds.h>
#include "libmy/movie.h"


//動画データファイルパス
#define MOVIE_FILE "/eroge/opMovie.bin"


//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
	
	//initVideoMode
	videoSetMode(MODE_FB0);
	
	//initVRAMBank
	vramSetBankA(VRAM_A_LCD);
	
	consoleDemoInit();
	printf("movie library test\n\nversion 0.01 beta\nJun 12, 2010\n\nBy REGEKATSU\n\n\n");
	printf("usage: KEY_A=PLAY, KEY_B=STOP");
	
	//動画再生ライブラリを初期化します。
	movie_Init();
	
	while(1){
		
		swiWaitForVBlank();
		scanKeys();
		
		if(keysDown() & KEY_A){
			//動画を再生します。
			movie_Play(VRAM_A_LCD, MOVIE_FILE, false);
			
		}else if(keysDown() & KEY_B){
			//動画を停止します。
			movie_Stop();
			
		}
		
	}
	
}

//---------------------------------------------------------------------------------


movie.h

/*---------------------------------------------------------------------------------
	
	movie library header
	
	version 0.01 beta
	Jun 12, 2010
	
	By REGEKATSU
	
---------------------------------------------------------------------------------*/

#ifndef _MOVIE_H_
#define _MOVIE_H_


#include <nds/ndstypes.h>


#ifdef __cplusplus
extern "C" {
#endif


//動画再生ライブラリを初期化します。
void movie_Init(void);

//動画を再生します。
//引数には描画先アドレス、動画ファイル名、繰り返し再生の有無、を指定します。
//
//動画データのファイル書式は以下のとおりになります。
// NDSスクリーンサイズ(256*192)のNDS画像形式の16bitBMPデータ1つを1シーンとした、
// 複数のシーンデータを1ファイルにまとめたもの。
//
//戻り値にはエラー情報が返ります。エラーがあった場合は動画再生は行われません。
// 0:エラー無し, -1:FAT初期化エラー, -2:ファイルオープンエラー, -3:ファイル書式エラー
int movie_Play(u16 *buf, const char *filename, bool loop);

//動画を停止します。
void movie_Stop(void);


#ifdef __cplusplus
}
#endif

#endif	//_MOVIE_H_


movie.c

/*---------------------------------------------------------------------------------
	
	movie library routine
	
	version 0.01 beta
	Jun 12, 2010
	
	By REGEKATSU
	
---------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fat.h>
#include <nds/interrupts.h>
#include <nds/timers.h>
#include <nds/arm9/video.h>
#include "movie.h"


//動画データにおける1シーンのデータサイズ
const u32 BG_BMP16_256x192 = SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(u16);


typedef enum{
	MOVIE_STOP = 0x00, 
	MOVIE_PLAY
}MOVIE_ACT;


typedef struct{
	
	u16 act;            //動画再生状態
	u32 timer;          //タイマー割り込み用カウンタ
	u16 *buf;           //動画出力先アドレス
	
	char filename[256]; //動画データファイルパス
	FILE *fp;           //動画データファイルポインタ
	u32 scenes;         //動画データシーン数(コマ数)
	bool loop;          //繰り返し再生の有無
	
}MovieStatus;


static MovieStatus st_movie;


void movie_Timer1Handler(void);
void movie_Update(void);


//---------------------------------------------------------------------------------
void movie_Init(void){
//---------------------------------------------------------------------------------
	
	//内部変数の初期化。
	
	memset(&st_movie, 0, sizeof(st_movie));
	
	
	//タイマー割り込みを使って動画再生を実現する。
	//タイマー1を使い、1秒間に30回、割り込みを発生させる。
	//これにより、秒間30コマの動画再生を行う。
	
	TIMER_DATA(1) = TIMER_FREQ_256(30);
	TIMER_CR(1)  = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_DIV_256;
	irqSet(IRQ_TIMER(1), movie_Timer1Handler);
	
	
}

//---------------------------------------------------------------------------------
void movie_Timer1Handler(void){
//---------------------------------------------------------------------------------
	
	//動画再生の更新処理を行う。
	movie_Update();
	
}

//---------------------------------------------------------------------------------
int movie_Play(u16 *buf, const char *filename, bool loop){
//---------------------------------------------------------------------------------
	
	fpos_t size = 0;
	
	
	//状態を停止中に設定する。
	
	st_movie.act = MOVIE_STOP;
	
	
	//動画再生情報を内部変数にセットする。
	
	st_movie.buf = buf;
	strcpy(st_movie.filename, filename);
	st_movie.loop = loop;
	
	
	//DLDIパッチが適用されているか確認する。
	//DLDIパッチ未適用の場合、-1:FAT初期化エラー を返す。
	
	//(動画データを内部に持つことは、NDS内蔵メモリが上限4Mということもあって、
	//nitroFS等のファイルシステムライブラリを使わない実現が難しかったりとプログラム的に面倒。
	//そういう事情があり、動画データは外部ファイルという形式を取っている。)。
	
	if(fatInitDefault() == false) return -1;
	
	
	//動画データを確認する。
	//動画データが確認(オープン)出来なかった場合、-2:ファイルオープンエラー を返す。
	
	if((st_movie.fp = fopen(st_movie.filename, "rb")) == NULL) { fclose(st_movie.fp); return -2; }
	
	
	//動画データのファイルサイズを取得して、サイズからファイル書式が正しいものかどうか確認する。
	//動画データのファイル書式が正しくなかった場合、-3:ファイル書式エラー を返す。
	
	//動画データのファイル書式:
	// NDSスクリーンサイズ(256*192)のNDS画像形式の16bitBMPデータ1つを1シーンとした、
	// 複数のシーンデータを1ファイルにまとめたもの。
	
	//動画データのシーン総数割り出し:
	// 動画データファイルサイズ÷動画1シーンサイズ(256×192×2)=動画シーン総数
	
	fseek(st_movie.fp, 0, SEEK_END); 
	fgetpos(st_movie.fp, &size);
	
	if(size % BG_BMP16_256x192 != 0) { fclose(st_movie.fp); return -3; }
	st_movie.scenes = size / BG_BMP16_256x192;
	
	
	//全てのエラーチェックに問題が無ければ、
	//タイマー割り込みを有効して、再生状態を再生中に設定する。
	
	st_movie.timer = 0;
	irqEnable(IRQ_TIMER(1));
	
	st_movie.act = MOVIE_PLAY;
	
	return 0;
	
	
}

//---------------------------------------------------------------------------------
void movie_Update(void){
//---------------------------------------------------------------------------------
	
	//再生中以外の状態で再生処理を実行しない。
	
	if(st_movie.act != MOVIE_PLAY) return;
	
	
	if(st_movie.timer < st_movie.scenes - 1){
		
		//シーンデータを順に(タイマー割り込み毎に)、次のシーンデータが無くなるまで
		//1シーンづつ動画データの読込・描画を行う。
		
		fseek(st_movie.fp, st_movie.timer * BG_BMP16_256x192, SEEK_SET);
		fread((u16 *)st_movie.buf, BG_BMP16_256x192 / 2, sizeof(u16), st_movie.fp);
		st_movie.timer++;
		
		
	}else{
		
		//次のシーンデータが無くなった場合。
		
		if(st_movie.loop == true){
			
			//繰り返し再生有りならタイマーを無効にせず、タイマー値をリセットする(シーンデータ頭出し→順次再生へ)。
			st_movie.timer = 0;
			
		}else{
			
			//繰り返し再生無しならタイマーを無効にして、動画再生処理を停止する。
			movie_Stop();
			
		}
		
		
	}
	
}

//---------------------------------------------------------------------------------
void movie_Stop(void){
//---------------------------------------------------------------------------------
	
	//既に停止状態である場合、停止処理を実行しない。
	
	if(st_movie.act == MOVIE_STOP) return;
	
	
	//動画データファイルをクローズして、タイマー割り込みを無効にする。また、再生状態も停止にする。
	
	fclose(st_movie.fp);
	irqDisable(IRQ_TIMER(1));
	
	st_movie.act = MOVIE_STOP;
	
	
}

//---------------------------------------------------------------------------------