static int decode_and_send_const(DNxHDDecodeStreamConnect* connect, const unsigned char* buffer, unsigned int bufferSize) { int finished; /* We know that avcodec_decode_video will not modify the input data, so we can cast buffer to non-const */ avcodec_decode_video(connect->decoder->dec, connect->decoder->decFrame, &finished, (unsigned char*)buffer, bufferSize); if (!finished) { ml_log_error("Failed to decode DNxHD video\n"); return 0; } /* reformat decoded frame */ yuv422_to_yuv422(connect->streamInfo.width, connect->streamInfo.height, 0, connect->decoder->decFrame, connect->sinkBuffer); /* send decoded frame to sink */ if (!msk_receive_stream_frame(connect->sink, connect->sinkStreamId, connect->sinkBuffer, connect->sinkBufferSize)) { ml_log_error("failed to write frame to media sink\n"); return 0; } return 1; }
ReadWriteLockGuard::ReadWriteLockGuard(pthread_rwlock_t* rwlock, bool writeLock) : _rwlock(NULL) { if (writeLock) { if (pthread_rwlock_wrlock(rwlock) != 0) { ml_log_error("pthread_rwlock_wrlock failed\n"); } else { _rwlock = rwlock; } } else { if (pthread_rwlock_rdlock(rwlock) != 0) { ml_log_error("pthread_rwlock_rdlock failed\n"); } else { _rwlock = rwlock; } } }
static int ddc_allocate_buffer(void* data, int streamId, unsigned char** buffer, unsigned int bufferSize) { DVDecodeStreamConnect* connect = (DVDecodeStreamConnect*)data; int result; if (connect->sourceStreamId != streamId) { ml_log_error("Buffer allocation request for unknown source stream %d in copy connect\n", streamId); return 0; } if (connect->dvDataSize != bufferSize) { ml_log_error("Invalid DV buffer allocation request for stream %d in copy connect\n", streamId); return 0; } /* ask sink to allocate buffer for decoded frame */ result = msk_get_stream_buffer(connect->sink, connect->sinkStreamId, connect->sinkBufferSize, &connect->sinkBuffer); if (!result) { ml_log_error("Sink failed to allocate buffer for stream %d for DV decoder connector\n", streamId); return 0; } *buffer = connect->dvData; return 1; }
static int create_dnxhd_decoder(StreamFormat format, int width, int height, int numFFMPEGThreads, DNxHDDecoder** decoder) { DNxHDDecoder* newDecoder = NULL; AVCodec* avDecoder = NULL; CALLOC_ORET(newDecoder, DNxHDDecoder, 1); newDecoder->format = format; newDecoder->width = width; newDecoder->height = height; avDecoder = avcodec_find_decoder(CODEC_ID_DNXHD); if (!avDecoder) { ml_log_error("Could not find the DNxHD decoder\n"); goto fail; } newDecoder->dec = avcodec_alloc_context(); if (!newDecoder->dec) { ml_log_error("Could not allocate DNxHD decoder context\n"); goto fail; } if (numFFMPEGThreads > 1) { avcodec_thread_init(newDecoder->dec, numFFMPEGThreads); newDecoder->isThreaded = 1; } avcodec_set_dimensions(newDecoder->dec, width, height); newDecoder->dec->pix_fmt = PIX_FMT_YUV422P; if (avcodec_open(newDecoder->dec, avDecoder) < 0) { ml_log_error("Could not open decoder\n"); goto fail; } newDecoder->openedDecoder = 1; newDecoder->decFrame = avcodec_alloc_frame(); if (!newDecoder->decFrame) { ml_log_error("Could not allocate decoded frame\n"); goto fail; } *decoder = newDecoder; return 1; fail: free_dnxhd_decoder(&newDecoder); return 0; }
static int ddc_receive_frame(void* data, int streamId, unsigned char* buffer, unsigned int bufferSize) { DVDecodeStreamConnect* connect = (DVDecodeStreamConnect*)data; int status; int result = 1; if (connect->sourceStreamId != streamId) { ml_log_error("Received frame for unknown source stream %d in copy connect\n", streamId); return 0; } /* signal to ddc_sync at later time that we have received a frame to decode and send */ connect->frameWasReceived = 1; if (!connect->useWorkerThread) { result = decode_and_send(connect); } else { /* check that the worker isn't busy */ PTHREAD_MUTEX_LOCK(&connect->workerMutex); if (connect->workerIsBusy) { ml_log_error("DV connect worker thread is still busy, and therefore cannot receive a new frame\n"); result = 0; } PTHREAD_MUTEX_UNLOCK(&connect->workerMutex); if (result != 1) { return result; } /* signal worker that a new frame is ready */ PTHREAD_MUTEX_LOCK(&connect->workerMutex); connect->frameIsReady = 1; status = pthread_cond_signal(&connect->frameIsReadyCond); if (status != 0) { ml_log_error("DV connect worker thread failed to send frame is ready condition signal\n"); result = 0; } PTHREAD_MUTEX_UNLOCK(&connect->workerMutex); } return result; }
int vsd_dump(const char* filename, FILE* output) { VideoSwitchDatabase* db; unsigned char buf[VIDEO_SWITCH_DB_ENTRY_SIZE * 50]; int numEntries = 0; int startEntry = 0; if (!vsd_open_read(filename, &db)) { return 0; } do { if (!vsd_load(db, startEntry, buf, VIDEO_SWITCH_DB_ENTRY_SIZE * 50, &numEntries)) { ml_log_error("Failed to load entries from video switch database\n"); goto fail; } int i; for (i = 0; i < numEntries; i++) { VideoSwitchDbEntry entry; if (!vsd_parse_entry(&buf[VIDEO_SWITCH_DB_ENTRY_SIZE * i], VIDEO_SWITCH_DB_ENTRY_SIZE * (50 - i), &entry)) { ml_log_error("Failed to parse video switch database entry %d\n", i); goto fail; } printf("%d, %s, %04d-%02d-%02d, %02d:%02d:%02d:%02d%c\n", entry.sourceIndex, entry.sourceId, entry.year, entry.month, entry.day, entry.tc.hour, entry.tc.min, entry.tc.sec, entry.tc.frame, entry.tc.isDropFrame ? 'D' : ' '); } startEntry += numEntries; } while (numEntries > 0); vsd_close(&db); return 1; fail: vsd_close(&db); return 0; }
static int ddc_allocate_buffer(void* data, int streamId, unsigned char** buffer, unsigned int bufferSize) { DNxHDDecodeStreamConnect* connect = (DNxHDDecodeStreamConnect*)data; int result; if (connect->sourceStreamId != streamId) { ml_log_error("Buffer allocation request for unknown source stream %d in copy connect\n", streamId); return 0; } if (connect->dnxhdDataSize < bufferSize) { /* allocate buffer if neccessary and set size */ if (connect->dnxhdDataAllocSize < bufferSize) { SAFE_FREE(&connect->dnxhdData); connect->dnxhdDataSize = 0; connect->dnxhdDataAllocSize = 0; CALLOC_ORET(connect->dnxhdData, unsigned char, bufferSize + FF_INPUT_BUFFER_PADDING_SIZE /* FFMPEG for some reason needs the extra space */); connect->dnxhdDataAllocSize = bufferSize; /* we lie and don't include the FFMPEG extra space */ } connect->dnxhdDataSize = bufferSize; }
int vsd_load(VideoSwitchDatabase* db, int startEntry, unsigned char* buffer, int bufferSize, int* numEntries) { int totalEntries = vsd_get_num_entries(db); if (totalEntries == 0) { *numEntries = 0; return 1; } int readEntries = totalEntries - startEntry; readEntries = (bufferSize / VIDEO_SWITCH_DB_ENTRY_SIZE < readEntries) ? bufferSize / VIDEO_SWITCH_DB_ENTRY_SIZE : readEntries; if (readEntries <= 0) { *numEntries = 0; return 1; } if (fseeko(db->file, startEntry * VIDEO_SWITCH_DB_ENTRY_SIZE, SEEK_SET) != 0) { ml_log_error("Failed to seek to position in video switch database file: %s\n", strerror(errno)); return 0; } *numEntries = fread(buffer, VIDEO_SWITCH_DB_ENTRY_SIZE, readEntries, db->file); return 1; }
int msc_add_stream_to_map(MediaSourceStreamMap* map, int streamId, int sourceId) { int i; for (i = 0; i < map->numStreams; i++) { if (map->streams[i].streamId == streamId) { map->streams[i].sourceId = sourceId; return 1; } } if (map->numStreams + 1 < (int)(sizeof(map->streams) / sizeof(int))) { map->streams[map->numStreams].sourceId = sourceId; map->streams[map->numStreams].streamId = streamId; map->numStreams++; } else { ml_log_error("Number streams exceeds maximum (%d) expected\n", sizeof(map->streams) / sizeof(int)); return 0; } return 1; }
ReadWriteLockGuard::~ReadWriteLockGuard() { if (_rwlock != NULL) { if (pthread_rwlock_unlock(_rwlock) != 0) { ml_log_error("pthread_rwlock_unlock failed\n"); } } }
static int cyc_receive_frame_const(void* data, int streamId, const unsigned char* buffer, unsigned int bufferSize) { CopyStreamConnect* connect = (CopyStreamConnect*)data; if (connect->sourceStreamId != streamId) { ml_log_error("Received frame for unknown source stream %d in copy connect\n", streamId); return 0; } if (!msk_receive_stream_frame_const(connect->sink, connect->sinkStreamId, buffer, bufferSize)) { ml_log_error("failed to write frame to media sink\n"); return 0; } return 1; }
int init_cond_var(pthread_cond_t* cond) { int err; if ((err = pthread_cond_init(cond, NULL)) != 0) { ml_log_error("Failed to initialise conditional variable: %s\n", strerror(err)); return 0; } return 1; }
int init_mutex(pthread_mutex_t* mutex) { int err; if ((err = pthread_mutex_init(mutex, NULL)) != 0) { ml_log_error("Failed to initialise mutex: %s\n", strerror(err)); return 0; } return 1; }
int vsd_open_write(const char* filename, VideoSwitchDatabase** db) { FILE* file = fopen(filename, "wb"); if (file == NULL) { ml_log_error("Failed to open video switch database file '%s': %s\n", filename, strerror(errno)); return 0; } return open(file, db); }
int vsd_append_entry_with_date(VideoSwitchDatabase* db, int sourceIndex, const char* sourceId, int year, int month, int day, const Timecode* tc) { if (fseeko(db->file, 0, SEEK_END) != 0) { ml_log_error("Failed to seek to end of video switch database file: %s\n", strerror(errno)); return 0; } char buf[VIDEO_SWITCH_DB_ENTRY_SIZE]; memset(buf, 0, VIDEO_SWITCH_DB_ENTRY_SIZE); /* source index - 1 byte */ buf[SOURCE_INDEX_OFFSET] = (unsigned char)(sourceIndex); /* source id - string plus null terminator */ strncpy(&buf[SOURCE_ID_OFFSET], sourceId, SOURCE_ID_LEN - 1); /* date */ snprintf(&buf[DATE_OFFSET], DATE_LEN + 1, "%04d-%02d-%02d", (year > 9999) ? 0 : year, (month > 12) ? 0 : month, (day > 31) ? 0 : day); /* timecode - text timecode plus drop frame indicator */ snprintf(&buf[TIMECODE_OFFSET], TIMECODE_LEN + 1, "%02d:%02d:%02d:%02d%c", (tc->hour > 24) ? 0 : tc->hour, (tc->min > 59) ? 0 : tc->min, (tc->sec > 59) ? 0 : tc->sec, (tc->frame > 30) ? 0 : tc->frame, tc->isDropFrame ? 'D' : 'N'); if (fwrite(buf, VIDEO_SWITCH_DB_ENTRY_SIZE, 1, db->file) != 1) { ml_log_error("Failed to write video switch database entry: %s\n", strerror(errno)); return 0; } fflush(db->file); return 1; }
int vsd_get_num_entries(VideoSwitchDatabase* db) { struct stat st; if (fstat(fileno(db->file), &st) != 0) { ml_log_error("Failed to stat video switch database file: %s\n", strerror(errno)); return 0; } return st.st_size / VIDEO_SWITCH_DB_ENTRY_SIZE; }
static int cyc_allocate_buffer(void* data, int streamId, unsigned char** buffer, unsigned int bufferSize) { CopyStreamConnect* connect = (CopyStreamConnect*)data; if (connect->sourceStreamId != streamId) { ml_log_error("Buffer allocation request for unknown source stream %d in copy connect\n", streamId); return 0; } return msk_get_stream_buffer(connect->sink, connect->sinkStreamId, bufferSize, buffer); }
static int bmsrc_accept_frame(void* data, int streamId, const FrameInfo* frameInfo) { BufferedMediaSource* bufSource = (BufferedMediaSource*)data; BufferedStream* stream = get_stream(bufSource, streamId); if (stream == NULL) { ml_log_error("Unknown stream %d\n", streamId); return 0; } return !stream->isDisabled; }
static int ddc_sync(void* data) { DVDecodeStreamConnect* connect = (DVDecodeStreamConnect*)data; int status; int workerResult = 0; int doneWaiting; if (!connect->frameWasReceived) { /* no work was required */ return 1; } /* reset for next time */ connect->frameWasReceived = 0; if (!connect->useWorkerThread) { /* work is already complete */ return 1; } /* TODO: timed wait to prevent eternal loop? */ /* wait until worker has processed the frame and is no longer busy */ doneWaiting = 0; while (!doneWaiting && !connect->stopped) { /* wait for worker */ PTHREAD_MUTEX_LOCK(&connect->workerMutex); if (connect->workerIsBusy) { status = pthread_cond_wait(&connect->workerIsBusyCond, &connect->workerMutex); if (status != 0) { ml_log_error("DV connect worker thread failed to wait for condition\n"); /* TODO: don't try again? */ } } /* worker is not busy and no frame is waiting to be processed */ if (!connect->workerIsBusy && !connect->frameIsReady) { workerResult = connect->workerResult; doneWaiting = 1; } PTHREAD_MUTEX_UNLOCK(&connect->workerMutex); } return workerResult; }
static int init_frame(X11DisplayFrame* frame) { X11DisplaySink* sink = (X11DisplaySink*)frame->sink; XLockDisplay(sink->x11Common.windowInfo.display); sink->depth = DefaultDepth(sink->x11Common.windowInfo.display, DefaultScreen(sink->x11Common.windowInfo.display)); sink->visual = DefaultVisual(sink->x11Common.windowInfo.display, DefaultScreen(sink->x11Common.windowInfo.display)); XUnlockDisplay(sink->x11Common.windowInfo.display); if (sink->depth < 15) { ml_log_error("Unknown display depth %d\n", sink->depth); return 0; } frame->videoFormat = sink->videoFormat; if (sink->videoFormat == UYVY_10BIT_FORMAT) { frame->inputBufferSize = (sink->inputWidth + 5) / 6 * 16 * sink->inputHeight; frame->convertBufferSize = sink->inputWidth * sink->inputHeight * 2; if (sink->swScale != 1) { frame->scaleBufferSize = sink->width * sink->height * 2; } } else if (frame->videoFormat == UYVY_FORMAT || frame->videoFormat == YUV422_FORMAT) { frame->inputBufferSize = sink->inputWidth * sink->inputHeight * 2; if (sink->swScale != 1) { frame->scaleBufferSize = sink->width * sink->height * 2; } } else /* YUV420_FORMAT */ { frame->inputBufferSize = sink->inputWidth * sink->inputHeight * 3 / 2; if (sink->swScale != 1) { frame->scaleBufferSize = sink->width * sink->height * 3 / 2; } } frame->rgbBufferSize = sink->width * sink->height * 4; /* max 32-bit */ MALLOC_ORET(frame->inputBuffer, unsigned char, frame->inputBufferSize); if (frame->convertBufferSize > 0) { MALLOC_ORET(frame->convertBuffer, unsigned char, frame->convertBufferSize); }
int vsd_parse_entry(const unsigned char* entryBuffer, int entryBufferSize, VideoSwitchDbEntry* entry) { int hour, min, sec, frame; char isDropFrame; int year, month, day; if (entryBufferSize < VIDEO_SWITCH_DB_ENTRY_SIZE) { ml_log_error("Entry buffer size %d is smaller than the entry size %d\n", entryBufferSize, VIDEO_SWITCH_DB_ENTRY_SIZE); return 0; } if (sscanf((const char*)&entryBuffer[DATE_OFFSET], "%04d-%02d-%02d", &year, &month, &day) != 3) { ml_log_error("Failed to parse date in video switch database entry\n"); return 0; } if (sscanf((const char*)&entryBuffer[TIMECODE_OFFSET], "%02d:%02d:%02d:%02d%c", &hour, &min, &sec, &frame, &isDropFrame) != 5) { ml_log_error("Failed to parse timecode in video switch database entry\n"); return 0; } entry->sourceIndex = (int)entryBuffer[SOURCE_INDEX_OFFSET]; entry->sourceId = (const char*)(&entryBuffer[SOURCE_ID_OFFSET]); entry->year = year; entry->month = month; entry->day = day; entry->tc.hour = hour; entry->tc.min = min; entry->tc.sec = sec; entry->tc.frame = frame; entry->tc.isDropFrame = (isDropFrame == 'D'); return 1; }
int bks_create(const StreamInfo* videoStreamInfo, int64_t length, MediaSource** source) { BlankSource* newSource = NULL; if (videoStreamInfo->type != PICTURE_STREAM_TYPE) { ml_log_error("Blank source only does video\n"); return 0; } CALLOC_ORET(newSource, BlankSource, 1); newSource->length = length; newSource->mediaSource.data = newSource; newSource->mediaSource.finalise_blank_source = bks_finalise_blank_source; newSource->mediaSource.get_num_streams = bks_get_num_streams; newSource->mediaSource.get_stream_info = bks_get_stream_info; newSource->mediaSource.set_frame_rate_or_disable = bks_set_frame_rate_or_disable; newSource->mediaSource.disable_stream = bks_disable_stream; newSource->mediaSource.disable_video = bks_disable_video; newSource->mediaSource.stream_is_disabled = bks_stream_is_disabled; newSource->mediaSource.read_frame = bks_read_frame; newSource->mediaSource.is_seekable = bks_is_seekable; newSource->mediaSource.seek = bks_seek; newSource->mediaSource.get_length = bks_get_length; newSource->mediaSource.get_position = bks_get_position; newSource->mediaSource.get_available_length = bks_get_available_length; newSource->mediaSource.eof = bks_eof; newSource->mediaSource.set_source_name = bks_set_source_name; newSource->mediaSource.set_clip_id = bks_set_clip_id; newSource->mediaSource.close = bks_close; newSource->streamInfo = *videoStreamInfo; newSource->streamInfo.sourceId = msc_create_id(); CHK_OFAIL(add_known_source_info(&newSource->streamInfo, SRC_INFO_TITLE, "Blank Source")); CHK_OFAIL(add_known_source_info(&newSource->streamInfo, SRC_INFO_ORIGINAL_STREAM_FORMAT, get_stream_format_string(newSource->streamInfo.format))); *source = &newSource->mediaSource; return 1; fail: bks_close(newSource); return 0; }
static int bmsrc_allocate_buffer(void* data, int streamId, unsigned char** buffer, unsigned int bufferSize) { BufferedMediaSource* bufSource = (BufferedMediaSource*)data; BufferedStream* stream = get_stream(bufSource, streamId); if (stream == NULL) { ml_log_error("Unknown stream %d\n", streamId); return 0; } if (bufferSize > stream->bufferSize) { MALLOC_ORET(stream->buffer, unsigned char, bufferSize); stream->bufferSize = bufferSize; }
static int append_source(MediaSourceList* sourceList, MediaSource** source) { MediaSourceElement* ele = sourceList; MediaSourceElement* newEle = NULL; int i; /* handle first appended source */ if (ele->source == NULL) { ele->source = *source; ele->numStreams = msc_get_num_streams(*source); *source = NULL; return 1; } /* move to end */ while (ele->next != NULL) { ele = ele->next; } /* create element */ if ((newEle = (MediaSourceElement*)malloc(sizeof(MediaSourceElement))) == NULL) { ml_log_error("Failed to allocate memory\n"); return 0; } memset(newEle, 0, sizeof(MediaSourceElement)); /* append source and take ownership */ ele->next = newEle; newEle->source = *source; /* get num streams and disabled count */ newEle->numStreams = msc_get_num_streams(*source); newEle->disabledStreamCount = 0; for (i = 0; i < newEle->numStreams; i++) { if (msc_stream_is_disabled(newEle->source, i)) { newEle->disabledStreamCount++; } } *source = NULL; return 1; }
int create_joinable_thread(pthread_t* thread, void* (*start_func)(void*), void* arg) { int result; pthread_attr_t attr; pthread_t newThread; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); result = pthread_create(&newThread, &attr, start_func, arg); if (result == 0) { *thread = newThread; } else { ml_log_error("Failed to create joinable thread: %s\n", strerror(result)); } pthread_attr_destroy(&attr); return result == 0; }
int create_dv_connect(MediaSink* sink, int sinkStreamId, int sourceStreamId, const StreamInfo* streamInfo, int numFFMPEGThreads, int useWorkerThread, StreamConnect** connect) { DVDecodeStreamConnect* newConnect; StreamInfo decodedStreamInfo; int result; /* register stream with sink */ if (streamInfo->format == DV25_YUV420_FORMAT) { decodedStreamInfo = *streamInfo; decodedStreamInfo.format = YUV420_FORMAT; result = msk_accept_stream(sink, &decodedStreamInfo); } else if (streamInfo->format == DV25_YUV411_FORMAT) { decodedStreamInfo = *streamInfo; decodedStreamInfo.format = YUV411_FORMAT; result = msk_accept_stream(sink, &decodedStreamInfo); } else /* streamInfo->format == DV50_FORMAT || streamInfo->format == DV100_FORMAT */ { decodedStreamInfo = *streamInfo; decodedStreamInfo.format = YUV422_FORMAT; result = msk_accept_stream(sink, &decodedStreamInfo); } /* try UYVY if default format is not accepted */ if (!result) { decodedStreamInfo = *streamInfo; decodedStreamInfo.format = UYVY_FORMAT; result = msk_accept_stream(sink, &decodedStreamInfo); } if (!result) { /* shouldn't be here because a call to dv_connect_accept() should've returned false already */ ml_log_error("Failed to create DV connector because format is not accepted\n"); return 0; } if (!msk_register_stream(sink, sinkStreamId, &decodedStreamInfo)) { /* could have failed if max streams exceeded for example */ return 0; } CALLOC_ORET(newConnect, DVDecodeStreamConnect, 1); newConnect->useWorkerThread = useWorkerThread; newConnect->decodedFormat = decodedStreamInfo.format; if (streamInfo->format == DV25_YUV420_FORMAT || streamInfo->format == DV25_YUV411_FORMAT) { newConnect->dvDataSize = (stream_is_pal_frame_rate(streamInfo) ? 144000 : 120000); } else if (streamInfo->format == DV50_FORMAT) { newConnect->dvDataSize = (stream_is_pal_frame_rate(streamInfo) ? 288000 : 240000); } else /* streamInfo->format == DV100_FORMAT */ { newConnect->dvDataSize = 576000; } if ((newConnect->dvData = (unsigned char*)calloc( newConnect->dvDataSize + FF_INPUT_BUFFER_PADDING_SIZE /* FFMPEG for some reason needs the extra space */, sizeof(unsigned char))) == NULL) { ml_log_error("Failed to allocate memory\n"); goto fail; } newConnect->sink = sink; newConnect->sourceStreamId = sourceStreamId; newConnect->sinkStreamId = sinkStreamId; newConnect->streamInfo = *streamInfo; if (decodedStreamInfo.format == UYVY_FORMAT) { newConnect->sinkBufferSize = streamInfo->width * streamInfo->height * 2; } else if (decodedStreamInfo.format == YUV422_FORMAT) { newConnect->sinkBufferSize = streamInfo->width * streamInfo->height * 2; } else /* YUV420 / YUV411 */ { newConnect->sinkBufferSize = streamInfo->width * streamInfo->height * 3 / 2; } newConnect->streamConnect.data = newConnect; newConnect->streamConnect.get_source_listener = ddc_get_source_listener; newConnect->streamConnect.sync = ddc_sync; newConnect->streamConnect.close = ddc_close; newConnect->sourceListener.data = newConnect; newConnect->sourceListener.accept_frame = ddc_accept_frame; newConnect->sourceListener.allocate_buffer = ddc_allocate_buffer; newConnect->sourceListener.deallocate_buffer = ddc_deallocate_buffer; newConnect->sourceListener.receive_frame = ddc_receive_frame; newConnect->sourceListener.receive_frame_const = ddc_receive_frame_const; /* create DV decoder */ CHK_OFAIL(init_dv_decoder_resources()); CHK_OFAIL(create_dv_decoder(streamInfo->format, streamInfo->width, streamInfo->height, numFFMPEGThreads, &newConnect->decoder)); /* create worker thread */ if (useWorkerThread) { CHK_OFAIL(init_mutex(&newConnect->workerMutex)); CHK_OFAIL(init_cond_var(&newConnect->frameIsReadyCond)); CHK_OFAIL(init_cond_var(&newConnect->workerIsBusyCond)); CHK_OFAIL(create_joinable_thread(&newConnect->workerThreadId, worker_thread, newConnect)); } *connect = &newConnect->streamConnect; return 1; fail: ddc_close(newConnect); return 0; }
static int ddc_receive_frame_const(void* data, int streamId, const unsigned char* buffer, unsigned int bufferSize) { DVDecodeStreamConnect* connect = (DVDecodeStreamConnect*)data; int status; int result; unsigned char* nonconstBuffer; if (connect->useWorkerThread) { /* the worker thread requires the data to be copied into connect->dvData */ result = ddc_allocate_buffer(data, streamId, &nonconstBuffer, bufferSize); if (result) { memcpy(nonconstBuffer, buffer, bufferSize); result = ddc_receive_frame(data, streamId, nonconstBuffer, bufferSize); } return result; } if (connect->sourceStreamId != streamId) { ml_log_error("Received frame for unknown source stream %d in copy connect\n", streamId); return 0; } /* ask sink to allocate buffer for decoded frame */ result = msk_get_stream_buffer(connect->sink, connect->sinkStreamId, connect->sinkBufferSize, &connect->sinkBuffer); if (!result) { ml_log_error("Sink failed to allocate buffer for stream %d for DV decoder connector\n", streamId); return 0; } /* signal to ddc_sync at later time that we have received a frame to decode and send */ connect->frameWasReceived = 1; if (!connect->useWorkerThread) { result = decode_and_send_const(connect, buffer, bufferSize); } else { /* check that the worker isn't busy */ PTHREAD_MUTEX_LOCK(&connect->workerMutex); if (connect->workerIsBusy) { ml_log_error("DV connect worker thread is still busy, and therefore cannot receive a new frame\n"); result = 0; } PTHREAD_MUTEX_UNLOCK(&connect->workerMutex); if (result != 1) { return result; } /* signal worker that a new frame is ready */ PTHREAD_MUTEX_LOCK(&connect->workerMutex); connect->frameIsReady = 1; status = pthread_cond_signal(&connect->frameIsReadyCond); if (status != 0) { ml_log_error("DV connect worker thread failed to send frame is ready condition signal\n"); result = 0; } PTHREAD_MUTEX_UNLOCK(&connect->workerMutex); } return result; }
static int decode_and_send_const(DVDecodeStreamConnect* connect, const unsigned char* buffer, unsigned int bufferSize) { int finished; /* We know that avcodec_decode_video will not modify the input data, so we can cast buffer to non-const */ avcodec_decode_video(connect->decoder->dec, connect->decoder->decFrame, &finished, (unsigned char*)buffer, bufferSize); if (!finished) { ml_log_error("error decoding DV video\n"); return 0; } /* reformat decoded frame */ if (connect->streamInfo.format == DV25_YUV420_FORMAT) { if (connect->decodedFormat == UYVY_FORMAT) { yuv4xx_to_uyvy(connect->streamInfo.width, connect->streamInfo.height, stream_is_pal_frame_rate(&connect->streamInfo), connect->decoder->decFrame, connect->sinkBuffer); } else /* YUV420 */ { yuv4xx_to_yuv4xx(connect->streamInfo.width, connect->streamInfo.height, stream_is_pal_frame_rate(&connect->streamInfo), connect->decoder->decFrame, connect->sinkBuffer); } } else if (connect->streamInfo.format == DV25_YUV411_FORMAT) { if (connect->decodedFormat == UYVY_FORMAT) { yuv4xx_to_uyvy(connect->streamInfo.width, connect->streamInfo.height, stream_is_pal_frame_rate(&connect->streamInfo), connect->decoder->decFrame, connect->sinkBuffer); } else /* YUV411 */ { yuv4xx_to_yuv4xx(connect->streamInfo.width, connect->streamInfo.height, stream_is_pal_frame_rate(&connect->streamInfo), connect->decoder->decFrame, connect->sinkBuffer); } } else /* DV50_FORMAT or DV100_FORMAT */ { if (connect->decodedFormat == UYVY_FORMAT) { yuv422_to_uyvy(connect->streamInfo.width, connect->streamInfo.height, stream_is_pal_frame_rate(&connect->streamInfo), connect->decoder->decFrame, connect->sinkBuffer); } else /* YUV422 */ { yuv422_to_yuv422(connect->streamInfo.width, connect->streamInfo.height, stream_is_pal_frame_rate(&connect->streamInfo), connect->decoder->decFrame, connect->sinkBuffer); } } /* send decoded frame to sink */ if (!msk_receive_stream_frame(connect->sink, connect->sinkStreamId, connect->sinkBuffer, connect->sinkBufferSize)) { ml_log_error("failed to write frame to media sink\n"); return 0; } return 1; }
static int create_dv_decoder(StreamFormat format, int width, int height, int numFFMPEGThreads, DVDecoder** decoder) { int decoderResourceRefCount = g_decoderResourceRefCount; int i; DVDecoder* newDecoder = NULL; int numDecoders = g_decoderResource.numDecoders; AVCodec* avDecoder = NULL; /* see if there is matching decoder not in use */ if (decoderResourceRefCount > 0) { for (i = 0; i < numDecoders; i++) { if (!g_decoderResource.decoder[i]->inUse && g_decoderResource.decoder[i]->format == format && g_decoderResource.decoder[i]->width == width && g_decoderResource.decoder[i]->height == height) { /* found one not in use */ *decoder = g_decoderResource.decoder[i]; g_decoderResource.decoder[i]->inUse = 1; return 1; } } } /* create a new one */ CALLOC_ORET(newDecoder, DVDecoder, 1); newDecoder->inUse = 1; newDecoder->format = format; newDecoder->width = width; newDecoder->height = height; avDecoder = avcodec_find_decoder(CODEC_ID_DVVIDEO); if (!avDecoder) { ml_log_error("Could not find the DV decoder\n"); goto fail; } newDecoder->dec = avcodec_alloc_context(); if (!newDecoder->dec) { ml_log_error("Could not allocate DV decoder context\n"); goto fail; } if (numFFMPEGThreads > 1) { avcodec_thread_init(newDecoder->dec, numFFMPEGThreads); newDecoder->isThreaded = 1; } avcodec_set_dimensions(newDecoder->dec, width, height); if (format == DV25_YUV420_FORMAT) { newDecoder->dec->pix_fmt = PIX_FMT_YUV420P; } else if (format == DV25_YUV411_FORMAT) { newDecoder->dec->pix_fmt = PIX_FMT_YUV411P; } else { newDecoder->dec->pix_fmt = PIX_FMT_YUV422P; } if (avcodec_open(newDecoder->dec, avDecoder) < 0) { ml_log_error("Could not open decoder\n"); goto fail; } newDecoder->openedDecoder = 1; newDecoder->decFrame = avcodec_alloc_frame(); if (!newDecoder->decFrame) { ml_log_error("Could not allocate decoded frame\n"); goto fail; } /* add to static resources if they have been initialised */ if (decoderResourceRefCount > 0) { if ((size_t)g_decoderResource.numDecoders >= sizeof(g_decoderResource.decoder) / sizeof(DVDecoder*)) { /* more than x decoders? what are you doing? */ ml_log_error("Number of DV decoders exceeded hard coded limit %d\n", sizeof(g_decoderResource.decoder) / sizeof(DVDecoder)); goto fail; } PTHREAD_MUTEX_LOCK(&g_decoderResource.resourceMutex); g_decoderResource.decoder[g_decoderResource.numDecoders] = newDecoder; g_decoderResource.numDecoders++; PTHREAD_MUTEX_UNLOCK(&g_decoderResource.resourceMutex); } *decoder = newDecoder; return 1; fail: free_dv_decoder(&newDecoder); return 0; }
static void* worker_thread(void* arg) { DVDecodeStreamConnect* connect = (DVDecodeStreamConnect*)arg; int status; int workerResult; int doneWaiting; while (!connect->stopped) { /* wait until a frame is ready */ doneWaiting = 0; while (!doneWaiting && !connect->stopped) { /* wait for a frame */ PTHREAD_MUTEX_LOCK(&connect->workerMutex); if (!connect->frameIsReady) { status = pthread_cond_wait(&connect->frameIsReadyCond, &connect->workerMutex); if (status != 0) { ml_log_error("DV connect worker thread failed to wait for condition\n"); /* TODO: don't try again? */ } } /* done waiting if there is a frame ready for processing */ if (connect->frameIsReady) { /* worker will now be busy */ connect->workerIsBusy = 1; connect->workerResult = 0; connect->frameIsReady = 0; doneWaiting = 1; } PTHREAD_MUTEX_UNLOCK(&connect->workerMutex); } /* no more work */ if (connect->stopped) { break; } /* decode and send frame to sink */ workerResult = decode_and_send(connect); /* signal that we are done with the frame */ PTHREAD_MUTEX_LOCK(&connect->workerMutex); connect->workerResult = workerResult; connect->workerIsBusy = 0; status = pthread_cond_signal(&connect->workerIsBusyCond); if (status != 0) { ml_log_error("DV connect worker thread failed to send worker busy condition signal\n"); } PTHREAD_MUTEX_UNLOCK(&connect->workerMutex); } pthread_exit((void*) 0); }