// Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
// SMS
// VideoConsumer.cpp
#include "FileUploadClient.h"
#include "FtpClient.h"
#include "SftpClient.h"
#include "VideoConsumer.h"
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <Application.h>
#include <Buffer.h>
#include <BufferGroup.h>
#include <Catalog.h>
#include <Locale.h>
#include <MediaRoster.h>
#include <NodeInfo.h>
#include <scheduler.h>
#include <StringView.h>
#include <TimeSource.h>
#include <View.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "VideoConsumer.cpp"
#define M1 ((double)1000000.0)
#define JITTER 20000
#define FUNCTION printf
#define ERROR printf
#define PROGRESS printf
#define LOOP printf
static status_t SetFileType(BFile* file, int32 translator, uint32 type);
const media_raw_video_format vid_format = {29.97, 1, 0, 239,
B_VIDEO_TOP_LEFT_RIGHT, 1, 1, {B_RGB16, 320, 240, 320 * 4, 0, 0}};
VideoConsumer::VideoConsumer(const char* name, BView* view,
BStringView* statusLine,
BMediaAddOn* addon, const uint32 internalId)
: BMediaNode(name),
BMediaEventLooper(),
BBufferConsumer(B_MEDIA_RAW_VIDEO),
fStatusLine(statusLine),
fInternalID(internalId),
fAddOn(addon),
fConnectionActive(false),
fMyLatency(20000),
fWindow(NULL),
fView(view),
fOurBuffers(false),
fBuffers(NULL),
fTimeToFtp(false),
fFtpComplete(true),
fRate(1000000),
fImageFormat(0),
fTranslator(0),
fUploadClient(0),
fPassiveFtp(true)
{
FUNCTION("VideoConsumer::VideoConsumer\n");
AddNodeKind(B_PHYSICAL_OUTPUT);
SetEventLatency(0);
fWindow = fView->Window();
for (uint32 j = 0; j < 3; j++) {
fBitmap[j] = NULL;
fBufferMap[j] = 0;
}
strcpy(fFileNameText, "");
strcpy(fServerText, "");
strcpy(fLoginText, "");
strcpy(fPasswordText, "");
strcpy(fDirectoryText, "");
SetPriority(B_DISPLAY_PRIORITY);
}
VideoConsumer::~VideoConsumer()
{
FUNCTION("VideoConsumer::~VideoConsumer\n");
Quit();
if (fWindow) {
puts(B_TRANSLATE("Locking the window"));
if (fWindow->Lock()) {
puts(B_TRANSLATE("Closing the window"));
fWindow->Close();
fWindow = 0;
}
}
// clean up ftp thread
// wait up to 30 seconds if ftp is in progress
int32 count = 0;
while (!fFtpComplete && count < 30) {
snooze(1000000);
count++;
}
if (count == 30)
kill_thread(fFtpThread);
DeleteBuffers();
}
/********************************
From BMediaNode
********************************/
BMediaAddOn*
VideoConsumer::AddOn(int32* cookie) const
{
FUNCTION("VideoConsumer::AddOn\n");
// do the right thing if we're ever used with an add-on
*cookie = fInternalID;
return fAddOn;
}
// This implementation is required to get around a bug in
// the ppc compiler.
void
VideoConsumer::Start(bigtime_t performanceTime)
{
BMediaEventLooper::Start(performanceTime);
}
void
VideoConsumer::Stop(bigtime_t performanceTime, bool immediate)
{
BMediaEventLooper::Stop(performanceTime, immediate);
}
void
VideoConsumer::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
{
BMediaEventLooper::Seek(mediaTime, performanceTime);
}
void
VideoConsumer::TimeWarp(bigtime_t atRealTime, bigtime_t toPerformanceTime)
{
BMediaEventLooper::TimeWarp(atRealTime, toPerformanceTime);
}
status_t
VideoConsumer::DeleteHook(BMediaNode* node)
{
return BMediaEventLooper::DeleteHook(node);
}
void
VideoConsumer::NodeRegistered()
{
FUNCTION("VideoConsumer::NodeRegistered\n");
fIn.destination.port = ControlPort();
fIn.destination.id = 0;
fIn.source = media_source::null;
fIn.format.type = B_MEDIA_RAW_VIDEO;
fIn.format.u.raw_video = vid_format;
Run();
}
status_t
VideoConsumer::RequestCompleted(const media_request_info& info)
{
FUNCTION("VideoConsumer::RequestCompleted\n");
switch (info.what) {
case media_request_info::B_SET_OUTPUT_BUFFERS_FOR:
if (info.status != B_OK)
ERROR("VideoConsumer::RequestCompleted: Not using our buffers!\n");
break;
default:
ERROR("VideoConsumer::RequestCompleted: Invalid argument\n");
break;
}
return B_OK;
}
status_t
VideoConsumer::HandleMessage(int32 message, const void* data, size_t size)
{
//FUNCTION("VideoConsumer::HandleMessage\n");
ftp_msg_info* info = (ftp_msg_info*)data;
status_t status = B_OK;
switch (message) {
case FTP_INFO:
PROGRESS("VideoConsumer::HandleMessage - FTP_INFO message\n");
fRate = info->rate;
fImageFormat = info->imageFormat;
fTranslator = info->translator;
fPassiveFtp = info->passiveFtp;
fUploadClient = info->uploadClient;
strcpy(fFileNameText, info->fileNameText);
strcpy(fServerText, info->serverText);
strcpy(fLoginText, info->loginText);
strcpy(fPasswordText, info->passwordText);
strcpy(fDirectoryText, info->directoryText);
// remove old user events
EventQueue()->FlushEvents(TimeSource()->Now(), BTimedEventQueue::B_ALWAYS,
true, BTimedEventQueue::B_USER_EVENT);
if (fRate != B_INFINITE_TIMEOUT) {
// if rate is not "Never," push an event
// to restart captures 5 seconds from now
media_timed_event event(TimeSource()->Now() + 5000000,
BTimedEventQueue::B_USER_EVENT);
EventQueue()->AddEvent(event);
}
break;
}
return status;
}
void
VideoConsumer::BufferReceived(BBuffer* buffer)
{
LOOP("VideoConsumer::Buffer #%" B_PRId32 " received, start_time %" B_PRIdBIGTIME
"\n", buffer->ID(), buffer->Header()->start_time);
if (RunState() == B_STOPPED) {
buffer->Recycle();
return;
}
media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER,
buffer, BTimedEventQueue::B_RECYCLE_BUFFER);
EventQueue()->AddEvent(event);
}
void
VideoConsumer::ProducerDataStatus(const media_destination& forWhom, int32 status,
bigtime_t atMediaTime)
{
FUNCTION("VideoConsumer::ProducerDataStatus\n");
if (forWhom != fIn.destination)
return;
}
status_t
VideoConsumer::CreateBuffers(const media_format& withFormat)
{
FUNCTION("VideoConsumer::CreateBuffers\n");
DeleteBuffers();
// delete any old buffers
status_t status = B_OK;
// create a buffer group
uint32 xSize = withFormat.u.raw_video.display.line_width;
uint32 ySize = withFormat.u.raw_video.display.line_count;
color_space colorspace = withFormat.u.raw_video.display.format;
PROGRESS("VideoConsumer::CreateBuffers - Colorspace = %d\n", colorspace);
fBuffers = new BBufferGroup();
status = fBuffers->InitCheck();
if (status != B_OK) {
ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n");
return status;
}
// and attach the bitmaps to the buffer group
for (uint32 j = 0; j < 3; j++) {
fBitmap[j] = new BBitmap(BRect(0, 0, (xSize - 1), (ySize - 1)), colorspace,
false, true);
if (fBitmap[j]->IsValid()) {
buffer_clone_info info;
if ((info.area = area_for(fBitmap[j]->Bits())) == B_ERROR)
ERROR("VideoConsumer::CreateBuffers - ERROR IN AREA_FOR\n");
info.offset = 0;
info.size = (size_t)fBitmap[j]->BitsLength();
info.flags = j;
info.buffer = 0;
if ((status = fBuffers->AddBuffer(info)) != B_OK) {
ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n");
return status;
}
else
PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n");
} else {
ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING "
"BUFFER: %08" B_PRIx32 "\n", status);
return B_ERROR;
}
}
BBuffer* buffList[3];
for (int j = 0; j < 3; j++)
buffList[j] = NULL;
if ((status = fBuffers->GetBufferList(3, buffList)) == B_OK)
for (int j = 0; j < 3; j++)
if (buffList[j] != NULL) {
fBufferMap[j] = buffList[j];
PROGRESS(" j = %d buffer = %p\n", j, fBufferMap[j]);
} else {
ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n");
return B_ERROR;
}
else
ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n");
fFtpBitmap = new BBitmap(BRect(0, 0, xSize - 1, ySize - 1), B_RGB32, false, false);
FUNCTION("VideoConsumer::CreateBuffers - EXIT\n");
return status;
}
void
VideoConsumer::DeleteBuffers()
{
FUNCTION("VideoConsumer::DeleteBuffers\n");
if (fBuffers) {
delete fBuffers;
fBuffers = NULL;
for (uint32 j = 0; j < 3; j++)
if (fBitmap[j]->IsValid()) {
delete fBitmap[j];
fBitmap[j] = NULL;
}
}
FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
}
status_t
VideoConsumer::Connected(const media_source& producer, const media_destination& where,
const media_format& withFormat, media_input* outInput)
{
FUNCTION("VideoConsumer::Connected\n");
fIn.source = producer;
fIn.format = withFormat;
fIn.node = Node();
sprintf(fIn.name, "Video Consumer");
*outInput = fIn;
uint32 userData = 0;
int32 changeTag = 1;
if (CreateBuffers(withFormat) == B_OK)
BBufferConsumer::SetOutputBuffersFor(producer, fDestination,
fBuffers, (void *)&userData, &changeTag, true);
else {
ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n");
return B_ERROR;
}
fConnectionActive = true;
FUNCTION("VideoConsumer::Connected - EXIT\n");
return B_OK;
}
void
VideoConsumer::Disconnected(const media_source& producer, const media_destination& where)
{
FUNCTION("VideoConsumer::Disconnected\n");
if (where == fIn.destination && producer == fIn.source) {
// disconnect the connection
fIn.source = media_source::null;
delete fFtpBitmap;
fConnectionActive = false;
}
}
status_t
VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format)
{
FUNCTION("VideoConsumer::AcceptFormat\n");
if (dest != fIn.destination) {
ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n");
return B_MEDIA_BAD_DESTINATION;
}
if (format->type == B_MEDIA_NO_TYPE)
format->type = B_MEDIA_RAW_VIDEO;
if (format->type != B_MEDIA_RAW_VIDEO) {
ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n");
return B_MEDIA_BAD_FORMAT;
}
if (format->u.raw_video.display.format != B_RGB32
&& format->u.raw_video.display.format != B_RGB16
&& format->u.raw_video.display.format != B_RGB15
&& format->u.raw_video.display.format != B_GRAY8
&&
format->u.raw_video.display.format != media_raw_video_format::wildcard.display.format) {
ERROR("AcceptFormat - not a format we know about!\n");
return B_MEDIA_BAD_FORMAT;
}
if (format->u.raw_video.display.format == media_raw_video_format::wildcard.display.format) {
format->u.raw_video.display.format = B_RGB16;
}
char formatString[256];
string_for_format(*format, formatString, 256);
FUNCTION("VideoConsumer::AcceptFormat: %s\n", formatString);
return B_OK;
}
status_t
VideoConsumer::GetNextInput(int32* cookie, media_input* outInput)
{
FUNCTION("VideoConsumer::GetNextInput\n");
// custom build a destination for this connection
// put connection number in id
if (*cookie < 1) {
fIn.node = Node();
fIn.destination.id = *cookie;
sprintf(fIn.name, "Video Consumer");
*outInput = fIn;
(*cookie)++;
return B_OK;
} else {
ERROR("VideoConsumer::GetNextInput - - BAD INDEX\n");
return B_MEDIA_BAD_DESTINATION;
}
}
void
VideoConsumer::DisposeInputCookie(int32 /*cookie*/)
{
}
status_t
VideoConsumer::GetLatencyFor(const media_destination& forWhom, bigtime_t* outLatency,
media_node_id* out_timesource)
{
FUNCTION("VideoConsumer::GetLatencyFor\n");
if (forWhom != fIn.destination)
return B_MEDIA_BAD_DESTINATION;
*outLatency = fMyLatency;
*out_timesource = TimeSource()->ID();
return B_OK;
}
status_t
VideoConsumer::FormatChanged(const media_source& producer, const media_destination& consumer,
int32 fromChangeCount, const media_format& format)
{
FUNCTION("VideoConsumer::FormatChanged\n");
if (consumer != fIn.destination)
return B_MEDIA_BAD_DESTINATION;
if (producer != fIn.source)
return B_MEDIA_BAD_SOURCE;
fIn.format = format;
return CreateBuffers(format);
}
void
VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
LOOP("VideoConsumer::HandleEvent\n");
BBuffer* buffer;
switch (event->type) {
case BTimedEventQueue::B_START:
PROGRESS("VideoConsumer::HandleEvent - START\n");
break;
case BTimedEventQueue::B_STOP:
PROGRESS("VideoConsumer::HandleEvent - STOP\n");
EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS,
true, BTimedEventQueue::B_HANDLE_BUFFER);
break;
case BTimedEventQueue::B_USER_EVENT:
PROGRESS("VideoConsumer::HandleEvent - USER EVENT\n");
if (RunState() == B_STARTED) {
fTimeToFtp = true;
PROGRESS("Pushing user event for %.4f, time now %.4f\n",
(event->event_time + fRate) / M1, event->event_time/M1);
media_timed_event newEvent(event->event_time + fRate,
BTimedEventQueue::B_USER_EVENT);
EventQueue()->AddEvent(newEvent);
}
break;
case BTimedEventQueue::B_HANDLE_BUFFER:
{
LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n");
buffer = (BBuffer *)event->pointer;
if (RunState() == B_STARTED && fConnectionActive) {
// see if this is one of our buffers
uint32 index = 0;
fOurBuffers = true;
while (index < 3)
if (buffer == fBufferMap[index])
break;
else
index++;
if (index == 3) {
// no, buffers belong to consumer
fOurBuffers = false;
index = 0;
}
if (fFtpComplete && fTimeToFtp) {
PROGRESS("VidConsumer::HandleEvent - SPAWNING FTP THREAD\n");
fTimeToFtp = false;
fFtpComplete = false;
memcpy(fFtpBitmap->Bits(), buffer->Data(), fFtpBitmap->BitsLength());
fFtpThread = spawn_thread(FtpRun, "Video Window Ftp", B_NORMAL_PRIORITY, this);
resume_thread(fFtpThread);
}
if ((RunMode() == B_OFFLINE)
|| ((TimeSource()->Now() > (buffer->Header()->start_time - JITTER))
&& (TimeSource()->Now() < (buffer->Header()->start_time + JITTER)))) {
if (!fOurBuffers)
// not our buffers, so we need to copy
memcpy(fBitmap[index]->Bits(), buffer->Data(), fBitmap[index]->BitsLength());
if (fWindow->Lock()) {
uint32 flags;
if ((fBitmap[index]->ColorSpace() == B_GRAY8) &&
!bitmaps_support_space(fBitmap[index]->ColorSpace(), &flags)) {
// handle mapping of GRAY8 until app server knows how
uint32* start = (uint32*)fBitmap[index]->Bits();
int32 size = fBitmap[index]->BitsLength();
uint32* end = start + size / 4;
for (uint32* p = start; p < end; p++)
*p = (*p >> 3) & 0x1f1f1f1f;
}
fView->DrawBitmap(fBitmap[index], fView->Bounds());
fWindow->Unlock();
}
}
else
PROGRESS("VidConsumer::HandleEvent - DROPPED FRAME\n");
buffer->Recycle();
}
else
buffer->Recycle();
break;
}
default:
ERROR("VideoConsumer::HandleEvent - BAD EVENT\n");
break;
}
}
status_t
VideoConsumer::FtpRun(void* data)
{
FUNCTION("VideoConsumer::FtpRun\n");
((VideoConsumer *)data)->FtpThread();
return 0;
}
void
VideoConsumer::FtpThread()
{
char fullPath[B_PATH_NAME_LENGTH];
FUNCTION("VideoConsumer::FtpThread\n");
if (fUploadClient == 2) {
// 64 + 64 = 128 max
snprintf(fullPath, B_PATH_NAME_LENGTH, "%s/%s", fDirectoryText, fFileNameText);
LocalSave(fullPath, fFtpBitmap);
} else if (LocalSave(fFileNameText, fFtpBitmap) == B_OK)
FtpSave(fFileNameText);
#if 0
// save a small version, too
BBitmap* b = new BBitmap(BRect(0,0,159,119), B_RGB32, true, false);
BView* v = new BView(BRect(0,0,159,119), "SmallView 1", 0, B_WILL_DRAW);
b->AddChild(v);
b->Lock();
v->DrawBitmap(fFtpBitmap, v->Frame());
v->Sync();
b->Unlock();
if (LocalSave("small.jpg", b) == B_OK)
FtpSave("small.jpg");
delete b;
#endif
fFtpComplete = true;
}
void
VideoConsumer::UpdateFtpStatus(const char* status)
{
printf("FTP STATUS: %s\n",status);
if (fView->Window()->Lock()) {
fStatusLine->SetText(status);
fView->Window()->Unlock();
}
}
status_t
VideoConsumer::LocalSave(char* filename, BBitmap* bitmap)
{
BFile* output;
UpdateFtpStatus(B_TRANSLATE("Capturing Image" B_UTF8_ELLIPSIS));
/* save a local copy of the image in the requested format */
output = new BFile();
if (output->SetTo(filename, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE) == B_NO_ERROR) {
BBitmapStream input(bitmap);
status_t err = BTranslatorRoster::Default()->Translate(&input, NULL, NULL,
output, fImageFormat);
if (err == B_OK) {
err = SetFileType(output, fTranslator, fImageFormat);
if (err != B_OK)
UpdateFtpStatus(B_TRANSLATE("Error setting type of output file"));
}
else
UpdateFtpStatus(B_TRANSLATE("Error writing output file"));
input.DetachBitmap(&bitmap);
output->Unset();
delete output;
return B_OK;
} else {
UpdateFtpStatus(B_TRANSLATE("Error creating output file"));
return B_ERROR;
}
}
status_t
VideoConsumer::FtpSave(char* filename)
{
FileUploadClient *ftp;
//XXX: make that cleaner
switch (fUploadClient) {
case 0:
ftp = new FtpClient;
break;
case 1:
ftp = new SftpClient;
break;
case 2:
return B_OK;
default:
fprintf(stderr, B_TRANSLATE("invalid upload client %ld\n"),
fUploadClient);
return EINVAL;
}
ftp->SetPassive(fPassiveFtp);
// ftp the local file to our web site
UpdateFtpStatus(B_TRANSLATE("Logging in" B_UTF8_ELLIPSIS));
if (ftp->Connect((string)fServerText, (string)fLoginText,
(string)fPasswordText)) {
// connect to server
UpdateFtpStatus(B_TRANSLATE("Connected" B_UTF8_ELLIPSIS));
if (ftp->ChangeDir((string)fDirectoryText)) {
// cd to the desired directory
UpdateFtpStatus(B_TRANSLATE("Upload" B_UTF8_ELLIPSIS));
if (ftp->PutFile((string)filename, (string)"temp")) {
// send the file to the server
ftp->Chmod((string)"temp", (string)"644");
// make it world readable
UpdateFtpStatus(B_TRANSLATE("Renaming" B_UTF8_ELLIPSIS));
if (ftp->MoveFile((string)"temp", (string)filename)) {
// change to the desired name
uint32 time = real_time_clock();
char s[80];
strcpy(s, B_TRANSLATE("Last Capture: "));
strcat(s, ctime((const time_t*)&time));
s[strlen(s) - 1] = 0;
UpdateFtpStatus(s);
delete ftp;
return B_OK;
} else
UpdateFtpStatus(B_TRANSLATE("Rename failed"));
} else
UpdateFtpStatus(B_TRANSLATE("File upload failed"));
} else
UpdateFtpStatus(B_TRANSLATE("Couldn't find requested directory on "
"server"));
}
else
UpdateFtpStatus(B_TRANSLATE("Server login failed"));
delete ftp;
return B_ERROR;
}
status_t
SetFileType(BFile* file, int32 translator, uint32 type)
{
translation_format* formats;
int32 count;
status_t err = BTranslatorRoster::Default()->GetOutputFormats(translator,
(const translation_format **)&formats, &count);
if (err < B_OK)
return err;
const char* mime = NULL;
for (int ix = 0; ix < count; ix++) {
if (formats[ix].type == type) {
mime = formats[ix].MIME;
break;
}
}
if (mime == NULL) {
/* this should not happen, but being defensive might be prudent */
return B_ERROR;
}
/* use BNodeInfo to set the file type */
BNodeInfo ninfo(file);
return ninfo.SetType(mime);
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fFtpThread, fFtpBitmap.