/*
* main.c
*
* ============================================================================
* Copyright (c) Texas Instruments Inc 2007
*
* Use of this software is controlled by the terms and conditions found in the
* license agreement under which this software has been supplied or provided.
* ============================================================================
*/
/* Standard Linux headers */
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <signal.h>
/* Davinci specific kernel headers */
#include <video/dm355fb.h>
/* Codec Engine headers */
#include <xdc/std.h>
#include <ti/sdo/ce/trace/gt.h>
#include <ti/sdo/ce/CERuntime.h>
#include <ti/sdo/ce/utils/trace/TraceUtil.h>
/* Demo headers */
#include <rendezvous.h>
#include <fifoutil.h>
#include <pause.h>
#include "encode.h"
#include "display.h"
#include "video.h"
#include "capture.h"
#include "writer.h"
#include "speech.h"
#include "ctrl.h"
/* The levels of initialization */
#define LOGSINITIALIZED 0x1
#define PAUSEOPENED 0x2
#define INITRENDEZVOUSOPENED 0x4
#define CLEANUPRENDEZVOUSOPENED 0x8
#define PRIMERENDEZVOUSOPENED 0x10
#define INFIFODISPLAYOPENED 0x20
#define OUTFIFODISPLAYOPENED 0x40
#define DISPLAYTHREADCREATED 0x80
#define INFIFOCAPTUREOPENED 0x100
#define OUTFIFOCAPTUREOPENED 0x200
#define CAPTURETHREADCREATED 0x400
#define INFIFOWRITEROPENED 0x800
#define OUTFIFOWRITEROPENED 0x1000
#define WRITERTHREADCREATED 0x2000
#define VIDEOTHREADCREATED 0x4000
#define SPEECHTHREADCREATED 0x8000
typedef struct Args {
SpeechEncoder speechEncoder;
VideoEncoder videoEncoder;
CaptureOperation captureOp;
SoundInput soundInput;
char *speechFile;
char *videoFile;
int imageWidth;
int imageHeight;
int videoBitRate;
int svideoInput;
int keyboard;
int time;
int interface;
} Args;
/* Global variable declarations for this application */
GlobalData gbl = { 0, 0, 0, 0, 0, NOSTD };
static void signalHandler(int signum)
{
gbl.quit = TRUE;
}
/******************************************************************************
* usage
******************************************************************************/
static void usage(void)
{
fprintf(stderr, "Usage: encode [options]nn"
"Options:n"
"-s | --speechfile Speech file to record ton"
"-v | --videofile Video file to record ton"
"-r | --resolution Video resolution ('width'x'height') [720x480]n"
"-b | --bitrate Bit rate to encode video at [variable]n"
"-d | --deinterlace Disable removal of interlacing artifacts from then"
" captured video frames before encoding [off]n"
"-x | --svideo Use s-video instead of composite video input [off]n"
"-l | --linein Use line in for encoding sound instead of mic [off]n"
"-k | --keyboard Enable keyboard interface [off]n"
"-t | --time Number of seconds to run the demo [infinite]n"
"-i | --interface Launch the demo interface when exiting [off]n"
"-h | --help Print this messagenn"
"You must supply at least a video or a speech file or bothn"
"with appropriate extensions for the file formats.nn");
}
/******************************************************************************
* parseArgs
******************************************************************************/
static void parseArgs(int argc, char *argv[], Args *argsp)
{
const char shortOptions[] = "s:v:r:b:zfdkxt:lih";
const struct option longOptions[] = {
{"speechfile", required_argument, NULL, 's'},
{"videofile", required_argument, NULL, 'v'},
{"resolution", required_argument, NULL, 'r'},
{"bitrate", required_argument, NULL, 'b'},
{"deinterlace", no_argument, NULL, 'd'},
{"svideo", no_argument, NULL, 'x'},
{"keyboard", no_argument, NULL, 'k'},
{"time", required_argument, NULL, 't'},
{"linein", no_argument, NULL, 'l'},
{"interface", no_argument, NULL, 'i'},
{"help", no_argument, NULL, 'h'},
{0, 0, 0, 0}
};
int index;
int c;
char *extension;
int imageWidth;
int imageHeight;
for (;;) {
c = getopt_long(argc, argv, shortOptions, longOptions, &index);
if (c == -1) {
break;
}
switch (c) {
case 0:
break;
case 's':
extension = rindex(optarg, '.');
if (extension == NULL) {
fprintf(stderr, "Speech file without extension: %sn",
optarg);
exit(EXIT_FAILURE);
}
if (strcmp(extension, ".g711") == 0) {
argsp->speechEncoder = G711_SPEECH_ENCODER;
}
else {
fprintf(stderr, "Unknown speech file extension: %sn",
extension);
exit(EXIT_FAILURE);
}
argsp->speechFile = optarg;
break;
case 'v':
extension = rindex(optarg, '.');
if (extension == NULL) {
fprintf(stderr, "Video file without extension: %sn",
optarg);
exit(EXIT_FAILURE);
}
if (strcmp(extension, ".264") == 0) {
argsp->videoEncoder = H264_VIDEO_ENCODER;
}
else if (strcmp(extension, ".mpeg4") == 0) {
argsp->videoEncoder = MPEG4_VIDEO_ENCODER;
}
else {
fprintf(stderr, "Unknown video file extension: %sn",
extension);
exit(EXIT_FAILURE);
}
argsp->videoFile = optarg;
break;
case 'r':
if (sscanf(optarg, "%dx%d", &imageWidth,
&imageHeight) != 2) {
fprintf(stderr, "Invalid resolution supplied (%s)n",
optarg);
usage();
exit(EXIT_FAILURE);
}
/* Sanity check resolution */
if (imageWidth < MIN_WIDTH || imageHeight < MIN_HEIGHT ||
imageWidth > D1_WIDTH || imageHeight > D1_HEIGHT) {
fprintf(stderr, "Video resolution must be between %dx%d "
"and %dx%dn", MIN_WIDTH, MIN_HEIGHT,
D1_WIDTH, D1_HEIGHT);
exit(EXIT_FAILURE);
}
/* Codecs only support multiples of 16 */
argsp->imageWidth = imageWidth & ~0xf;
argsp->imageHeight = imageHeight & ~0xf;
break;
case 'b':
argsp->videoBitRate = atoi(optarg);
break;
case 'd':
argsp->captureOp = CAPOP_COPY;
break;
case 'x':
argsp->svideoInput = TRUE;
break;
case 'k':
argsp->keyboard = TRUE;
break;
case 't':
argsp->time = atoi(optarg);
break;
case 'l':
argsp->soundInput = LINEIN_SOUND_INPUT;
break;
case 'i':
argsp->interface = TRUE;
break;
case 'h':
usage();
exit(EXIT_SUCCESS);
default:
usage();
exit(EXIT_FAILURE);
}
}
/* The smooth module only supports heights >= 144 */
if (argsp->captureOp == CAPOP_SMOOTH && argsp->imageHeight < 144) {
fprintf(stderr, "nOnly heights >= 144 supported with -dnn");
usage();
exit(EXIT_FAILURE);
}
/* Need at least one file to encode to */
if (!argsp->videoFile && !argsp->speechFile) {
usage();
exit(EXIT_FAILURE);
}
}
/******************************************************************************
* detectVideoStandard
******************************************************************************/
static int detectVideoStandard(void)
{
int fd;
int std;
/* Open video display device */
fd = open(FBVID_DEVICE, O_RDWR);
if (fd == -1) {
ERR("Failed to open fb device %s (%s)n", FBVID_DEVICE,
strerror(errno));
return FAILURE;
}
/* Query the display device driver for video standard chosen */
if (ioctl(fd, FBIO_GETSTD, &std) == -1) {
ERR("Failed to get video standard from display device drivern");
return FAILURE;
}
if ((std >> 16) == NTSC) {
DBG("NTSC selectedn");
gblSetYFactor(NTSCSTD);
}
else {
DBG("PAL selectedn");
gblSetYFactor(PALSTD);
}
close(fd);
return SUCCESS;
}
/******************************************************************************
* main
******************************************************************************/
int main(int argc, char *argv[])
{
unsigned int initMask = 0;
int status = EXIT_SUCCESS;
int numThreads;
struct sched_param schedParam;
Rendezvous_Obj rendezvousInit;
Rendezvous_Obj rendezvousCleanup;
Rendezvous_Obj rendezvousPrime;
Pause_Obj pause;
pthread_t displayThread;
pthread_t captureThread;
pthread_t writerThread;
pthread_t videoThread;
pthread_t speechThread;
DisplayEnv displayEnv;
CaptureEnv captureEnv;
WriterEnv writerEnv;
VideoEnv videoEnv;
SpeechEnv speechEnv;
CtrlEnv ctrlEnv;
pthread_attr_t attr;
void *ret;
Args args = {
0,
0,
CAPOP_SMOOTH,
MIC_SOUND_INPUT,
NULL,
NULL,
720,
480,
-1,
FALSE,
FALSE,
FOREVER,
FALSE
};
struct sigaction sigAction;
/* insure a clean shutdown if user types ctrl-c */
sigAction.sa_handler = signalHandler;
sigemptyset(&sigAction.sa_mask);
sigAction.sa_flags = 0;
sigaction(SIGINT, &sigAction, NULL);
/* Detect PAL or NTSC */
if (detectVideoStandard() == FAILURE) {
cleanup(EXIT_FAILURE);
}
args.imageHeight = D1_HEIGHT;
/* Parse the arguments given to the app and set the app environment */
parseArgs(argc, argv, &args);
printf("Encode demo started.n");
/* Initialize the mutex which protects the global data */
pthread_mutex_init(&gbl.mutex, NULL);
/* Set the priority of this whole process to max (requires root) */
setpriority(PRIO_PROCESS, 0, -20);
/* Initialize Codec Engine runtime */
CERuntime_init();
DBG("Codec Engine initializedn");
#if 0
/* Initialize the logs. Must be done after CERuntime_init() */
TraceUtil_start(ENGINE_NAME);
initMask |= LOGSINITIALIZED;
DBG("Logging initializedn");
#endif
/* Initialize the Pause object which syncronizes pausing of processing */
Pause_open(&pause);
initMask |= PAUSEOPENED;
DBG("Pause object openedn");
/* Determine the number of threads needing synchronization */
numThreads = 1;
if (args.videoFile) {
numThreads += 4;
}
if (args.speechFile) {
numThreads += 1;
}
/* Open the object which synchronizes the thread initialization */
Rendezvous_open(&rendezvousInit, numThreads);
initMask |= INITRENDEZVOUSOPENED;
DBG("Init rendezvous opened for %d threadsn", numThreads);
/* Open the object which synchronizes the thread cleanup */
Rendezvous_open(&rendezvousCleanup, numThreads);
initMask |= CLEANUPRENDEZVOUSOPENED;
DBG("Cleanup rendezvous opened for %d threadsn", numThreads);
/* Open the object which synchronizes the display priming (not used) */
Rendezvous_open(&rendezvousPrime, 1);
initMask |= PRIMERENDEZVOUSOPENED;
DBG("Priming rendezvous opened for 2 threadsn");
/* Initialize the thread attributes */
if (pthread_attr_init(&attr)) {
ERR("Failed to initialize thread attrsn");
cleanup(EXIT_FAILURE);
}
/* Force the thread to use custom scheduling attributes */
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) {
ERR("Failed to set schedule inheritance attributen");
cleanup(EXIT_FAILURE);
}
/* Set the thread to be fifo real time scheduled */
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
ERR("Failed to set FIFO scheduling policyn");
cleanup(EXIT_FAILURE);
}
/* Create the video threads if a file name is supplied */
if (args.videoFile) {
/* Open the display input fifo */
if (FifoUtil_open(&displayEnv.inFifo,
sizeof(DisplayBufferElement)) == FIFOUTIL_FAILURE) {
ERR("Failed to open input fifon");
cleanup(EXIT_FAILURE);
}
initMask |= INFIFODISPLAYOPENED;
/* Open the display output fifo */
if (FifoUtil_open(&displayEnv.outFifo,
sizeof(DisplayBufferElement)) == FIFOUTIL_FAILURE) {
ERR("Failed to open output fifon");
cleanup(EXIT_FAILURE);
}
initMask |= OUTFIFODISPLAYOPENED;
/* Set the display thread priority */
schedParam.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (pthread_attr_setschedparam(&attr, &schedParam)) {
ERR("Failed to set scheduler parametersn");
cleanup(EXIT_FAILURE);
}
/* Create the display thread */
displayEnv.hRendezvousInit = &rendezvousInit;
displayEnv.hRendezvousCleanup = &rendezvousCleanup;
displayEnv.hRendezvousPrime = &rendezvousPrime;
displayEnv.hPause = &pause;
if (pthread_create(&displayThread, &attr, displayThrFxn, &displayEnv)) {
ERR("Failed to create display threadn");
cleanup(EXIT_FAILURE);
}
initMask |= DISPLAYTHREADCREATED;
DBG("Display thread createdn");
/* Open the capture input fifo */
if (FifoUtil_open(&captureEnv.inFifo,
sizeof(CaptureBufferElement)) == FIFOUTIL_FAILURE) {
ERR("Failed to open capture input fifon");
cleanup(EXIT_FAILURE);
}
initMask |= INFIFOCAPTUREOPENED;
/* Open the capture output fifo */
if (FifoUtil_open(&captureEnv.outFifo,
sizeof(CaptureBufferElement)) == FIFOUTIL_FAILURE) {
ERR("Failed to open capture output fifon");
cleanup(EXIT_FAILURE);
}
initMask |= OUTFIFOCAPTUREOPENED;
/* Set the capture thread priority */
schedParam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
if (pthread_attr_setschedparam(&attr, &schedParam)) {
ERR("Failed to set scheduler parametersn");
cleanup(EXIT_FAILURE);
}
/* Create the capture thread */
captureEnv.hRendezvousInit = &rendezvousInit;
captureEnv.hRendezvousCleanup = &rendezvousCleanup;
captureEnv.hRendezvousPrime = &rendezvousPrime;
captureEnv.hPause = &pause;
captureEnv.hDisplayOutFifo = &displayEnv.outFifo;
captureEnv.hDisplayInFifo = &displayEnv.inFifo;
captureEnv.captureOp = args.captureOp;
captureEnv.svideoInput = args.svideoInput;
captureEnv.imageWidth = args.imageWidth;
captureEnv.imageHeight = args.imageHeight;
if (pthread_create(&captureThread, &attr, captureThrFxn, &captureEnv)) {
ERR("Failed to create capture threadn");
cleanup(EXIT_FAILURE);
}
initMask |= CAPTURETHREADCREATED;
DBG("Capture thread createdn");
/* Open the writer input fifo */
if (FifoUtil_open(&writerEnv.inFifo,
sizeof(WriterBufferElement)) == FIFOUTIL_FAILURE) {
ERR("Failed to open input writer fifon");
cleanup(EXIT_FAILURE);
}
initMask |= INFIFOWRITEROPENED;
/* Open the writer output fifo */
if (FifoUtil_open(&writerEnv.outFifo,
sizeof(WriterBufferElement)) == FIFOUTIL_FAILURE) {
ERR("Failed to open output writer fifon");
cleanup(EXIT_FAILURE);
}
initMask |= OUTFIFOWRITEROPENED;
/* Set the writer thread priority */
schedParam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
if (pthread_attr_setschedparam(&attr, &schedParam)) {
ERR("Failed to set scheduler parametersn");
cleanup(EXIT_FAILURE);
}
/* Create the writer thread */
writerEnv.hRendezvousInit = &rendezvousInit;
writerEnv.hRendezvousCleanup = &rendezvousCleanup;
writerEnv.hPause = &pause;
writerEnv.videoFile = args.videoFile;
if (pthread_create(&writerThread, &attr, writerThrFxn, &writerEnv)) {
ERR("Failed to create writer threadn");
cleanup(EXIT_FAILURE);
}
initMask |= WRITERTHREADCREATED;
DBG("Writer thread createdn");
/* Set the video thread priority */
schedParam.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (pthread_attr_setschedparam(&attr, &schedParam)) {
ERR("Failed to set scheduler parametersn");
cleanup(EXIT_FAILURE);
}
/* Create the video thread */
videoEnv.hRendezvousInit = &rendezvousInit;
videoEnv.hRendezvousCleanup = &rendezvousCleanup;
videoEnv.hPause = &pause;
videoEnv.hCaptureOutFifo = &captureEnv.outFifo;
videoEnv.hCaptureInFifo = &captureEnv.inFifo;
videoEnv.hWriterOutFifo = &writerEnv.outFifo;
videoEnv.hWriterInFifo = &writerEnv.inFifo;
videoEnv.videoEncoder = args.videoEncoder;
videoEnv.videoBitRate = args.videoBitRate;
videoEnv.imageWidth = args.imageWidth;
videoEnv.imageHeight = args.imageHeight;
if (pthread_create(&videoThread, &attr, videoThrFxn, &videoEnv)) {
ERR("Failed to create video threadn");
cleanup(EXIT_FAILURE);
}
initMask |= VIDEOTHREADCREATED;
}
/* Create the speech thread if a file name is supplied */
if (args.speechFile) {
/* Set the thread priority */
schedParam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 2;
if (pthread_attr_setschedparam(&attr, &schedParam)) {
ERR("Failed to set scheduler parametersn");
cleanup(EXIT_FAILURE);
}
/* Create the thread */
speechEnv.hRendezvousInit = &rendezvousInit;
speechEnv.hRendezvousCleanup = &rendezvousCleanup;
speechEnv.hPause = &pause;
speechEnv.speechFile = args.speechFile;
speechEnv.speechEncoder = args.speechEncoder;
speechEnv.soundInput = args.soundInput;
if (pthread_create(&speechThread, &attr, speechThrFxn, &speechEnv)) {
ERR("Failed to create speech threadn");
cleanup(EXIT_FAILURE);
}
initMask |= SPEECHTHREADCREATED;
}
/* Main thread becomes the control thread */
ctrlEnv.hRendezvousInit = &rendezvousInit;
ctrlEnv.hRendezvousCleanup = &rendezvousCleanup;
ctrlEnv.hPause = &pause;
ctrlEnv.keyboard = args.keyboard;
ctrlEnv.time = args.time;
ctrlEnv.videoFile = args.videoFile;
ctrlEnv.videoEncoder = args.videoEncoder;
ctrlEnv.speechFile = args.speechFile;
ctrlEnv.speechEncoder = args.speechEncoder;
ret = ctrlThrFxn(&ctrlEnv);
if (ret == THREAD_FAILURE) {
status = EXIT_FAILURE;
}
cleanup:
/* Make sure the other threads aren't waiting for init to complete */
Rendezvous_force(&rendezvousInit);
/* Make sure the other threads aren't stuck pausing */
Pause_off(&pause);
/* Wait until the other threads terminate */
if (initMask & SPEECHTHREADCREATED) {
if (pthread_join(speechThread, &ret) == 0) {
if (ret == THREAD_FAILURE) {
status = EXIT_FAILURE;
}
}
}
if (initMask & VIDEOTHREADCREATED) {
if (pthread_join(videoThread, &ret) == 0) {
if (ret == THREAD_FAILURE) {
status = EXIT_FAILURE;
}
}
}
if (initMask & WRITERTHREADCREATED) {
if (pthread_join(writerThread, &ret) == 0) {
if (ret == THREAD_FAILURE) {
status = EXIT_FAILURE;
}
}
}
if (initMask & OUTFIFOWRITEROPENED) {
FifoUtil_close(&writerEnv.outFifo);
}
if (initMask & INFIFOWRITEROPENED) {
FifoUtil_close(&writerEnv.inFifo);
}
if (initMask & CAPTURETHREADCREATED) {
if (pthread_join(captureThread, &ret) == 0) {
if (ret == THREAD_FAILURE) {
status = EXIT_FAILURE;
}
}
}
if (initMask & OUTFIFOCAPTUREOPENED) {
FifoUtil_close(&captureEnv.outFifo);
}
if (initMask & INFIFOCAPTUREOPENED) {
FifoUtil_close(&captureEnv.inFifo);
}
if (initMask & DISPLAYTHREADCREATED) {
if (pthread_join(displayThread, &ret) == 0) {
if (ret == THREAD_FAILURE) {
status = EXIT_FAILURE;
}
}
}
if (initMask & OUTFIFODISPLAYOPENED) {
FifoUtil_close(&displayEnv.outFifo);
}
if (initMask & INFIFODISPLAYOPENED) {
FifoUtil_close(&displayEnv.inFifo);
}
if (initMask & PRIMERENDEZVOUSOPENED) {
Rendezvous_close(&rendezvousPrime);
}
if (initMask & CLEANUPRENDEZVOUSOPENED) {
Rendezvous_close(&rendezvousCleanup);
}
if (initMask & INITRENDEZVOUSOPENED) {
Rendezvous_close(&rendezvousInit);
}
if (initMask & PAUSEOPENED) {
Pause_close(&pause);
}
#if 0
if (initMask & LOGSINITIALIZED) {
TraceUtil_stop();
}
#endif
/* Destroy the global mutex */
pthread_mutex_destroy(&gbl.mutex);
if (status == EXIT_SUCCESS && args.interface) {
if (execl("./interface", "interface", "-l 3", NULL) == -1) {
status = EXIT_FAILURE;
}
}
exit(status);
}
|