Quicktime - added SDI sudio input

This commit is contained in:
Martin Pulec
2011-08-30 18:22:05 +02:00
parent 4663760709
commit bb5ceea0c7
4 changed files with 276 additions and 41 deletions

View File

@@ -59,6 +59,7 @@
#define SAMPLE_RATE 48000
#define SAMPLES_PER_FRAME 2048
#define CHANNELS 2
#define SECONDS 5
struct state_portaudio_playback {
audio_frame frame;
@@ -100,7 +101,7 @@ void portaudio_reconfigure_audio(void *state, int quant_samples, int channels,
s->frame.sample_rate = sample_rate;
s->frame.state = s;
data_len = s->frame.bps * s->frame.ch_count * s->frame.sample_rate; // can hold up to 1 sec
data_len = s->frame.bps * s->frame.ch_count * s->frame.sample_rate * SECONDS; // can hold up to 1 sec
s->frame.data = (char*)malloc(data_len);
assert(s->frame.data != NULL);
@@ -354,7 +355,7 @@ int portaudio_init_audio_frame(audio_frame *buffer)
buffer->ch_count = CHANNELS;
buffer->aux = 0;
buffer->sample_rate = SAMPLE_RATE;
buffer->data_len = SAMPLES_PER_FRAME * buffer->bps * buffer->ch_count;
buffer->data_len = SAMPLES_PER_FRAME * buffer->bps * buffer->ch_count * SECONDS;
/* we allocate only one block, pointers point to appropriate parts of the block */
if((buffer->data = (char*)malloc(buffer->data_len)) == NULL)
@@ -408,7 +409,7 @@ void portaudio_decode_frame(void *dst, void *src, int data_len, int buffer_len,
(struct state_portaudio_playback *) state;
// we can receive 1 sec at most
assert(buffer_len <= s->frame.bps * s->frame.ch_count * s->frame.sample_rate);
assert(buffer_len <= s->frame.bps * s->frame.ch_count * s->frame.sample_rate * SECONDS);
s->frame.data_len = buffer_len;
memcpy(dst, src, data_len);

View File

@@ -48,12 +48,8 @@
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Revision: 1.15.2.3 $
* $Date: 2010/02/04 15:51:33 $
*
*/
#include "host.h"
#include "config.h"
#include "config_unix.h"
#include "debug.h"
@@ -63,6 +59,7 @@
#include "video_capture/quicktime.h"
#include "video_display/quicktime.h"
#include "video_codec.h"
#include "audio/audio.h"
#include <math.h>
#ifdef HAVE_MACOSX
@@ -76,20 +73,38 @@ struct qt_grabber_state {
uint32_t magic;
SeqGrabComponent grabber;
SGChannel video_channel;
SGChannel audio_channel;
Rect bounds;
GWorldPtr gworld;
ImageSequence seqID;
int sg_idle_enough;
int major;
int minor;
int audio_major;
int audio_minor;
struct video_frame frame;
struct audio_frame audio;
char *abuffer[2], *vbuffer[2];
int abuffer_len;
int grab_buf_idx;
const struct codec_info_t *c_info;
unsigned gui:1;
int frames;
struct timeval t0;
const quicktime_mode_t *qt_mode;
unsigned int grab_audio:1;
pthread_t thread_id;
pthread_mutex_t lock;
pthread_cond_t boss_cv;
pthread_cond_t worker_cv;
volatile int boss_waiting;
volatile int worker_waiting;
volatile int work_to_do;
};
void * vidcap_quicktime_thread(void *state);
/*
* Sequence grabber data procedure
* The sequence grabber calls the data function whenever any of the grabbers
@@ -126,29 +141,40 @@ qt_data_proc(SGChannel c, Ptr p, long len, long *offset, long chRefCon,
struct qt_grabber_state *s = (struct qt_grabber_state *)refCon;
struct timeval t;
UNUSED(c);
UNUSED(offset);
UNUSED(chRefCon);
UNUSED(time);
UNUSED(writeType);
if (s == NULL) {
debug_msg("corrupt state\n");
return -1;
}
memcpy(s->frame.data, p, len);
s->sg_idle_enough = 1;
if(c == s->video_channel) {
memcpy(s->vbuffer[s->grab_buf_idx], p, len);
s->sg_idle_enough = 1;
s->frames++;
gettimeofday(&t, NULL);
double seconds = tv_diff(t, s->t0);
if (seconds >= 5) {
float fps = s->frames / seconds;
fprintf(stderr, "%d frames in %g seconds = %g FPS\n", s->frames,
seconds, fps);
s->t0 = t;
s->frames = 0;
s->frames++;
gettimeofday(&t, NULL);
double seconds = tv_diff(t, s->t0);
if (seconds >= 5) {
float fps = s->frames / seconds;
fprintf(stderr, "%d frames in %g seconds = %g FPS\n", s->frames,
seconds, fps);
s->t0 = t;
s->frames = 0;
}
} else if(c == s->audio_channel) {
switch(writeType) {
case seqGrabWriteReserve:
break;
case seqGrabWriteFill:
memcpy(s->abuffer[s->grab_buf_idx] + s->abuffer_len, p, len);
s->abuffer_len += len;
break;
case seqGrabWriteAppend:
break;
}
}
return 0;
@@ -342,16 +368,25 @@ static int qt_open_grabber(struct qt_grabber_state *s, char *fmt)
return 0;
}
/* do not check for grab audio in case that we will only display usage */
if (SGNewChannel(s->grabber, SoundMediaType, &s->audio_channel) !=
noErr) {
fprintf(stderr, "Warning: Creating audio channel failed. "
"Disabling sound output.\n");
s->grab_audio = FALSE;
}
/* Print available devices */
int i;
int j;
SGDeviceInputList inputList;
SGDeviceList deviceList;
if (strcmp(fmt, "help") == 0) {
printf("\nUsage:\t-d quicktime -g <device>:<mode>:<pixel_type>[:<audio_device>:<audio_mode>]\n\n");
if (SGGetChannelDeviceList
(s->video_channel, sgDeviceListIncludeInputs,
&deviceList) == noErr) {
fprintf(stdout, "Available capture devices:\n");
fprintf(stdout, "\nAvailable capture devices:\n");
for (i = 0; i < (*deviceList)->count; i++) {
SGDeviceName *deviceEntry =
&(*deviceList)->entry[i];
@@ -390,7 +425,7 @@ static int qt_open_grabber(struct qt_grabber_state *s, char *fmt)
SGDisposeDeviceList(s->grabber, deviceList);
CodecNameSpecListPtr list;
GetCodecNameList(&list, 1);
printf("Compression types:\n");
printf("\nCompression types:\n");
for (i = 0; i < list->count; i++) {
int fcc = list->list[i].cType;
printf("\t%d) ", i);
@@ -406,6 +441,47 @@ static int qt_open_grabber(struct qt_grabber_state *s, char *fmt)
printf("\n");
}
}
if (SGGetChannelDeviceList
(s->audio_channel, sgDeviceListIncludeInputs,
&deviceList) == noErr) {
fprintf(stdout, "\nAvailable audio devices:\n");
for (i = 0; i < (*deviceList)->count; i++) {
SGDeviceName *deviceEntry =
&(*deviceList)->entry[i];
fprintf(stdout, " Device %d: ", i);
nprintf(deviceEntry->name);
if (deviceEntry->flags &
sgDeviceNameFlagDeviceUnavailable) {
fprintf(stdout,
" - ### NOT AVAILABLE ###");
}
if (i == (*deviceList)->selectedIndex) {
fprintf(stdout, " - ### ACTIVE ###");
}
fprintf(stdout, "\n");
short activeInputIndex = 0;
inputList = deviceEntry->inputs;
if (inputList && (*inputList)->count >= 1) {
SGGetChannelDeviceAndInputNames
(s->video_channel, NULL, NULL,
&activeInputIndex);
for (j = 0; j < (*inputList)->count;
j++) {
fprintf(stdout, "\t");
fprintf(stdout, "- %d. ", j);
nprintf((unsigned char*)&(*inputList)->entry
[j].name);
if ((i ==
(*deviceList)->selectedIndex)
&& (j == activeInputIndex))
fprintf(stdout,
" - ### ACTIVE ###");
fprintf(stdout, "\n");
}
}
}
SGDisposeDeviceList(s->grabber, deviceList);
}
return 0;
}
@@ -422,6 +498,21 @@ static int qt_open_grabber(struct qt_grabber_state *s, char *fmt)
debug_msg("Unable to set channel flags\n");
return 0;
}
if(s->grab_audio) {
if (SGSetChannelUsage
(s->audio_channel,
seqGrabRecord) != noErr) {
fprintf(stderr, "Unable to set audio channel usage\n");
s->grab_audio = FALSE;
}
if (SGSetChannelPlayFlags(s->audio_channel, channelPlayAllData) !=
noErr) {
fprintf(stderr, "Unable to set channel flags\n");
s->grab_audio = FALSE;
}
}
SGStartPreview(s->grabber);
@@ -457,6 +548,15 @@ static int qt_open_grabber(struct qt_grabber_state *s, char *fmt)
}
s->frame.color_spec = atoi(tmp);
s->audio_major = -1;
s->audio_minor = -1;
tmp = strtok(NULL, ":");
if(tmp) s->audio_major = atoi(tmp);
tmp = strtok(NULL, ":");
if(tmp) s->audio_minor = atoi(tmp);
if(s->audio_major == -1 && s->audio_minor == -1)
s->grab_audio = FALSE;
SGDeviceName *deviceEntry = &(*deviceList)->entry[s->major];
printf("Quicktime: Setting device: ");
nprintf(deviceEntry->name);
@@ -592,7 +692,79 @@ static int qt_open_grabber(struct qt_grabber_state *s, char *fmt)
}
s->frame.data_len = aligned_x * s->frame.height * s->c_info->bpp;
s->frame.data = malloc(s->frame.data_len);
s->vbuffer[0] = malloc(s->frame.data_len);
s->vbuffer[1] = malloc(s->frame.data_len);
s->grab_buf_idx = 0;
SGDisposeDeviceList(s->grabber, deviceList);
if(s->grab_audio) {
OSErr ret;
OSType compression;
if (SGGetChannelDeviceList
(s->audio_channel, sgDeviceListIncludeInputs,
&deviceList) != noErr) {
fprintf(stderr, "Unable to get list of quicktime audio devices\n");
s->grab_audio = FALSE;
goto AFTER_AUDIO;
}
SGDeviceName *deviceEntry = &(*deviceList)->entry[s->audio_major];
printf("Quicktime: Setting audio device: ");
nprintf(deviceEntry->name);
printf("\n");
if (SGSetChannelDevice(s->audio_channel, deviceEntry->name) !=
noErr) {
fprintf(stderr, "Setting up the selected audio device failed\n");
s->grab_audio = FALSE;
goto AFTER_AUDIO;
}
/* Select input */
inputList = deviceEntry->inputs;
printf("Quicktime: Setting audio input: ");
nprintf((unsigned char *)(&(*inputList)->entry[s->audio_minor].name));
printf("\n");
if (SGSetChannelDeviceInput(s->audio_channel, s->audio_minor) !=
noErr) {
fprintf(stderr, "Setting up input on selected audiodevice failed\n");
s->grab_audio = FALSE;
goto AFTER_AUDIO;
}
ret = SGGetSoundInputParameters(s->audio_channel, &s->audio.bps,
&s->audio.ch_count, &compression);
if(ret != noErr) {
fprintf(stderr, "Quicktime: failed to get audio properties");
s->grab_audio = FALSE;
goto AFTER_AUDIO;
}
/* if we need to specify format explicitly, we would use it here
* but take care that sowt is 16-bit etc.! */
/*ret = SGSetSoundInputParameters(s->audio_channel, s->audio.bps,
s->audio.ch_count, 'sowt');
if(ret != noErr) {
fprintf(stderr, "Quicktime: failed to set audio properties");
s->grab_audio = FALSE;
goto AFTER_AUDIO;
}*/
s->audio.bps /= 8; /* bits -> bytes */
Fixed tmp;
tmp = SGGetSoundInputRate(s->audio_channel);
/* next line solves common Fixed overflow (wtf QT?) */
s->audio.sample_rate = Fix2X(UnsignedFixedMulDiv(tmp, X2Fix(1), X2Fix(2)))* 2.0;
s->audio.aux = 0;
s->abuffer[0] = (char *) malloc(s->audio.sample_rate * s->audio.bps *
s->audio.ch_count);
s->abuffer[1] = (char *) malloc(s->audio.sample_rate * s->audio.bps *
s->audio.ch_count);
SGSetSoundRecordChunkSize(s->audio_channel, -65536/120); /* Negative argument meens
that the value is Fixed
(in secs). 1/120 sec
seems to be quite decent
value. */
}
AFTER_AUDIO:
SetPort(savedPort);
@@ -636,7 +808,7 @@ struct vidcap_type *vidcap_quicktime_probe(void)
}
/* Initialize the QuickTime grabbing system */
void *vidcap_quicktime_init(char *fmt)
void *vidcap_quicktime_init(char *fmt, unsigned int flags)
{
struct qt_grabber_state *s;
@@ -651,10 +823,28 @@ void *vidcap_quicktime_init(char *fmt)
s->sg_idle_enough = 0;
s->frame.color_spec = 0xffffffff;
if(flags & VIDCAP_FLAG_ENABLE_AUDIO) {
s->grab_audio = TRUE;
} else {
s->grab_audio = FALSE;
}
if (qt_open_grabber(s, fmt) == 0) {
free(s);
return NULL;
}
pthread_mutex_init(&s->lock, NULL);
pthread_cond_init(&s->boss_cv, NULL);
pthread_cond_init(&s->worker_cv, NULL);
s->boss_waiting = FALSE;
s->worker_waiting = FALSE;
s->work_to_do = TRUE;
s->grab_buf_idx = 0;
s->frame.data = s->vbuffer[0];
s->audio.data = s->abuffer[0];
pthread_create(&s->thread_id, NULL, vidcap_quicktime_thread, s);
}
return s;
@@ -676,6 +866,44 @@ void vidcap_quicktime_done(void *state)
}
}
void * vidcap_quicktime_thread(void *state)
{
struct qt_grabber_state *s = (struct qt_grabber_state *)state;
while(1) {
memset(s->abuffer[s->grab_buf_idx], 0, s->abuffer_len);
s->abuffer_len = 0;
/* Run the QuickTime sequence grabber idle function, which provides */
/* processor time to out data proc running as a callback. */
/* The while loop done in this way is also sort of nice bussy waiting */
/* and synchronizes capturing and sending. */
s->sg_idle_enough = 0;
while (!s->sg_idle_enough) {
if (SGIdle(s->grabber) != noErr) {
debug_msg("Error in SGIDle\n");
}
}
pthread_mutex_lock(&s->lock);
while(!s->work_to_do) {
s->worker_waiting = TRUE;
pthread_cond_wait((&s->worker_cv), &s->lock);
s->worker_waiting = FALSE;
}
s->audio.data_len = s->abuffer_len;
s->frame.data = s->vbuffer[s->grab_buf_idx];
s->audio.data = s->abuffer[s->grab_buf_idx];
s->grab_buf_idx = (s->grab_buf_idx + 1 ) % 2;
s->work_to_do = FALSE;
if(s->boss_waiting)
pthread_cond_signal(&s->boss_cv);
pthread_mutex_unlock(&s->lock);
}
return NULL;
}
/* Grab a frame */
struct video_frame *vidcap_quicktime_grab(void *state, int *count, struct audio_frame **audio)
{
@@ -683,24 +911,27 @@ struct video_frame *vidcap_quicktime_grab(void *state, int *count, struct audio_
assert(s != NULL);
assert(s->magic == MAGIC_QT_GRABBER);
*audio = NULL; /* currently no audio */
/* Run the QuickTime sequence grabber idle function, which provides */
/* processor time to out data proc running as a callback. */
/* The while loop done in this way is also sort of nice bussy waiting */
/* and synchronizes capturing and sending. */
s->sg_idle_enough = 0;
while (!s->sg_idle_enough) {
if (SGIdle(s->grabber) != noErr) {
debug_msg("Error in SGIDle\n");
*count = 0;
return NULL;
}
pthread_mutex_lock(&s->lock);
while(s->work_to_do) {
s->boss_waiting = TRUE;
pthread_cond_wait(&s->boss_cv, &s->lock);
s->boss_waiting = FALSE;
}
*count = 1;
if(s->grab_audio && s->audio.data_len > 0) {
*audio = &s->audio;
} else {
*audio = NULL;
}
s->work_to_do = TRUE;
if(s->worker_waiting)
pthread_cond_signal(&s->worker_cv);
pthread_mutex_unlock(&s->lock);
*count = 1; /* no tiled video by now */
return &s->frame;
}

View File

@@ -58,7 +58,7 @@
struct audio_frame;
struct vidcap_type *vidcap_quicktime_probe(void);
void *vidcap_quicktime_init(char *fmt);
void *vidcap_quicktime_init(char *fmt, unsigned int flags);
void vidcap_quicktime_done(void *state);
struct video_frame *vidcap_quicktime_grab(void *state, int *count, struct audio_frame **audio);

View File

@@ -925,7 +925,10 @@ static int find_mode(ComponentInstance *ci, int width, int height,
ret =
QTGetAtomDataPtr(modeListAtomContainer, atom,
&dataSize, (Ptr *) & dataPtr);
if(fabs(fps * 65536 - EndianS32_BtoN(dataPtr[0]) > 0.01)) {
/* Following computation is in Fixed data type - its real value
* is 65536 bigger than coresponding integer (Fixed cast to int)
*/
if(fabs(fps * 65536 - EndianS32_BtoN(dataPtr[0]) > 0.01 * 65536)) {
++i;
debug_msg("[quicktime] mode %dx%d@%0.2f not selected.\n",
width, height, EndianS32_BtoN(dataPtr[0])/65536.0);