/* * MiniMPEG4.c * * Main file for MPEG4 * * This wizard-generated code is based on code adapted from the * stationery files distributed as part of the Palm OS SDK 4.0. * * Copyright (c) 1999-2004 PalmOne, Inc. or its subsidiaries. * All rights reserved. */ #include #include #include #include #include "Common.h" #include "MiniMPEG4.h" #include "MiniMPEG4Rsc.h" #include "FileBrowserForm.h" #include "FileBrowserFormRsrc.h" #include "PalmOneCodecPluginMgr.h" #include "PalmOneCodecFormat.h" #include "ColorButtonGadget.h" #include "PalmLcdOverlay.h" #include "PalmOneCamera.h" #define ourMinVersion sysMakeROMVersion(3,0,0,sysROMStageDevelopment,0) #define kPalmOS20Version sysMakeROMVersion(2,0,0,sysROMStageDevelopment,0) #define Allocate(n) MemPtrNew(n) #define DATA_BUFFER_SIZE 16384 #define USE_RATE_CONTROL 1 /** Holds basic encoder/decoder info. */ typedef struct { Char inputFileName[256]; Char outputFileName[256]; FileRef inputFileRef; FileRef outputFileRef; UInt32 width; UInt32 height; UInt32 frameRate; UInt32 bitRate; UInt32 keyFrame; UInt32 IQuantization; UInt32 searchRange; UInt16 codecMgrLibRefNum; PalmCodecSession session; UInt32 imageFormat; Boolean loadFileInMemory; Boolean outputToSD; Boolean encoding; Boolean loop; } MPEG4Info; #if 0 #pragma mark Globals #endif /** Internal Globals. */ static MPEG4Info gMPEG4Info; static UInt32 gFrameCount = 0; static UInt32 gStartTicks = 0; static UInt32 gLastTick = 0; static UInt32 gRateControl= 0; static Char *gGOButtonLabel[] = { "Go", "Stop" }; static Char gLoadingText[] = "Loading"; static UInt32 gScreenDepth = 16; static UInt32 gPreviousScreenDepth = 0; RGBColorType gStartColor = { 0, 255, 255, 255 }; RGBColorType gEndColor = { 0, 0, 0, 255 }; static const RGBColorType gWhite = { 0, 255, 255, 255 }; static const RGBColorType gBlack = { 0, 0, 0, 0 }; // Extra static UInt16 gLCDOverlayRefNum = sysInvalidRefNum; static Boolean gUseOverlay = true; //static UInt16 gCameraRefNum = sysInvalidRefNum; #if 0 #pragma mark - #endif /*********************************************************************** * * FUNCTION: HandleMenu * * DESCRIPTION: Deals with menu commands issued * * PARAMETERS: itemID -> ID of the Menu which has been selected * * RETURNED: Nothing * * REVISION HISTORY: * * ***********************************************************************/ static void HandleMenu(UInt16 itemID) { FormType *frmAboutP = NULL; switch(itemID) { case AboutForm: frmAboutP = FrmInitForm(AboutForm); FrmDoDialog(frmAboutP); // Display the About Box. FrmDeleteForm(frmAboutP); break; } } /** * Read and Encode the parameters */ static Err PrvReadParameters(Boolean encoding) { Err err = errNone; FieldType *fldP = NULL; FormType *frmP = FrmGetActiveForm(); // Get the width & height fldP = FrmGetPtr(frmP, MainWidthField); if(FldGetTextPtr(fldP)) gMPEG4Info.width = StrAToI(FldGetTextPtr(fldP)); fldP = FrmGetPtr(frmP, MainHeightField); if(FldGetTextPtr(fldP)) gMPEG4Info.height = StrAToI(FldGetTextPtr(fldP)); // Read the file names fldP = FrmGetPtr(frmP, MainInputField); if (FldGetTextPtr(fldP)) StrCopy(gMPEG4Info.inputFileName, FldGetTextPtr(fldP)); else StrCopy(gMPEG4Info.inputFileName, ""); fldP = FrmGetPtr(frmP, MainOutputField); StrCopy(gMPEG4Info.outputFileName, FldGetTextPtr(fldP)); // Get the SearchRange & BitRate fldP = FrmGetPtr(frmP, MainSearchRangeField); if(FldGetTextPtr(fldP)) gMPEG4Info.searchRange = StrAToI(FldGetTextPtr(fldP)); fldP = FrmGetPtr(frmP, MainBitRateField); if(FldGetTextPtr(fldP)) gMPEG4Info.bitRate = StrAToI(FldGetTextPtr(fldP)); // Get the KeyFrame & IQuantization fldP = FrmGetPtr(frmP, MainKeyFrameField); if(FldGetTextPtr(fldP)) gMPEG4Info.keyFrame = StrAToI(FldGetTextPtr(fldP)); fldP = FrmGetPtr(frmP, MainIQuantizationField); if(FldGetTextPtr(fldP)) gMPEG4Info.IQuantization = StrAToI(FldGetTextPtr(fldP)); //Done: return err; } /** * Open input file */ static Err PrvFileOpenInput() { Err err = errNone; UInt32 volumeIterator = 0; UInt16 firstVolume = 0; // Get first card volumeIterator = vfsIteratorStart; err = VFSVolumeEnumerate(&firstVolume, &volumeIterator); if( err == expErrEnumerationEmpty ) { goto Done; } // If we have a file path, open the file if(gMPEG4Info.inputFileName[0]!=NULL) { err = VFSFileOpen(firstVolume, gMPEG4Info.inputFileName, vfsModeRead, &gMPEG4Info.inputFileRef); if( err ) goto Done; } Done: return err; } /** * Open output file */ static Err PrvFileOpenOutput() { Err err = errNone; UInt32 volumeIterator = 0; UInt16 firstVolume = 0; // Get first card volumeIterator = vfsIteratorStart; err = VFSVolumeEnumerate(&firstVolume, &volumeIterator); if( err == expErrEnumerationEmpty ) { goto Done; } // If we have a file path, open the file if(gMPEG4Info.outputFileName[0]!=NULL) { err = VFSFileOpen(firstVolume, gMPEG4Info.outputFileName, vfsModeWrite|vfsModeCreate|vfsModeTruncate, &gMPEG4Info.outputFileRef); if( err ) goto Done; } Done: return err; } /** * File info callback. * This function is called by the file browser */ static void PrvGetInputFileInfoCallback(UInt16 volume, const Char* path, const Char* file) { // Create the full path name StrCopy(gMPEG4Info.inputFileName, path); // Internally, we expect full path StrCat(gMPEG4Info.inputFileName, file); } /** * Load a file in memory. * Draws a fill rectangle with specified colors. * */ static UInt32 PrvLoadFileInMemory(FileRef srcFile, UInt32 inputBufferSize, UInt8 *fileBufferP, Boolean useFtr) { UInt32 read = 0; UInt32 total = 0; UInt32 current = 0; UInt32 xPosition = 80 - FntCharsWidth(gLoadingText, StrLen(gLoadingText)) / 2; RGBColorType oldColor; EventType event; UInt8 *tempBufferP = NULL; RectangleType goButtonBounds; WinDrawOperation oldOperation; UInt32 toRead = inputBufferSize / 100; RectangleType rect = { { 30, 147 }, { 1, 10 } }; RGBColorType color = { 0, 0, 0, 0 }; Int32 R, G, B; Int32 sR, sG, sB; Err err=errNone; // FtrPtrNew - Allocate temporary buffer if(useFtr) { tempBufferP = MemPtrNew(4096); } // Save old color WinSetBackColorRGB(&color, &oldColor); R = (UInt32)gStartColor.r << 8; G = (UInt32)gStartColor.g << 8; B = (UInt32)gStartColor.b << 8; // Color steps sR = (((Int32)gEndColor.r - (Int32)gStartColor.r) << 8) / 101; sG = (((Int32)gEndColor.g - (Int32)gStartColor.g) << 8) / 101; sB = (((Int32)gEndColor.b - (Int32)gStartColor.b) << 8) / 101; // Get the bounds for the Go button FrmGetObjectBounds(FrmGetActiveForm(), FrmGetObjectIndex(FrmGetActiveForm(),MainGoButton), &goButtonBounds); while(1) { EvtGetEvent(&event, 0); if(event.eType == penDownEvent && RctPtInRectangle(event.screenX, event.screenY, &goButtonBounds)) { total = 0; goto Done; } if(useFtr) { err=VFSFileRead(srcFile, 4096, tempBufferP, &read); DmWrite(fileBufferP, total, tempBufferP, read); } else { err=VFSFileRead(srcFile, toRead, fileBufferP, &read); fileBufferP += read; } if(err!=errNone) break; if(!read) break; total += read; current = (total * 100) / inputBufferSize + 31; if( current > rect.topLeft.x) { R += sR; G += sG; B += sB; if(R < 0) R = 0; if(R > 65536) R = 65536; if(G < 0) G = 0; if(G > 65536) G = 65536; if(B < 0) B = 0; if(B > 65536) B = 65536; color.r = R >> 8; color.g = G >> 8; color.b = B >> 8; WinSetBackColorRGB(&color, NULL); WinFillRectangle(&rect, 0); oldOperation = WinSetDrawMode(winOverlay); WinPaintChars(gLoadingText, StrLen(gLoadingText), xPosition, 146); WinSetDrawMode(oldOperation); rect.topLeft.x = current; } } Done: if(tempBufferP) MemPtrFree(tempBufferP); WinSetBackColorRGB(&oldColor, NULL); return total; } /** * Update the current UI * Displays the frmae count, the average FPS and the current FPS */ static void PrvUpdateUI() { Char text[32]; FieldType *fldP = NULL; UInt32 curTicks = TimGetTicks(); UInt32 fps = curTicks - gStartTicks; if(fps) fps = (sysTicksPerSecond * gFrameCount) / fps; // Average FPS StrIToA(text, fps); fldP = FrmGetPtr(FrmGetActiveForm(), MainAverageFPSField); SetFieldTextFromStr(fldP, text, true); // Current FPS fps = ( curTicks - gLastTick ); if(fps) fps = sysTicksPerSecond / fps; StrIToA(text, fps); fldP = FrmGetPtr(FrmGetActiveForm(), MainCurrentFPSField); SetFieldTextFromStr(fldP, text, true); gLastTick = curTicks; // Current Frame Count StrPrintF(text, "%04ld", gFrameCount); fldP = FrmGetPtr(FrmGetActiveForm(), MainFramesField); SetFieldTextFromStr(fldP, text, true); } /** * DrawLaoding rectangle */ static void PrvDrawLoadingRect() { RGBColorType oldColor; RectangleType rect = { { 30, 147 }, { 101, 10 } }; // Draw the progress bar WinSetForeColorRGB(&gWhite, &oldColor); WinDrawRectangle(&rect, 0); WinSetForeColorRGB(&gBlack, NULL); WinDrawRectangleFrame(rectangleFrame, &rect); // Restore WinSetForeColorRGB(&oldColor, NULL); } #if 0 #pragma mark - #endif /** * Load a frame from memory. */ static void PrvLoadFrameFromMemory(UInt8 *bufferP, PalmImagePlanarType *planarImage, UInt32 imageFormat) { switch(imageFormat) { case palmCodecImageYCbCr420Planar: planarImage->planeP[0] = bufferP; bufferP += planarImage->width * planarImage->height; planarImage->planeP[1] = bufferP; bufferP += planarImage->width * planarImage->height / 4; planarImage->planeP[2] = bufferP; break; case palmCodecImageYCbCr422Planar: planarImage->planeP[0] = bufferP; bufferP += planarImage->width * planarImage->height; planarImage->planeP[1] = bufferP; bufferP += planarImage->width * planarImage->height / 2; planarImage->planeP[2] = bufferP; break; } } /** * Laos a frame from file */ static Err PrvLoadFrameFromFile(FileRef fileRef, PalmImagePlanarType *planarImage, UInt32 imageFormat) { Err err = errNone; UInt32 read = 0; // UInt32 width = 0; UInt32 height = 0; UInt32 YHeight = 0; UInt32 YWidth = 0; UInt32 CHeight = 0; UInt32 CWidth = 0; UInt8 *planeP = NULL; switch(imageFormat) { case palmCodecImageYCbCr420Planar: YWidth = planarImage->width; YHeight = planarImage->height; CWidth = planarImage->width / 2; CHeight = planarImage->height / 2; break; case palmCodecImageYCbCr422Planar: YWidth = planarImage->width; YHeight = planarImage->height; CWidth = planarImage->width / 2; CHeight = planarImage->height; break; default: err = appErrorClass; goto Done; } // Load the Y components planeP = planarImage->planeP[0]; for(height = 0; height < YHeight; height++) { err = VFSFileRead(fileRef, YWidth, planeP, &read); if(err || read != YWidth) { err = vfsErrFileEOF; goto Done; } planeP += YWidth; } // Load the Cb components planeP = planarImage->planeP[1]; for(height = 0; height < CHeight; height++) { err = VFSFileRead(fileRef,CWidth, planeP, &read); if(err || read != CWidth) { err = vfsErrFileEOF; goto Done; } planeP += CWidth; } // Load the Cr components planeP = planarImage->planeP[2]; for(height = 0; height < CHeight; height++) { err = VFSFileRead(fileRef, CWidth, planeP, &read); if(err || read != CWidth) { err = vfsErrFileEOF; goto Done; } planeP += CWidth; } Done: return err; } #if 0 #pragma mark - #endif /** * Run the MPEG4 Decoder * * The loop option is done by resetting the decoder since the * next loop will have to read again the VO & VOL header. */ static Err PrvStartDecoder() { Err err = errNone; EventType event; UInt32 inSize = 0; UInt32 outSize = 0; Boolean useFtr = false; UInt32 inputBufferSize = 0; UInt8 *inputBufferP = NULL; UInt8 *currentBufferP = NULL; // UInt8 *offScreenP = NULL; // WinHandle offScreenH = NULL; RectangleType goButtonBounds; PalmImagePlanarType planarImage; PalmImagePlanarType *swapImageP = NULL; PalmImageParamType *imageParamP = NULL; UInt32 read = 0; // Make sure atack data is clean MemSet(&planarImage, sizeof(PalmImagePlanarType), 0); // Read parameters and check err = PrvReadParameters(false); if(err) goto Done; // Open the input and output files err = PrvFileOpenInput(); if(err) goto Done; if(gMPEG4Info.outputToSD) { err = PrvFileOpenOutput(); if(err) goto Done; } // Allocate image structure imageParamP = (PalmImageParamType*)MemPtrNew(sizeof(PalmImageParamType)); if(!imageParamP) { err = memErrNotEnoughSpace; FrmCustomAlert(ErrorAlert, "Not enough memory", NULL, NULL); goto Done; } MemSet(imageParamP, sizeof(PalmImageParamType), 0); // Allocate swap image swapImageP = (PalmImagePlanarType*)MemPtrNew(sizeof(PalmImagePlanarType)); if(!swapImageP) { err = memErrNotEnoughSpace; FrmCustomAlert(ErrorAlert, "Not enough memory", NULL, NULL); goto Done; } MemSet(swapImageP, sizeof(PalmImagePlanarType), 0); // Set Image Params imageParamP->width = 0; // Don't care imageParamP->height = 0; // Don't care imageParamP->rowByte = 0; // Don't care imageParamP->endianess = 0; // Don't care // Create the MPEG4 Decoding session //err = CodecMgrCreateSession(gMPEG4Info.codecMgrLibRefNum, palmCodecVideoMPEG4, NULL, gMPEG4Info.imageFormat, imageParamP, &gMPEG4Info.session); err = CodecMgrCreateSessionByID(gMPEG4Info.codecMgrLibRefNum, palmCodecVideoMPEG4, NULL, gMPEG4Info.imageFormat, imageParamP, 'mp4d', 0, &gMPEG4Info.session); if(err) { FrmCustomAlert(ErrorAlert, "Unable to create session", NULL, NULL); goto Done; } // Set the planar image planarImage.width = gMPEG4Info.width; planarImage.height = gMPEG4Info.height; planarImage.planeCount = 3; switch(gMPEG4Info.imageFormat) { case palmCodecImageYCbCr420Planar: planarImage.planeP[0] = Allocate( (3 * planarImage.width * planarImage.height) / 2); planarImage.planeP[1] = (UInt8*)planarImage.planeP[0] + planarImage.width * planarImage.height; planarImage.planeP[2] = (UInt8*)planarImage.planeP[1] + (planarImage.width * planarImage.height / 4); planarImage.planeWidth[0] = planarImage.width; planarImage.planeWidth[1] = planarImage.width / 2; planarImage.planeWidth[2] = planarImage.width / 2; break; default: FrmCustomAlert(ErrorAlert, "Bad image format", NULL, NULL); goto Done; break; } if(!planarImage.planeP[0]) { err = memErrNotEnoughSpace; FrmCustomAlert(ErrorAlert, "Not enough memory", NULL, NULL); goto Done; } // Use a byte-swapped copy for the CPM swapImageP->width = Swap32(planarImage.width); swapImageP->height = Swap32(planarImage.height); swapImageP->planeCount = Swap32(planarImage.planeCount); swapImageP->planeP[0] = (void*)Swap32(planarImage.planeP[0]); swapImageP->planeP[1] = (void*)Swap32(planarImage.planeP[1]); swapImageP->planeP[2] = (void*)Swap32(planarImage.planeP[2]); swapImageP->planeWidth[0] = Swap32(planarImage.planeWidth[0]); swapImageP->planeWidth[1] = Swap32(planarImage.planeWidth[1]); swapImageP->planeWidth[2] = Swap32(planarImage.planeWidth[2]); // Running from file if(gMPEG4Info.loadFileInMemory) { // Get the file size err = VFSFileSize(gMPEG4Info.inputFileRef, &inputBufferSize); if(err || !inputBufferSize) goto Done; currentBufferP = inputBufferP = (UInt8*)Allocate(inputBufferSize); if(!inputBufferP) { // Try with FtrPtr err = FtrPtrNew(appFileCreator, 0, inputBufferSize, (void*)&inputBufferP); if(err) { err = memErrNotEnoughSpace; FrmCustomAlert(ErrorAlert, "Not enough memory", NULL, NULL); goto Done; } currentBufferP = inputBufferP; useFtr = true; } // Load the file if(!PrvLoadFileInMemory(gMPEG4Info.inputFileRef, inputBufferSize, inputBufferP, useFtr)) goto Done; } else { // Allocate buffer for VFS inputBufferP = (UInt8*)Allocate(DATA_BUFFER_SIZE); // Load first input buffer currentBufferP = inputBufferP; err = VFSFileRead(gMPEG4Info.inputFileRef, DATA_BUFFER_SIZE, inputBufferP, &inputBufferSize); if(err) goto Done; } // Get the bounds for the Go button FrmGetObjectBounds(FrmGetActiveForm(), FrmGetObjectIndex(FrmGetActiveForm(),MainGoButton), &goButtonBounds); // LCD Overlay if( gUseOverlay) { LcdOverlayLibSettingType overlaySetting; RectangleType rect = { { 0, 0 }, { 0, 0 } }; rect.extent.x = planarImage.width / 2; rect.extent.y = planarImage.height / 2; overlaySetting.type = kLcdOverlayLibDataFormatYCbCr420Planar; err = LcdOverlayLibControl(gLCDOverlayRefNum, kLcdOverlayLibCtrlFormatSet, (void*)&overlaySetting); err |= LcdOverlayLibControl(gLCDOverlayRefNum, kLcdOverlayLibCtrlRectSet, &rect); err |= LcdOverlayLibControl(gLCDOverlayRefNum, kLcdOverlayLibCtrlStart, NULL); if(err) { FrmCustomAlert(ErrorAlert, "LCD Overlay Init failed!!!", NULL, NULL); goto Done; } } // Get the current time here we we go! gFrameCount = 0; gRateControl = sysTicksPerSecond / 15; // Rate control: 15 FPS gStartTicks = gLastTick = TimGetTicks(); // Decode Frames while(true) { // Set input params inSize = inputBufferSize; outSize = sizeof(PalmImagePlanarType); // Check the event queue EvtGetEvent(&event, 0); if(event.eType == penDownEvent && RctPtInRectangle(event.screenX, event.screenY, &goButtonBounds)) goto Done; // Decode a frame err = CodecMgrEncodeDecode(gMPEG4Info.codecMgrLibRefNum, gMPEG4Info.session, currentBufferP, &inSize, swapImageP, &outSize); switch(err) { //UInt32 read = 0; case kCodecSyncNotFound: if(gMPEG4Info.loadFileInMemory) { if(gMPEG4Info.loop) { err=VFSFileSize(gMPEG4Info.inputFileRef, &inputBufferSize); if(err!=errNone) return err; currentBufferP = inputBufferP; err = CodecMgrResetSession(gMPEG4Info.codecMgrLibRefNum, gMPEG4Info.session); } else { FrmCustomAlert(InfoAlert, "Decoding done!", NULL, NULL); goto Done; } } else { // Reload the buffer MemMove(inputBufferP, currentBufferP, inputBufferSize); err = VFSFileRead(gMPEG4Info.inputFileRef, DATA_BUFFER_SIZE - inputBufferSize, inputBufferP + inputBufferSize, &read); if(!read) { if(gMPEG4Info.loop) { VFSFileSeek(gMPEG4Info.inputFileRef, vfsOriginBeginning, 0); currentBufferP = inputBufferP; err=VFSFileRead(gMPEG4Info.inputFileRef, DATA_BUFFER_SIZE, inputBufferP, &inputBufferSize); if(err!=errNone) return err; err = CodecMgrResetSession(gMPEG4Info.codecMgrLibRefNum, gMPEG4Info.session); } else { if(gUseOverlay) err = LcdOverlayLibControl(gLCDOverlayRefNum, kLcdOverlayLibCtrlStop, NULL); FrmCustomAlert(InfoAlert, "Decoding done!", NULL, NULL); goto Done; } } // Set current buffer inputBufferSize += read; currentBufferP = inputBufferP; } break; // Real error case kCodecMgrLibNotSupported: case kCodecErrBadParam: if(gUseOverlay) err = LcdOverlayLibControl(gLCDOverlayRefNum, kLcdOverlayLibCtrlStop, NULL); FrmCustomAlert(InfoAlert, "Decoding done!", NULL, NULL); goto Done; default: break; } // Update the input buffer size inputBufferSize -= inSize; currentBufferP += inSize; // Update the Frame counter if(outSize) { gFrameCount++; if(gUseOverlay) { err = LcdOverlayLibControl(gLCDOverlayRefNum, kLcdOverlayLibCtrlDraw, planarImage.planeP[0]); } if(gMPEG4Info.outputToSD) { UInt32 written = 0; UInt32 YSize = planarImage.width * planarImage.height; UInt32 CSize = planarImage.width * planarImage.height / 4; // Write the Y components err = VFSFileWrite(gMPEG4Info.outputFileRef, YSize, planarImage.planeP[0], &written); if(err || written != YSize) { FrmCustomAlert(ErrorAlert, "Error during file write!", NULL, NULL); goto Done; } // Write the Cb components err = VFSFileWrite(gMPEG4Info.outputFileRef, CSize, planarImage.planeP[1], &written); if(err || written != CSize) { FrmCustomAlert(ErrorAlert, "Error during file write!", NULL, NULL); goto Done; } // Write the Cr components err = VFSFileWrite(gMPEG4Info.outputFileRef, CSize, planarImage.planeP[2], &written); if(err || written != CSize) { FrmCustomAlert(ErrorAlert, "Error during file write!", NULL, NULL); goto Done; } } } #if USE_RATE_CONTROL == 1 if((TimGetTicks() - gLastTick) < gRateControl) SysTaskDelay(gRateControl - TimGetTicks() + gLastTick); #endif // Update the UI frame count PrvUpdateUI(); } Done: //DEADCODE, not reachable // if(offScreenH) // WinDeleteWindow(offScreenH, false); if(inputBufferP){ if(useFtr) FtrPtrFree(appFileCreator, 0); else MemPtrFree(inputBufferP); } if(planarImage.planeP[0]) MemPtrFree(planarImage.planeP[0]); if(imageParamP) MemPtrFree(imageParamP); if(swapImageP) MemPtrFree(swapImageP); // Close overlay if( gUseOverlay) err = LcdOverlayLibControl(gLCDOverlayRefNum, kLcdOverlayLibCtrlStop, NULL); // Delete the session if(gMPEG4Info.session) CodecMgrDeleteSession(gMPEG4Info.codecMgrLibRefNum, &gMPEG4Info.session); // Close files if(gMPEG4Info.inputFileRef) { VFSFileClose(gMPEG4Info.inputFileRef); gMPEG4Info.inputFileRef = NULL; } if(gMPEG4Info.outputFileRef) { VFSFileClose(gMPEG4Info.outputFileRef); gMPEG4Info.outputFileRef = NULL; } // Quick refresh if(gUseOverlay) FrmDrawForm(FrmGetActiveForm()); return err; } /** * Run the MPEG4 Encoder * * The loop option is done by simply rewinding the file or stream. */ static Err PrvStartEncoder() { Err err = errNone; EventType event; Boolean useFtr = false; UInt32 inSize = 0; UInt32 outSize = 0; UInt32 inputBufferSize = 0; UInt32 fileOffset = 0; UInt8 *inputBufferP = NULL; UInt8 *outputBufferP = NULL; UInt8 *savedPlaneP[3] = { NULL, NULL, NULL }; RectangleType goButtonBounds; PalmImagePlanarType planarImage; PalmImagePlanarType *swapImageP = NULL; PalmImageParamType *imageParamP = NULL; PalmVideoMPEG4EncodeParamType *mpeg4ParamP = NULL; // Make sure atack data is clean MemSet(&planarImage, sizeof(PalmImagePlanarType), 0); // Read parameters and check err = PrvReadParameters(false); if(err) goto Done; // Open the input and output files err = PrvFileOpenInput(); if(err) goto Done; if(gMPEG4Info.outputToSD) { err = PrvFileOpenOutput(); if(err) goto Done; } // Allocate image structure imageParamP = (PalmImageParamType*)MemPtrNew(sizeof(PalmImageParamType)); if(!imageParamP) { err = memErrNotEnoughSpace; FrmCustomAlert(ErrorAlert, "Not enough memory", NULL, NULL); goto Done; } MemSet(imageParamP, sizeof(PalmImageParamType), 0); // Allocate swap image swapImageP = (PalmImagePlanarType*)MemPtrNew(sizeof(PalmImagePlanarType)); if(!swapImageP) { err = memErrNotEnoughSpace; FrmCustomAlert(ErrorAlert, "Not enough memory", NULL, NULL); goto Done; } MemSet(swapImageP, sizeof(PalmImagePlanarType), 0); // Allocate MPEG4 param mpeg4ParamP = (PalmVideoMPEG4EncodeParamType*)MemPtrNew(sizeof(PalmVideoMPEG4EncodeParamType)); if(!mpeg4ParamP) { err = memErrNotEnoughSpace; FrmCustomAlert(ErrorAlert, "Not enough memory", NULL, NULL); goto Done; } MemSet(mpeg4ParamP, sizeof(PalmVideoMPEG4EncodeParamType), 0); // Set Image Params imageParamP->width = Swap32(gMPEG4Info.width); imageParamP->height = Swap32(gMPEG4Info.height); imageParamP->rowByte = 0; // Don't care imageParamP->endianess = 0; // Don't care // Set MPEG4 Params mpeg4ParamP->profile = 0; // Don't care for now mpeg4ParamP->volWidth = Swap32(gMPEG4Info.width); mpeg4ParamP->volHeight = Swap32(gMPEG4Info.height); mpeg4ParamP->keyFrame = Swap32(gMPEG4Info.keyFrame); mpeg4ParamP->frameRate = Swap32(gMPEG4Info.frameRate); mpeg4ParamP->bitRate = Swap32(gMPEG4Info.bitRate); mpeg4ParamP->algorithm = Swap32(palmCodecMPEG4MVFAST); mpeg4ParamP->IVOPQuantization = Swap32(gMPEG4Info.IQuantization); mpeg4ParamP->VOVOLVersionID = Swap32(2); // Set the planar image planarImage.width = gMPEG4Info.width; planarImage.height = gMPEG4Info.height; switch(gMPEG4Info.imageFormat) { case palmCodecImageYCbCr420Planar: planarImage.planeP[0] = Allocate(planarImage.width * planarImage.height); planarImage.planeP[1] = Allocate(planarImage.width * planarImage.height / 4); planarImage.planeP[2] = Allocate(planarImage.width * planarImage.height / 4); planarImage.planeWidth[0] = planarImage.width; planarImage.planeWidth[1] = planarImage.width / 2; planarImage.planeWidth[2] = planarImage.width / 2; break; case palmCodecImageYCbCr422Planar: planarImage.planeP[0] = Allocate(planarImage.width * planarImage.height); planarImage.planeP[1] = Allocate(planarImage.width * planarImage.height / 2); planarImage.planeP[2] = Allocate(planarImage.width * planarImage.height / 2); planarImage.planeWidth[0] = planarImage.width; planarImage.planeWidth[1] = planarImage.width / 2; planarImage.planeWidth[2] = planarImage.width / 2; break; default: FrmCustomAlert(ErrorAlert, "Bad image format", NULL, NULL); goto Done; break; } // Check for memory if(!planarImage.planeP[0] || !planarImage.planeP[1] || !planarImage.planeP[2]) { err = memErrNotEnoughSpace; FrmCustomAlert(ErrorAlert, "Not enough memory", NULL, NULL); goto Done; } // Save plane pointers since they can be modified savedPlaneP[0] = planarImage.planeP[0]; savedPlaneP[1] = planarImage.planeP[1]; savedPlaneP[2] = planarImage.planeP[2]; // Create a copy for the CPM swapImageP->width = Swap32(planarImage.width); swapImageP->height = Swap32(planarImage.height); swapImageP->planeP[0] = (void*)Swap32(planarImage.planeP[0]); swapImageP->planeP[1] = (void*)Swap32(planarImage.planeP[1]); swapImageP->planeP[2] = (void*)Swap32(planarImage.planeP[2]); swapImageP->planeWidth[0] = Swap32(planarImage.planeWidth[0]); swapImageP->planeWidth[1] = Swap32(planarImage.planeWidth[1]); swapImageP->planeWidth[2] = Swap32(planarImage.planeWidth[2]); // Create the MPEG4 Encoding session err = CodecMgrCreateSession(gMPEG4Info.codecMgrLibRefNum, gMPEG4Info.imageFormat, imageParamP, palmCodecVideoMPEG4, mpeg4ParamP, &gMPEG4Info.session); if(err) { FrmCustomAlert(ErrorAlert, "Unable to create session", NULL, NULL); goto Done; } // Allocate output buffer outputBufferP = (UInt8*)Allocate(planarImage.width * planarImage.height * 2); if(!outputBufferP) { err = memErrNotEnoughSpace; FrmCustomAlert(ErrorAlert, "Not enough memory", NULL, NULL); goto Done; } MemSet(mpeg4ParamP, sizeof(PalmVideoMPEG4EncodeParamType), 0); // Running from file if(gMPEG4Info.loadFileInMemory) { // Get the file size err = VFSFileSize(gMPEG4Info.inputFileRef, &inputBufferSize); if(err || !inputBufferSize) goto Done; // Might work.... inputBufferP = (UInt8*)Allocate(inputBufferSize); if(!inputBufferP) { // Try with FtrPtr err = FtrPtrNew(appFileCreator, 0, inputBufferSize, (void*)&inputBufferP); if(err) { err = memErrNotEnoughSpace; FrmCustomAlert(ErrorAlert, "Not enough memory", NULL, NULL); goto Done; } useFtr = true; } // Load the file if(!PrvLoadFileInMemory(gMPEG4Info.inputFileRef, inputBufferSize, inputBufferP, useFtr)) goto Done; } // Get the bounds for the Go button FrmGetObjectBounds(FrmGetActiveForm(), FrmGetObjectIndex(FrmGetActiveForm(),MainGoButton), &goButtonBounds); // Get the current time here we we go! gFrameCount = 0; gStartTicks = gLastTick = TimGetTicks(); // Decode Frames while(true) { // Load raw data from input file if(gMPEG4Info.loadFileInMemory) { if( gMPEG4Info.loop && fileOffset < inputBufferSize) fileOffset = 0; if( fileOffset < inputBufferSize ) { PrvLoadFrameFromMemory(inputBufferP + fileOffset, &planarImage, gMPEG4Info.imageFormat); swapImageP->planeP[0] = (void*)Swap32(planarImage.planeP[0]); swapImageP->planeP[1] = (void*)Swap32(planarImage.planeP[1]); swapImageP->planeP[2] = (void*)Swap32(planarImage.planeP[2]); switch(gMPEG4Info.imageFormat) { case palmCodecImageYCbCr420Planar: fileOffset += ( 3 * planarImage.width * planarImage.height ) / 2; break; case palmCodecImageYCbCr422Planar: fileOffset += ( 2 * planarImage.width * planarImage.height ); break; } } else { FrmCustomAlert(InfoAlert, "Encoding done!", NULL, NULL); goto Done; } } else { // Load from file err = PrvLoadFrameFromFile(gMPEG4Info.inputFileRef, &planarImage, gMPEG4Info.imageFormat); if(err) { if(gMPEG4Info.loop) { VFSFileSeek(gMPEG4Info.inputFileRef, vfsOriginBeginning, 0); err = PrvLoadFrameFromFile(gMPEG4Info.inputFileRef, &planarImage, gMPEG4Info.imageFormat); } else { FrmCustomAlert(InfoAlert, "Encoding done!", NULL, NULL); goto Done; } } swapImageP->planeP[0] = (void*)Swap32(planarImage.planeP[0]); swapImageP->planeP[1] = (void*)Swap32(planarImage.planeP[1]); swapImageP->planeP[2] = (void*)Swap32(planarImage.planeP[2]); } inSize = sizeof(PalmImagePlanarType); outSize = planarImage.width * planarImage.height * 2; // Check the event queue EvtGetEvent(&event, 0); if(event.eType == penDownEvent && RctPtInRectangle(event.screenX, event.screenY, &goButtonBounds)) goto Done; // Encode one frame err = CodecMgrEncodeDecode(gMPEG4Info.codecMgrLibRefNum, gMPEG4Info.session, swapImageP, &inSize, outputBufferP, &outSize); // Update the Frame counter if(outSize) { gFrameCount++; if(gMPEG4Info.outputToSD) { UInt32 written = 0; // Write the Buffer err = VFSFileWrite(gMPEG4Info.outputFileRef, outSize, outputBufferP, &written); if(err || written != outSize) { FrmCustomAlert(ErrorAlert, "Error during file write!", NULL, NULL); goto Done; } } } // Update the UI frame count PrvUpdateUI(); } Done: if(inputBufferP) { if(useFtr) FtrPtrFree(appFileCreator, 0); else MemPtrFree(inputBufferP); } if(outputBufferP) MemPtrFree(outputBufferP); if(savedPlaneP[0]) MemPtrFree(savedPlaneP[0]); if(savedPlaneP[1]) MemPtrFree(savedPlaneP[1]); if(savedPlaneP[2]) MemPtrFree(savedPlaneP[2]); if(imageParamP) MemPtrFree(imageParamP); if(swapImageP) MemPtrFree(swapImageP); // Delete the session if(gMPEG4Info.session) CodecMgrDeleteSession(gMPEG4Info.codecMgrLibRefNum, &gMPEG4Info.session); // Close files if(gMPEG4Info.inputFileRef) { VFSFileClose(gMPEG4Info.inputFileRef); gMPEG4Info.inputFileRef = NULL; } if(gMPEG4Info.outputFileRef) { VFSFileClose(gMPEG4Info.outputFileRef); gMPEG4Info.outputFileRef = NULL; } return err; } #if 0 #pragma mark - #endif /** * Initialize the form */ static void MainFormInit(FormType *frmP) { Char text[32]; FieldType *fldP = NULL; ControlType *ctlP = NULL; // Set the current mode if(gMPEG4Info.encoding) { ctlP = FrmGetPtr(frmP, MainEncodePushButton); CtlSetValue(ctlP, true); } else { ctlP = FrmGetPtr(frmP, MainDecodePushButton); CtlSetValue(ctlP, true); } // Set encoder specfic params StrIToA(text, gMPEG4Info.searchRange); fldP = FrmGetPtr(frmP, MainSearchRangeField); SetFieldTextFromStr(fldP, text, false); StrIToA(text, gMPEG4Info.bitRate); fldP = FrmGetPtr(frmP, MainBitRateField); SetFieldTextFromStr(fldP, text, false); StrIToA(text, gMPEG4Info.keyFrame); fldP = FrmGetPtr(frmP, MainKeyFrameField); SetFieldTextFromStr(fldP, text, false); StrIToA(text, gMPEG4Info.IQuantization); fldP = FrmGetPtr(frmP, MainIQuantizationField); SetFieldTextFromStr(fldP, text, false); // Set Width and Height StrIToA(text, gMPEG4Info.width); fldP = FrmGetPtr(frmP, MainWidthField); SetFieldTextFromStr(fldP, text, false); StrIToA(text, gMPEG4Info.height); fldP = FrmGetPtr(frmP, MainHeightField); SetFieldTextFromStr(fldP, text, false); // Set files if(gMPEG4Info.inputFileName[0]!=NULL) { fldP = FrmGetPtr(frmP, MainInputField); SetFieldTextFromStr(fldP, gMPEG4Info.inputFileName, false); } if(gMPEG4Info.outputFileName[0]!=NULL) { fldP = FrmGetPtr(frmP, MainOutputField); SetFieldTextFromStr(fldP, gMPEG4Info.outputFileName, false); } // Set the checkbox ctlP = FrmGetPtr(frmP, MainLoadInMemCheckbox); CtlSetValue(ctlP, gMPEG4Info.loadFileInMemory); ctlP = FrmGetPtr(frmP, MainToSDCheckbox); CtlSetValue(ctlP, gMPEG4Info.outputToSD); ctlP = FrmGetPtr(frmP, MainLoopCheckbox); CtlSetValue(ctlP, gMPEG4Info.loop); // Overlay if(gLCDOverlayRefNum == sysInvalidRefNum) FrmHideObject(frmP, FrmGetObjectIndex(frmP, MainOverlayPushButton)); else { ctlP = FrmGetPtr(frmP, MainOverlayPushButton); CtlSetValue(ctlP, gUseOverlay); } } /** * MainFormHandleEvent */ static Boolean MainFormHandleEvent(EventType * eventP) { Boolean handled = false; FormType *frmP = FrmGetActiveForm(); FieldType *fldP = NULL; switch(eventP->eType) { case frmOpenEvent: MainFormInit(frmP); FrmDrawForm(frmP); PrvDrawLoadingRect(); FrmNavObjectTakeFocus(frmP, MainInputField); handled = true; break; case frmUpdateEvent: break; case popSelectEvent: switch(eventP->data.popSelect.controlID) { case MainFormatPopTrigger: switch(eventP->data.popSelect.selection) { case 0: gMPEG4Info.imageFormat = palmCodecImageYCbCr420Planar; break; case 1: gMPEG4Info.imageFormat = palmCodecImageYCbCr422Planar; break; } break; case MainQuickPickPopTrigger: switch(eventP->data.popSelect.selection) { case 0: gMPEG4Info.width = 320; gMPEG4Info.height = 240; break; case 1: gMPEG4Info.width = 176; gMPEG4Info.height = 144; break; case 2: gMPEG4Info.width = 352; gMPEG4Info.height = 288; break; } { Char text[32]; // Set Width and Height StrIToA(text, gMPEG4Info.width); fldP = FrmGetPtr(frmP, MainWidthField); SetFieldTextFromStr(fldP, text, true); StrIToA(text, gMPEG4Info.height); fldP = FrmGetPtr(frmP, MainHeightField); SetFieldTextFromStr(fldP, text, true); } break; } break; case ctlSelectEvent: switch(eventP->data.ctlSelect.controlID) { case MainInputGraphicButton: // Sets the callback function and return form if(PrvReadParameters(gMPEG4Info.encoding)==errNone) { // Store before switching form FileBrowserSetCallback(&PrvGetInputFileInfoCallback, MainForm); FrmGotoForm(FileBrowserForm); handled = true; } break; case MainEncodePushButton: gMPEG4Info.encoding = true; break; case MainDecodePushButton: gMPEG4Info.encoding = false; break; case MainLoadInMemCheckbox: gMPEG4Info.loadFileInMemory = CtlGetValue(eventP->data.ctlSelect.pControl); break; case MainToSDCheckbox: gMPEG4Info.outputToSD = CtlGetValue(eventP->data.ctlSelect.pControl); break; case MainLoopCheckbox: gMPEG4Info.loop = CtlGetValue(eventP->data.ctlSelect.pControl); break; case MainGoButton: CtlSetLabel(FrmGetPtr(FrmGetActiveForm(), MainGoButton), gGOButtonLabel[1]); if(gMPEG4Info.encoding) PrvStartEncoder(); else PrvStartDecoder(); CtlSetLabel(FrmGetPtr(FrmGetActiveForm(), MainGoButton), gGOButtonLabel[0]); PrvDrawLoadingRect(); handled = true; break; case MainStartColorGadget: { IndexedColorType colorIndex = WinRGBToIndex(&gStartColor); if( UIPickColor(&colorIndex, NULL, UIPickColorStartPalette, "End Color", NULL) ) WinIndexToRGB(colorIndex, &gStartColor); FrmDrawForm(FrmGetActiveForm()); PrvDrawLoadingRect(); handled = true; } break; case MainEndColorGadget: { IndexedColorType colorIndex = WinRGBToIndex(&gEndColor); if( UIPickColor(&colorIndex, NULL, UIPickColorStartPalette, "End Color", NULL) ) WinIndexToRGB(colorIndex, &gEndColor); FrmDrawForm(FrmGetActiveForm()); PrvDrawLoadingRect(); handled = true; } break; case MainOverlayPushButton: gUseOverlay = eventP->data.ctlSelect.on; FrmDrawForm(FrmGetActiveForm()); PrvDrawLoadingRect(); handled = true; break; } break; case menuEvent: HandleMenu(eventP->data.menu.itemID); FrmDrawForm(frmP); handled = true; break; default: break; } return handled; } /** * AppHandleEvent */ static Boolean AppHandleEvent(EventType * eventP) { UInt16 formId; FormType * frmP; if (eventP->eType == frmLoadEvent) { formId = eventP->data.frmLoad.formID; frmP = FrmInitForm(formId); FrmSetActiveForm(frmP); switch (formId) { case MainForm: FrmSetEventHandler(frmP, MainFormHandleEvent); FrmSetGadgetHandler(frmP, FrmGetObjectIndex(frmP, MainStartColorGadget), ColorButtonGadgetHandler); FrmSetGadgetHandler(frmP, FrmGetObjectIndex(frmP, MainEndColorGadget), ColorButtonGadgetHandler); break; case FileBrowserForm: FrmSetEventHandler(frmP, FileBrowserFormHandleEvent); break; default: break; } return true; } return false; } /** * AppEventLoop */ static void AppEventLoop(void) { UInt16 error = errNone; EventType event; do { EvtGetEvent(&event, evtWaitForever); if (! SysHandleEvent(&event)) if (! MenuHandleEvent(0, &event, &error)) if (! AppHandleEvent(&event)) FrmDispatchEvent(&event); } while (event.eType != appStopEvent); } /** * AppStart * Called when the application starts. */ static Err AppStart(void) { Err err = errNone; // Clear the global MemSet(&gMPEG4Info, sizeof(MPEG4Info), 0); // Try to find the hardware utils library err = SysLibFind(kCodecMgrLibName, &gMPEG4Info.codecMgrLibRefNum); if (err != errNone) { err = SysLibLoad(kCodecMgrLibType, kCodecMgrLibCreator, &gMPEG4Info.codecMgrLibRefNum); if(err == errNone) err = CodecMgrOpen(gMPEG4Info.codecMgrLibRefNum); } if(err) { FrmAlert(NoCPMLibAlert); goto Done; } // Try to load LCD Overlay library err = SysLibFind(kLcdOverlayLibName, &gLCDOverlayRefNum); if (err != errNone) { err = SysLibLoad(kLcdOverlayLibType, kLcdOverlayLibCreator, &gLCDOverlayRefNum); if(err == errNone) err = LcdOverlayLibOpen(gLCDOverlayRefNum); } // Don't generate an error for the Overlay library err = errNone; // Set default values gMPEG4Info.inputFileRef = NULL; gMPEG4Info.outputFileRef = NULL; gMPEG4Info.session = NULL; gMPEG4Info.width = 320; gMPEG4Info.height = 240; gMPEG4Info.frameRate = 15; gMPEG4Info.bitRate = 0; gMPEG4Info.keyFrame = 20; gMPEG4Info.IQuantization = 16; gMPEG4Info.imageFormat = palmCodecImageYCbCr420Planar; gMPEG4Info.searchRange = 10; gMPEG4Info.loadFileInMemory = true; gMPEG4Info.outputToSD = false; gMPEG4Info.encoding = true; gMPEG4Info.loop = false; StrCopy(gMPEG4Info.outputFileName, "/default.out"); // Switch to 16 bit color WinScreenMode(winScreenModeGet, NULL, NULL, &gPreviousScreenDepth, NULL); WinScreenMode(winScreenModeSet, NULL, NULL, &gScreenDepth, NULL); Done: return err; } /** * AppStop * Called when the application stops. */ static void AppStop(void) { Err err = errNone; FrmCloseAllForms(); // Close files if(gMPEG4Info.inputFileRef) VFSFileClose(gMPEG4Info.inputFileRef); if(gMPEG4Info.outputFileRef) VFSFileClose(gMPEG4Info.outputFileRef); // Close the library if(gMPEG4Info.codecMgrLibRefNum != sysInvalidRefNum) { err = CodecMgrClose(gMPEG4Info.codecMgrLibRefNum); if (err == errNone) err = SysLibRemove(gMPEG4Info.codecMgrLibRefNum); } if(gLCDOverlayRefNum != sysInvalidRefNum) { err = LcdOverlayLibClose(gLCDOverlayRefNum); if (err == errNone) err = SysLibRemove(gLCDOverlayRefNum); } // Restore previous color mode WinScreenMode(winScreenModeSet, NULL, NULL, &gPreviousScreenDepth, NULL); } // // RomVersionCompatible // static Err RomVersionCompatible(UInt32 requiredVersion, UInt16 launchFlags) { UInt32 romVersion; FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion); if (romVersion < requiredVersion) { if ((launchFlags & (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp)) == (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp)) { FrmAlert (RomIncompatibleAlert); if (romVersion < kPalmOS20Version) { AppLaunchWithCommand( sysFileCDefaultApp, sysAppLaunchCmdNormalLaunch, NULL); } } return sysErrRomIncompatible; } return errNone; } // // PilotMain // UInt32 PilotMain(UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags) { Err error; // SysNotifyParamType *notifyParamP = NULL; error = RomVersionCompatible (ourMinVersion, launchFlags); if (error) return (error); switch (cmd) { case sysAppLaunchCmdNormalLaunch: error = AppStart(); if (error) return error; FrmGotoForm(MainForm); AppEventLoop(); AppStop(); break; default: break; } //Done: return errNone; }