diff --git a/src/add-ons/media/media-add-ons/dvb/DVBCard.cpp b/src/add-ons/media/media-add-ons/dvb/DVBCard.cpp new file mode 100644 index 0000000000..5e86489d2a --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/DVBCard.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include // required on BeOS R5 +#include + +#include "DVBCard.h" + + +DVBCard::DVBCard(const char *path) + : fInitStatus(B_OK) + , fDev(-1) +{ + printf("DVBCard opening %s\n", path); + + fDev = open(path, O_RDWR); + if (fDev < 0) { + printf("DVBCard opening %s failed\n", path); + fInitStatus = B_ERROR; + return; + } + + dvb_frequency_info_t info; + if (do_ioctl(fDev, DVB_GET_FREQUENCY_INFO, &info) < 0) { + printf("DVB_GET_FREQUENCY_INFO failed with error %s\n", strerror(errno)); +// close(fDev); +// fDev = -1; +// return; + } + + fFreqMin = info.frequency_min; + fFreqMax = info.frequency_max; + fFreqStep = info.frequency_step; + + fCaptureActive = false; +} + + +DVBCard::~DVBCard() +{ + if (fDev > 0) + close(fDev); +} + + +status_t +DVBCard::InitCheck() +{ + return fInitStatus; +} + + +status_t +DVBCard::GetCardType(dvb_type_t *type) +{ + dvb_interface_info_t info; + + if (do_ioctl(fDev, DVB_GET_INTERFACE_INFO, &info) < 0) { + printf("DVB_GET_INTERFACE_INFO failed with error %s\n", strerror(errno)); + return errno; + } + + if (info.version < 1) { + printf("DVBCard::GetCardInfo wrong API version\n"); + return B_ERROR; + } + + *type = info.type; + return B_OK; +} + + +status_t +DVBCard::GetCardInfo(char *_name, int max_name_len, char *_info, int max_info_len) +{ + dvb_interface_info_t info; + + if (do_ioctl(fDev, DVB_GET_INTERFACE_INFO, &info) < 0) { + printf("DVB_GET_INTERFACE_INFO failed with error %s\n", strerror(errno)); + return errno; + } + +// strlcpy(_name, info.name, max_name_len); +// strlcpy(_info, info.info, max_info_len); + strcpy(_name, info.name); + strcpy(_info, info.info); + + if (info.version < 1) { + printf("DVBCard::GetCardInfo wrong API version\n"); + return B_ERROR; + } + + return B_OK; +} + + +status_t +DVBCard::SetTuningParameter(const dvb_tuning_parameters_t& param) +{ + if (fDev < 0) + return B_NO_INIT; + + printf("DVBCard::SetTuningParameter:\n"); +/* + printf("frequency %Ld\n", param.frequency); + printf("inversion %d\n", param.inversion); + printf("bandwidth %d\n", param.bandwidth); + printf("modulation %d\n", param.modulation); + printf("hierarchy %d\n", param.hierarchy); + printf("code_rate_hp %d\n", param.code_rate_hp); + printf("code_rate_lp %d\n", param.code_rate_lp); + printf("transmission_mode %d\n", param.transmission_mode); + printf("guard_interval %d\n", param.guard_interval); + printf("symbolrate %d\n", param.symbolrate); + printf("polarity %d\n", param.polarity); + printf("agc_inversion %d\n", param.agc_inversion); +*/ + params = param;// XXX temporary! + +// if (do_ioctl(fDev, DVB_SET_TUNING_PARAMETERS, const_cast(¶m)) < 0) { +// if (do_ioctl(fDev, DVB_SET_TUNING_PARAMETERS, (void *)(¶m)) < 0) { + if (ioctl(fDev, DVB_SET_TUNING_PARAMETERS, (void *)(¶m)) < 0) { + printf("DVB_SET_TUNING_PARAMETERS failed with error %s\n", strerror(errno)); + return errno; + } + + dvb_status_t status; + + bigtime_t start = system_time(); + bool has_lock = false; + bool has_signal = false; + bool has_carrier = false; + do { + if (do_ioctl(fDev, DVB_GET_STATUS, &status) < 0) { + printf("DVB_GET_STATUS failed with error %s\n", strerror(errno)); + return errno; + } + if (!has_signal && status & DVB_STATUS_SIGNAL) { + has_signal = true; + printf("got signal after %.6f\n", (system_time() - start) / 1e6); + } + if (!has_carrier && status & DVB_STATUS_CARRIER) { + has_carrier = true; + printf("got carrier after %.6f\n", (system_time() - start) / 1e6); + } + if (!has_lock && status & DVB_STATUS_LOCK) { + has_lock = true; + printf("got lock after %.6f\n", (system_time() - start) / 1e6); + } + + if ((status & (DVB_STATUS_SIGNAL|DVB_STATUS_CARRIER|DVB_STATUS_LOCK)) == (DVB_STATUS_SIGNAL|DVB_STATUS_CARRIER|DVB_STATUS_LOCK)) + break; + snooze(25000); + } while ((system_time() - start) < 5000000); + + + if ((status & (DVB_STATUS_SIGNAL|DVB_STATUS_CARRIER|DVB_STATUS_LOCK)) != (DVB_STATUS_SIGNAL|DVB_STATUS_CARRIER|DVB_STATUS_LOCK)) { + printf("DVB tuning failed! need these status flags: DVB_STATUS_SIGNAL, DVB_STATUS_CARRIER, DVB_STATUS_LOCK\n"); + return B_ERROR; + } + + return B_OK; +} + + +status_t +DVBCard::GetTuningParameter(dvb_tuning_parameters_t *param) +{ + // XXX get from CARD + *param = params; + return B_OK; +} + + +status_t +DVBCard::GetSignalInfo(uint32 *ss, uint32 *ber, uint32 *snr) +{ + if (do_ioctl(fDev, DVB_GET_SS, ss) < 0) { + printf("DVB_GET_SS failed with error %s\n", strerror(errno)); + return errno; + } + if (do_ioctl(fDev, DVB_GET_BER, ber) < 0) { + printf("DVB_GET_BER failed with error %s\n", strerror(errno)); + return errno; + } + if (do_ioctl(fDev, DVB_GET_SNR, snr) < 0) { + printf("DVB_GET_SNR failed with error %s\n", strerror(errno)); + return errno; + } + + printf("ss %08lx, ber %08lx, snr %08lx\n", *ss, *ber, *snr); + printf("ss %lu%%, ber %lu%%, snr %lu%%\n", *ss / (0xffffffff / 100), *ber / (0xffffffff / 100), *snr / (0xffffffff / 100)); + return B_OK; +} + + +status_t +DVBCard::CaptureStart() +{ + printf("DVBCard::CaptureStart\n"); + + if (fCaptureActive) { + printf("CaptureStart already called, doing nothing\n"); + return B_OK; + } + + if (do_ioctl(fDev, DVB_START_CAPTURE, 0) < 0) { + printf("DVB_START_CAPTURE failed with error %s\n", strerror(errno)); + return errno; + } + + fCaptureActive = true; + return B_OK; +} + + +status_t +DVBCard::CaptureStop() +{ + printf("DVBCard::CaptureStop\n"); + + if (!fCaptureActive) { + printf("CaptureStop already called, doing nothing\n"); + return B_OK; + } + + if (do_ioctl(fDev, DVB_STOP_CAPTURE, 0) < 0) { + printf("DVB_STOP_CAPTURE failed with error %s\n", strerror(errno)); + return errno; + } + + fCaptureActive = false; + return B_OK; +} + + +status_t +DVBCard::Capture(void **data, size_t *size, bigtime_t *end_time) +{ + dvb_capture_t cap; + + if (ioctl(fDev, DVB_CAPTURE, &cap) < 0) + return errno; + + *data = cap.data; + *size = cap.size; + *end_time = cap.end_time; + + return B_OK; +} + + +int +DVBCard::do_ioctl(int fDev, ulong op, void *arg) +{ + int res = 0; // silence stupid compiler warning + int i; + for (i = 0; i < 20; i++) { + res = ioctl(fDev, op, arg); + if (res >= 0) + break; + } + if (i != 0) printf("ioctl %lx repeated %d times\n", op, i); + return res; +} diff --git a/src/add-ons/media/media-add-ons/dvb/DVBCard.h b/src/add-ons/media/media-add-ons/dvb/DVBCard.h new file mode 100644 index 0000000000..aaec8be295 --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/DVBCard.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __DVB_CARD_H +#define __DVB_CARD_H + +#include "../drivers/cx23882/dvb.h" + +class DVBCard +{ +public: + DVBCard(const char *path); + ~DVBCard(); + + status_t InitCheck(); + + status_t GetCardType(dvb_type_t *type); + status_t GetCardInfo(char *name, int max_name_len, char *info, int max_info_len); + status_t GetSignalInfo(uint32 *ss, uint32 *ber, uint32 *snr); + + status_t SetTuningParameter(const dvb_tuning_parameters_t& param); + status_t GetTuningParameter(dvb_tuning_parameters_t *param); + + status_t CaptureStart(); + status_t CaptureStop(); + status_t Capture(void **data, size_t *size, bigtime_t *end_time); + +private: + + int do_ioctl(int fDev, ulong op, void *arg); + + status_t fInitStatus; + int fDev; + + int64 fFreqMin; + int64 fFreqMax; + int64 fFreqStep; + + bool fCaptureActive; + + dvb_tuning_parameters_t params; // XXX temporary cache +}; + + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/DVBMediaAddon.cpp b/src/add-ons/media/media-add-ons/dvb/DVBMediaAddon.cpp new file mode 100644 index 0000000000..d72b6f6c34 --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/DVBMediaAddon.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "DVBMediaAddon.h" +#include "DVBCard.h" +#include "DVBMediaNode.h" +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +struct device_info +{ + DVBCard * card; + char name[200]; + char info[200]; + media_format formats[5]; + flavor_info flavor; + bool active; // workaround for zeta +}; + + +extern "C" BMediaAddOn * +make_media_addon(image_id id) +{ + return new DVBMediaAddon(id); +} + + +DVBMediaAddon::DVBMediaAddon(image_id id) + : BMediaAddOn(id) +{ + ScanFolder("/dev/dvb"); +} + + +DVBMediaAddon::~DVBMediaAddon() +{ + FreeDeviceList(); +} + + +status_t +DVBMediaAddon::InitCheck(const char ** out_failure_text) +{ + if (!fDeviceList.CountItems()) { + *out_failure_text = "No supported device found"; + return B_ERROR; + } + return B_OK; +} + + +int32 +DVBMediaAddon::CountFlavors() +{ + return fDeviceList.CountItems(); +} + + +status_t +DVBMediaAddon::GetFlavorAt(int32 n, const flavor_info ** out_info) +{ + device_info *dev = (device_info *)fDeviceList.ItemAt(n); + if (!dev) + return B_ERROR; + *out_info = &dev->flavor; + return B_OK; +} + + +BMediaNode * +DVBMediaAddon::InstantiateNodeFor(const flavor_info * info, BMessage * config, status_t * out_error) +{ + printf("DVB: DVBMediaAddon::InstantiateNodeFor\n"); + + device_info *dev = (device_info *)fDeviceList.ItemAt(info->internal_id); + if (!dev || dev->flavor.internal_id != info->internal_id) { + *out_error = B_ERROR; + return NULL; + } + *out_error = B_OK; + + // workaround for Zeta + if (dev->active) { + printf("DVB: max instances violation!!!\n"); + *out_error = B_ERROR; + return NULL; + } else { + dev->active = true; + } + + return new DVBMediaNode(this, dev->name, dev->flavor.internal_id, dev->card, &dev->active); +} + + +bool +DVBMediaAddon::WantsAutoStart() +{ +#if BUILD_FOR_HAIKU + printf("DVB: DVBMediaAddon::WantsAutoStart - NO\n"); + return false; +#else + printf("DVB: DVBMediaAddon::WantsAutoStart - YES\n"); + return true; +#endif +} + + +status_t +DVBMediaAddon::AutoStart(int index, BMediaNode **outNode, int32 *outInternalID, bool *outHasMore) +{ + printf("DVB: DVBMediaAddon::AutoStart, index %d\n", index); + +#if BUILD_FOR_HAIKU + return B_ERROR; +#else + + device_info *dev = (device_info *)fDeviceList.ItemAt(index); + if (!dev) { + printf("DVB: DVBMediaAddon::AutoStart, bad index\n"); + return B_BAD_INDEX; + } + + *outHasMore = fDeviceList.ItemAt(index + 1) ? true : false; + + // workaround for Zeta + if (dev->active) { + printf("DVB: max instances violation!!!\n"); + *outInternalID = -1; + *outNode = NULL; + return B_MEDIA_ADDON_FAILED; + } + + dev->active = true; + + *outInternalID = dev->flavor.internal_id; + *outNode = new DVBMediaNode(this, dev->name, dev->flavor.internal_id, dev->card, &dev->active); + + printf("DVB: DVBMediaAddon::AutoStart, success\n"); + + return B_OK; +#endif +} + + +void +DVBMediaAddon::ScanFolder(const char *path) +{ + BDirectory dir(path); + if (dir.InitCheck() != B_OK) + return; + + BEntry ent; + + while (dir.GetNextEntry(&ent) == B_OK) { + BPath path(&ent); + if (ent.IsDirectory()) { + ScanFolder(path.Path()); + } else { + DVBCard *card = new DVBCard(path.Path()); + if (card->InitCheck() == B_OK) + AddDevice(card, path.Path()); + else + delete card; + } + } +} + + +void +DVBMediaAddon::AddDevice(DVBCard *card, const char *path) +{ + dvb_type_t type; + char name[250]; + char info[1000]; + const char *dvbtype; + const char *dvbnumber; + + // get card name, info and type + if (B_OK != card->GetCardType(&type)) { + printf("DVBMediaAddon::AddDevice: getting DVB card type failed\n"); + return; + } + if (B_OK != card->GetCardInfo(name, sizeof(name), info, sizeof(info))) { + printf("DVBMediaAddon::AddDevice: getting DVB card info failed\n"); + return; + } + + // get type + switch (type) { + case DVB_TYPE_DVB_C: dvbtype = "C"; break; + case DVB_TYPE_DVB_H: dvbtype = "H"; break; + case DVB_TYPE_DVB_S: dvbtype = "S"; break; + case DVB_TYPE_DVB_T: dvbtype = "T"; break; + default: + printf("DVBMediaAddon::AddDevice: unsupported DVB type %d\n", type); + return; + } + + // get interface number + const char *p = strrchr(path, '/'); + dvbnumber = p ? p + 1 : ""; + + // create device_info + device_info *dev = new device_info; + fDeviceList.AddItem(dev); + + dev->card = card; + + dev->active = false; // workaround for Zeta + + // setup name and info, it's important that name starts with "DVB" +// snprintf(dev->name, sizeof(dev->name), "DVB-%s %s (%s)", dvbtype, dvbnumber, name); +// strlcpy(dev->info, info, sizeof(dev->info)); + sprintf(dev->name, "DVB-%s %s %s", dvbtype, name, dvbnumber); + strcpy(dev->info, info); + + // setup supported formats (the lazy way) + memset(dev->formats, 0, sizeof(dev->formats)); + dev->formats[0].type = B_MEDIA_RAW_VIDEO; + dev->formats[1].type = B_MEDIA_RAW_AUDIO; + dev->formats[2].type = B_MEDIA_ENCODED_VIDEO; + dev->formats[3].type = B_MEDIA_ENCODED_AUDIO; + dev->formats[4].type = B_MEDIA_MULTISTREAM; + + // setup flavor info + dev->flavor.name = dev->name; + dev->flavor.info = dev->info; + dev->flavor.kinds = B_BUFFER_PRODUCER | B_CONTROLLABLE | B_PHYSICAL_INPUT; + dev->flavor.flavor_flags = B_FLAVOR_IS_GLOBAL; + dev->flavor.internal_id = fDeviceList.CountItems() - 1; + dev->flavor.possible_count = 1; + dev->flavor.in_format_count = 0; + dev->flavor.in_format_flags = 0; + dev->flavor.in_formats = 0; + dev->flavor.out_format_count = 5; + dev->flavor.out_format_flags = 0; + dev->flavor.out_formats = dev->formats; +} + + +void +DVBMediaAddon::FreeDeviceList() +{ + device_info *dev; + while ((dev = (device_info *)fDeviceList.RemoveItem((int32)0))) { + delete dev->card; + delete dev; + } +} diff --git a/src/add-ons/media/media-add-ons/dvb/DVBMediaAddon.h b/src/add-ons/media/media-add-ons/dvb/DVBMediaAddon.h new file mode 100644 index 0000000000..4371d3b73e --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/DVBMediaAddon.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DVB_MEDIA_ADDON_H_ +#define _DVB_MEDIA_ADDON_H_ + +#include + +class DVBCard; + +class DVBMediaAddon : public BMediaAddOn +{ +public: + DVBMediaAddon(image_id id); + ~DVBMediaAddon(); + + status_t InitCheck(const char **out_failure_text); + + int32 CountFlavors(); + + status_t GetFlavorAt(int32 n, const flavor_info **out_info); + + BMediaNode *InstantiateNodeFor(const flavor_info *info, BMessage *config, status_t *out_error); + + bool WantsAutoStart(); + status_t AutoStart(int index, BMediaNode **outNode, int32 *outInternalID, bool *outHasMore); + +protected: + void ScanFolder(const char *path); + + void AddDevice(DVBCard *card, const char *path); + void FreeDeviceList(); + +protected: + BList fDeviceList; +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/DVBMediaNode.cpp b/src/add-ons/media/media-add-ons/dvb/DVBMediaNode.cpp new file mode 100644 index 0000000000..aac0c9aa1d --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/DVBMediaNode.cpp @@ -0,0 +1,2701 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "DefaultDecoder.h" +#include "Support.h" +#include "HeaderFormat.h" +#include "packet.h" +#include "packet_queue.h" +#include "pes_extract.h" +#include "media_header_ex.h" +#include "config.h" + +//#define DUMP_VIDEO +//#define DUMP_AUDIO +//#define DUMP_RAW_AUDIO + + +#include "DVBMediaNode.h" + +#define ENABLE_TRACE +//#define ENABLE_TRACE_TIMING + +#undef TRACE + +#ifdef ENABLE_TRACE + #define TRACE printf +#else + #define TRACE(a...) +#endif + +#ifdef ENABLE_TRACE_TIMING + #define TRACE_TIMING printf +#else + #define TRACE_TIMING(a...) +#endif + +#define RETURN_IF_ERROR(expr) { status_t e = (expr); if (e != B_OK) return e; } + +#define ID_RAW_VIDEO 0 +#define ID_RAW_AUDIO 1 +#define ID_ENC_VIDEO 2 +#define ID_ENC_AUDIO 3 +#define ID_TS 4 + +// Timeouts for requesting buffers, if the system is busy, +// the output buffer queue is full, requesting a buffer will +// timeout, and we need to drop the current data +#define VIDEO_BUFFER_REQUEST_TIMEOUT 20000 +#define AUDIO_BUFFER_REQUEST_TIMEOUT 10000 + +// DVB data arrives early and with a timestamp, this is used to validate +// that the timestamp is correct and we don't get stuck +#define VIDEO_MAX_EARLY 3000000 // up to 3 seconds too early +#define VIDEO_MAX_LATE 50000 // no more than 50 ms too late +#define AUDIO_MAX_EARLY 3000000 // up to 3 seconds too early +#define AUDIO_MAX_LATE 50000 // no more than 50 ms too late + +#define PROCESSING_LATENCY 1500 // assumed latency for sending the buffer + +#define STOP_CAPTURE_WHILE_TUNING 1 + +#define MPEG2_VIDEO_DECODER_PATH "/boot/home/config/add-ons/media/dvb/video_decoder_addon" +#define MPEG2_AUDIO_DECODER_PATH "/boot/home/config/add-ons/media/dvb/audio_decoder_addon" +#define AC3_AUDIO_DECODER_PATH "/boot/home/config/add-ons/media/dvb/ac3_decoder_addon" +#define DEINTERLACE_FILTER_PATH "/boot/home/config/add-ons/media/dvb/deinterlace_filter_addon" + + +#define M_REFRESH_PARAMETER_WEB (BTimedEventQueue::B_USER_EVENT + 1) + + +DVBMediaNode::DVBMediaNode( + BMediaAddOn *addon, const char *name, + int32 internal_id, DVBCard *card, bool *active) // bool *active is a workaround for Zeta + : BMediaNode(name) + , BMediaEventLooper() + , BBufferProducer(B_MEDIA_RAW_VIDEO) + , BControllable() + , fStopDisabled(false) + , fOutputEnabledRawVideo(false) + , fOutputEnabledRawAudio(false) + , fOutputEnabledEncVideo(false) + , fOutputEnabledEncAudio(false) + , fOutputEnabledTS(false) + , fCardDataQueue(new PacketQueue(6)) + , fRawVideoQueue(new PacketQueue(56)) + , fRawAudioQueue(new PacketQueue(56)) + , fEncVideoQueue(new PacketQueue(56)) + , fEncAudioQueue(new PacketQueue(56)) + , fMpegTsQueue(new PacketQueue(16)) + , fCard(card) + , fCaptureThreadsActive(false) + , fThreadIdCardReader(-1) + , fThreadIdMpegDemux(-1) + , fThreadIdRawAudio(-1) + , fThreadIdRawVideo(-1) + , fThreadIdEncAudio(-1) + , fThreadIdEncVideo(-1) + , fThreadIdMpegTS(-1) + , fTerminateThreads(false) + , fDemux(new TSDemux(fRawVideoQueue, fRawAudioQueue, fEncVideoQueue, fEncAudioQueue, fMpegTsQueue)) + , fBufferGroupRawVideo(0) + , fBufferGroupRawAudio(0) + , fInterfaceType(DVB_TYPE_UNKNOWN) + , fAudioPid(-1) + , fVideoPid(-1) + , fPcrPid(-1) + , fTuningSuccess(false) + , fCaptureActive(false) + , fVideoDelaySem(create_sem(0, "video delay sem")) + , fAudioDelaySem(create_sem(0, "audio delay sem")) + , fSelectedState(-1) + , fSelectedRegion(-1) + , fSelectedChannel(-1) + , fSelectedAudio(-1) + , fStateList(new StringList) + , fRegionList(new StringList) + , fChannelList(new StringList) + , fAudioList(new StringList) + , fVideoDecoder(0) + , fAudioDecoder(0) + , fCurrentVideoPacket(0) + , fCurrentAudioPacket(0) + , fMpeg2VideoDecoderImage(-1) + , fMpeg2AudioDecoderImage(-1) + , fAC3DecoderImage(-1) + , fDeinterlaceFilterImage(-1) + , instantiate_mpeg2_video_decoder(NULL) + , instantiate_mpeg2_audio_decoder(NULL) + , instantiate_ac3_audio_decoder(NULL) + , instantiate_deinterlace_filter(NULL) + , fActive(active) +{ + TRACE("DVBMediaNode::DVBMediaNode\n"); + + AddNodeKind(B_PHYSICAL_INPUT); + + fInternalID = internal_id; + fAddOn = addon; + + fInitStatus = B_OK; + + LoadAddons(); + + InitDefaultFormats(); + + // in the beginning, the required formats are the same as the defaults + fRequiredFormatRawVideo = fDefaultFormatRawVideo; + fRequiredFormatRawAudio = fDefaultFormatRawAudio; + fRequiredFormatEncVideo = fDefaultFormatEncVideo; + fRequiredFormatEncAudio = fDefaultFormatEncAudio; + fRequiredFormatTS = fDefaultFormatTS; + + // start the BMediaEventLooper control loop running + Run(); + + TRACE("current RunMode = %d\n", RunMode()); + +#ifdef DUMP_VIDEO + fVideoFile = open("/boot/home/dvb-video.mpg", O_RDWR | O_CREAT | O_TRUNC); +#endif +#ifdef DUMP_AUDIO + fAudioFile = open("/boot/home/dvb-audio.mpg", O_RDWR | O_CREAT | O_TRUNC); +#endif +#ifdef DUMP_RAW_AUDIO + fRawAudioFile = open("/boot/home/dvb-audio.raw", O_RDWR | O_CREAT | O_TRUNC); +#endif +} + + +DVBMediaNode::~DVBMediaNode() +{ + TRACE("DVBMediaNode::~DVBMediaNode\n"); + + StopCapture(); + + delete_sem(fVideoDelaySem); + delete_sem(fAudioDelaySem); + + // fCard is owned by the media addon + delete fCardDataQueue; + delete fRawVideoQueue; + delete fRawAudioQueue; + delete fEncVideoQueue; + delete fEncAudioQueue; + delete fMpegTsQueue; + + delete fDemux; + +printf("deleting video buffer group...\n"); + delete fBufferGroupRawVideo; +printf("deleting video buffer group done\n"); + +printf("deleting audio buffer group...\n"); + // deleting the audio buffer group does hang on Zeta neo, + // but works on Haiku and seems to work on Zeta 1.0 with updated media_server + delete fBufferGroupRawAudio; +printf("deleting audio buffer group done\n"); + + delete fStateList; + delete fRegionList; + delete fChannelList; + delete fAudioList; + + UnloadAddons(); + +#ifdef DUMP_VIDEO + close(fVideoFile); +#endif +#ifdef DUMP_AUDIO + close(fAudioFile); +#endif +#ifdef DUMP_RAW_AUDIO + close(fRawAudioFile); +#endif + + *fActive = false; // workaround for zeta +} + + +image_id +DVBMediaNode::LoadAddon(const char *path, const char *name, void **ptr) +{ + image_id id = load_add_on(path); + if (id <= 0) { + *ptr = NULL; + printf("loading addon '%s' failed\n", path); + return -1; + } + status_t s = get_image_symbol(id, name, B_SYMBOL_TYPE_TEXT, ptr); + if (s < B_OK) { + unload_add_on(id); + *ptr = NULL; + printf("locating function '%s' in addon '%s' failed\n", name, path); + return -1; + } + return id; +} + + +void +DVBMediaNode::LoadAddons() +{ + fMpeg2VideoDecoderImage = LoadAddon(MPEG2_VIDEO_DECODER_PATH, + "instantiate_mpeg2_video_decoder", + (void **)&instantiate_mpeg2_video_decoder); + fMpeg2AudioDecoderImage = LoadAddon(MPEG2_AUDIO_DECODER_PATH, + "instantiate_mpeg2_audio_decoder", + (void **)&instantiate_mpeg2_audio_decoder); + fAC3DecoderImage = LoadAddon(AC3_AUDIO_DECODER_PATH, + "instantiate_ac3_audio_decoder", + (void **)&instantiate_ac3_audio_decoder); + fDeinterlaceFilterImage = LoadAddon(DEINTERLACE_FILTER_PATH, + "instantiate_deinterlace_filter", + (void **)&instantiate_deinterlace_filter); +} + + +void +DVBMediaNode::UnloadAddons() +{ + if (fMpeg2VideoDecoderImage >= 0) + unload_add_on(fMpeg2VideoDecoderImage); + if (fMpeg2AudioDecoderImage >= 0) + unload_add_on(fMpeg2AudioDecoderImage); + if (fAC3DecoderImage >= 0) + unload_add_on(fAC3DecoderImage); + if (fDeinterlaceFilterImage >= 0) + unload_add_on(fDeinterlaceFilterImage); +} + + +/* BMediaNode */ + + +BMediaAddOn * +DVBMediaNode::AddOn(int32 *internal_id) const +{ + if (internal_id) + *internal_id = fInternalID; + return fAddOn; +} + + +status_t +DVBMediaNode::HandleMessage(int32 message, const void *data, size_t size) +{ + return B_ERROR; +} + + +void +DVBMediaNode::Preroll() +{ + /* This hook may be called before the node is started to give the hardware + * a chance to start. */ +} + + +void +DVBMediaNode::SetTimeSource(BTimeSource *time_source) +{ + TRACE("DVBMediaNode::SetTimeSource\n"); + //printf("current RunMode = %d\n", RunMode()); + //printf("_m_recordDelay = %Ld\n", _m_recordDelay); +} + + +void +DVBMediaNode::SetRunMode(run_mode mode) +{ + TRACE("DVBMediaNode::SetRunMode: %d\n", mode); + TRACE("current RunMode = %d\n", RunMode()); + //printf("_m_recordDelay = %Ld\n", _m_recordDelay); +} + + +/* BMediaEventLooper */ + + +void +DVBMediaNode::NodeRegistered() +{ + TRACE("DVBMediaNode::NodeRegistered\n"); + + fOutputRawVideo.node = Node(); + fOutputRawVideo.source.port = ControlPort(); + fOutputRawVideo.source.id = ID_RAW_VIDEO; + fOutputRawVideo.destination = media_destination::null; + fOutputRawVideo.format = fDefaultFormatRawVideo; + strcpy(fOutputRawVideo.name, SourceDefaultName(fOutputRawVideo.source)); + + fOutputRawAudio.node = Node(); + fOutputRawAudio.source.port = ControlPort(); + fOutputRawAudio.source.id = ID_RAW_AUDIO; + fOutputRawAudio.destination = media_destination::null; + fOutputRawAudio.format = fDefaultFormatRawAudio; + strcpy(fOutputRawAudio.name, SourceDefaultName(fOutputRawAudio.source)); + + fOutputEncVideo.node = Node(); + fOutputEncVideo.source.port = ControlPort(); + fOutputEncVideo.source.id = ID_ENC_VIDEO; + fOutputEncVideo.destination = media_destination::null; + fOutputEncVideo.format = fDefaultFormatEncVideo; + strcpy(fOutputEncVideo.name, SourceDefaultName(fOutputEncVideo.source)); + + fOutputEncAudio.node = Node(); + fOutputEncAudio.source.port = ControlPort(); + fOutputEncAudio.source.id = ID_ENC_AUDIO; + fOutputEncAudio.destination = media_destination::null; + fOutputEncAudio.format = fDefaultFormatEncAudio; + strcpy(fOutputEncAudio.name, SourceDefaultName(fOutputEncAudio.source)); + + fOutputTS.node = Node(); + fOutputTS.source.port = ControlPort(); + fOutputTS.source.id = ID_TS; + fOutputTS.destination = media_destination::null; + fOutputTS.format = fDefaultFormatTS; + strcpy(fOutputTS.name, SourceDefaultName(fOutputTS.source)); + + fCard->GetCardType(&fInterfaceType); + + // set control thread priority + SetPriority(110); + + LoadSettings(); + + RefreshParameterWeb(); + + // this nodes operates in recording mode, so set it (will be done asynchronously) + BMediaRoster::Roster()->SetRunModeNode(Node(), B_RECORDING); + // as it's a notification hook, calling this doesn't work: SetRunMode(B_RECORDING); + + //printf("RunMode = %d\n", RunMode()); + //printf("_m_recordDelay = %Ld\n", _m_recordDelay); +} + + +void +DVBMediaNode::Stop(bigtime_t performance_time, bool immediate) +{ + if (fStopDisabled) + return; + else + BMediaEventLooper::Stop(performance_time, immediate); +} + + +void +DVBMediaNode::HandleEvent(const media_timed_event *event, + bigtime_t lateness, bool realTimeEvent) +{ + + switch(event->type) + { + case M_REFRESH_PARAMETER_WEB: + RefreshParameterWeb(); + break; + case BTimedEventQueue::B_START: + HandleStart(event->event_time); + break; + case BTimedEventQueue::B_STOP: + HandleStop(); + break; + case BTimedEventQueue::B_WARP: + HandleTimeWarp(event->bigdata); + break; + case BTimedEventQueue::B_SEEK: + HandleSeek(event->bigdata); + break; + case BTimedEventQueue::B_HANDLE_BUFFER: + case BTimedEventQueue::B_DATA_STATUS: + case BTimedEventQueue::B_PARAMETER: + default: + TRACE("DVBMediaNode::HandleEvent: Unhandled event -- %lx\n", event->type); + break; + } +} + + +/* BBufferProducer */ + + +status_t +DVBMediaNode::FormatChangeRequested(const media_source &source, + const media_destination &destination, media_format *io_format, + int32 *_deprecated_) +{ + TRACE("DVBMediaNode::FormatChangeRequested denied: %s\n", SourceDefaultName(source)); + return B_ERROR; +} + + +status_t +DVBMediaNode::GetNextOutput(int32 *cookie, media_output *out_output) +{ + switch (*cookie) { + case 0: *out_output = fOutputRawVideo; break; + case 1: *out_output = fOutputRawAudio; break; + case 2: *out_output = fOutputEncVideo; break; + case 3: *out_output = fOutputEncAudio; break; + case 4: *out_output = fOutputTS; break; + default: return B_BAD_INDEX; + } + + (*cookie) += 1; + return B_OK; +} + + +status_t +DVBMediaNode::DisposeOutputCookie(int32 cookie) +{ + return B_OK; +} + + +status_t +DVBMediaNode::SetBufferGroup(const media_source &source, BBufferGroup *group) +{ + TRACE("DVBMediaNode::SetBufferGroup denied: %s\n", SourceDefaultName(source)); + return B_ERROR; +} + + +status_t +DVBMediaNode::VideoClippingChanged(const media_source &for_source, + int16 num_shorts, int16 *clip_data, + const media_video_display_info &display, int32 *_deprecated_) +{ + return B_ERROR; +} + + +status_t +DVBMediaNode::GetLatency(bigtime_t *out_latency) +{ + if (B_OK != BBufferProducer::GetLatency(out_latency)) + *out_latency = 50000; + + printf("DVBMediaNode::GetLatency: %Ld\n", *out_latency); + *out_latency += PROCESSING_LATENCY; + return B_OK; +} + + +status_t +DVBMediaNode::FormatSuggestionRequested( + media_type type, int32 quality, media_format *format) +{ + TRACE("DVBMediaNode::FormatSuggestionRequested\n"); + + switch (type) { + case B_MEDIA_RAW_VIDEO: *format = fDefaultFormatRawVideo; break; + case B_MEDIA_RAW_AUDIO: *format = fDefaultFormatRawAudio; break; + case B_MEDIA_ENCODED_VIDEO: *format = fDefaultFormatEncVideo; break; + case B_MEDIA_ENCODED_AUDIO: *format = fDefaultFormatEncAudio; break; + case B_MEDIA_MULTISTREAM: *format = fDefaultFormatTS; break; + default: TRACE("Bad type!\n"); return B_MEDIA_BAD_FORMAT; + } + + #ifdef DEBUG + TRACE("suggested format: "); + PrintFormat(*format); + #endif + + return B_OK; +} + + +status_t +DVBMediaNode::FormatProposal(const media_source &source, media_format *format) +{ + TRACE("DVBMediaNode::FormatProposal: %s\n", SourceDefaultName(source)); + + /* The connection process: + * we are here => BBufferProducer::FormatProposal + * BBufferConsumer::AcceptFormat + * BBufferProducer::PrepareToConnect + * BBufferConsumer::Connected + * BBufferProducer::Connect + * + * What we need to do: + * - if the format contains a wildcard AND we have a requirement for that + * field, set it to the value we need. + * - if a field has a value that is not wildcard and not supported by us, + * we don't change it, and return B_MEDIA_BAD_FORMAT + * - after we are done, the format may still contain wildcards. + */ + + if (source.port != ControlPort()) + goto _bad_source; + + #ifdef DEBUG + TRACE("proposed format: "); + PrintFormat(*format); + TRACE("required format: "); + switch (source.id) { + case ID_RAW_VIDEO: PrintFormat(fRequiredFormatRawVideo); break; + case ID_RAW_AUDIO: PrintFormat(fRequiredFormatRawAudio); break; + case ID_ENC_VIDEO: PrintFormat(fRequiredFormatEncVideo); break; + case ID_ENC_AUDIO: PrintFormat(fRequiredFormatEncAudio); break; + case ID_TS: PrintFormat(fRequiredFormatTS); break; + } + #endif + + switch (source.id) { + case ID_RAW_VIDEO: + // check if destination still available + if (fOutputRawVideo.destination != media_destination::null) + goto _bad_source; + // set requirements and check if compatible + color_space c; + c = format->u.raw_video.display.format; + format->SpecializeTo(&fRequiredFormatRawVideo); + format->u.raw_video.display.format = c; +// if (!format->Matches(&fRequiredFormatRawVideo)) +// goto _bad_format_1; + if (!VerifyFormatRawVideo(format->u.raw_video)) + goto _bad_format_2; + break; + + case ID_RAW_AUDIO: + // check if destination still available + if (fOutputRawAudio.destination != media_destination::null) + goto _bad_source; + // set requirements and check if compatible + format->SpecializeTo(&fRequiredFormatRawAudio); + if (!format->Matches(&fRequiredFormatRawAudio)) + goto _bad_format_1; + if (!VerifyFormatRawAudio(format->u.raw_audio)) + goto _bad_format_2; + break; + + case ID_ENC_VIDEO: + // check if destination still available + if (fOutputEncVideo.destination != media_destination::null) + goto _bad_source; + // set requirements and check if compatible + format->SpecializeTo(&fRequiredFormatEncVideo); + if (!format->Matches(&fRequiredFormatEncVideo)) + goto _bad_format_1; + break; + + case ID_ENC_AUDIO: + // check if destination still available + if (fOutputEncAudio.destination != media_destination::null) + goto _bad_source; + // set requirements and check if compatible + format->SpecializeTo(&fRequiredFormatEncAudio); + if (!format->Matches(&fRequiredFormatEncAudio)) + goto _bad_format_1; + break; + + case ID_TS: + // check if destination still available + if (fOutputTS.destination != media_destination::null) + goto _bad_source; + // set requirements and check if compatible + format->SpecializeTo(&fRequiredFormatTS); + if (!format->Matches(&fRequiredFormatTS)) + goto _bad_format_1; + break; + + default: + goto _bad_source; + } + + #ifdef DEBUG + TRACE("final format: "); + PrintFormat(*format); + #endif + + return B_OK; + +_bad_source: + TRACE("Error: bad source!\n"); + return B_MEDIA_BAD_SOURCE; + +_bad_format_1: + TRACE("Error, bad format (1): "); + goto _bad_format; + +_bad_format_2: + TRACE("Error, bad format (2): "); + goto _bad_format; + +_bad_format: + #ifdef DEBUG + PrintFormat(*format); + #endif + return B_MEDIA_BAD_FORMAT; +} + + +status_t +DVBMediaNode::PrepareToConnect(const media_source &source, + const media_destination &destination, media_format *format, + media_source *out_source, char *out_name) +{ + /* The connection process: + * BBufferProducer::FormatProposal + * BBufferConsumer::AcceptFormat + * we are here => BBufferProducer::PrepareToConnect + * BBufferConsumer::Connected + * BBufferProducer::Connect + * + * At this point, the consumer's AcceptFormat() method has been called, + * and that node has potentially changed the proposed format. It may + * also have left wildcards in the format. PrepareToConnect() + * *must* fully specialize the format before returning! + */ + + TRACE("DVBMediaNode::PrepareToConnect: %s\n", SourceDefaultName(source)); + + #ifdef DEBUG + TRACE("connecting format: "); + PrintFormat(*format); + TRACE("required format: "); + switch (source.id) { + case ID_RAW_VIDEO: PrintFormat(fRequiredFormatRawVideo); break; + case ID_RAW_AUDIO: PrintFormat(fRequiredFormatRawAudio); break; + case ID_ENC_VIDEO: PrintFormat(fRequiredFormatEncVideo); break; + case ID_ENC_AUDIO: PrintFormat(fRequiredFormatEncAudio); break; + case ID_TS: PrintFormat(fRequiredFormatTS); break; + } + #endif + + // is the source valid? + if (source.port != ControlPort()) + goto _bad_source; + + // 1) check if the output is still available, + // 2) specialize and verify the format + switch (source.id) { + case ID_RAW_VIDEO: + if (fOutputRawVideo.destination != media_destination::null) + goto _already_connected; + SpecializeFormatRawVideo(&format->u.raw_video); +// if (!format->Matches(&fRequiredFormatRawVideo)) +// goto _bad_format; + if (!VerifyFormatRawVideo(format->u.raw_video)) + goto _bad_format; + break; + + case ID_RAW_AUDIO: + if (fOutputRawAudio.destination != media_destination::null) + goto _already_connected; + SpecializeFormatRawAudio(&format->u.raw_audio); + if (!format->Matches(&fRequiredFormatRawAudio)) + goto _bad_format; + if (!VerifyFormatRawAudio(format->u.raw_audio)) + goto _bad_format; + break; + + case ID_ENC_VIDEO: + if (fOutputEncVideo.destination != media_destination::null) + goto _already_connected; + SpecializeFormatEncVideo(&format->u.encoded_video); + if (!format->Matches(&fRequiredFormatEncVideo)) + goto _bad_format; + break; + + case ID_ENC_AUDIO: + if (fOutputEncAudio.destination != media_destination::null) + goto _already_connected; + SpecializeFormatEncAudio(&format->u.encoded_audio); + if (!format->Matches(&fRequiredFormatRawVideo)) + goto _bad_format; + break; + + case ID_TS: + if (fOutputTS.destination != media_destination::null) + goto _already_connected; + SpecializeFormatTS(&format->u.multistream); + if (!format->Matches(&fRequiredFormatTS)) + goto _bad_format; + break; + + default: + goto _bad_source; + } + + #ifdef DEBUG + TRACE("final format: "); + PrintFormat(*format); + #endif + + // reserve the connection by setting destination + // set the output's format to the new format + SetOutput(source, destination, *format); + + // set source and suggest a name + *out_source = source; + strcpy(out_name, SourceDefaultName(source)); + + return B_OK; + +_bad_source: + TRACE("Error: bad source!\n"); + return B_MEDIA_BAD_SOURCE; + +_bad_format: + #ifdef DEBUG + TRACE("Error, bad format: "); + PrintFormat(*format); + #endif + return B_MEDIA_BAD_FORMAT; + +_already_connected: + TRACE("Error: already connected!\n"); + return B_MEDIA_ALREADY_CONNECTED; +} + + +void +DVBMediaNode::Connect(status_t error, const media_source &source, + const media_destination &destination, const media_format &format, + char *io_name) +{ + /* The connection process: + * BBufferProducer::FormatProposal + * BBufferConsumer::AcceptFormat + * BBufferProducer::PrepareToConnect + * BBufferConsumer::Connected + * we are here => BBufferProducer::Connect + */ + + TRACE("DVBMediaNode::Connect: %s\n", SourceDefaultName(source)); + + if (error != B_OK) { + TRACE("Error during connecting\n"); + // if an error occured, unreserve the connection + ResetOutput(source); + return; + } + + #ifdef DEBUG + TRACE("connected format: "); + PrintFormat(format); + #endif + + // Since the destination is allowed to be changed by the + // consumer, the one we got in PrepareToConnect() is no + // longer correct, and must be updated here. + SetOutput(source, destination, format); + + // Set output as connected + switch (source.id) { + case ID_RAW_VIDEO: fOutputEnabledRawVideo = true; break; + case ID_RAW_AUDIO: fOutputEnabledRawAudio = true; break; + case ID_ENC_VIDEO: fOutputEnabledEncVideo = true; break; + case ID_ENC_AUDIO: fOutputEnabledEncAudio = true; break; + case ID_TS: fOutputEnabledTS = true; break; + default: break; + } + + // if the connection has no name, we set it now + if (strlen(io_name) == 0) + strcpy(io_name, SourceDefaultName(source)); + + #ifdef DEBUG + bigtime_t latency; + media_node_id ts; + if (B_OK != FindLatencyFor(destination, &latency, &ts)) + TRACE("FindLatencyFor failed\n"); + else + TRACE("downstream latency %Ld\n", latency); + #endif +} + + +void +DVBMediaNode::Disconnect(const media_source &source, + const media_destination &destination) +{ + TRACE("DVBMediaNode::Disconnect: %s\n", SourceDefaultName(source)); + + // unreserve the connection + ResetOutput(source); + + // Set output to disconnected + switch (source.id) { + case ID_RAW_VIDEO: fOutputEnabledRawVideo = false; break; + case ID_RAW_AUDIO: fOutputEnabledRawAudio = false; break; + case ID_ENC_VIDEO: fOutputEnabledEncVideo = false; break; + case ID_ENC_AUDIO: fOutputEnabledEncAudio = false; break; + case ID_TS: fOutputEnabledTS = false; break; + default: break; + } +} + + +void +DVBMediaNode::LateNoticeReceived(const media_source &source, + bigtime_t how_much, bigtime_t performance_time) +{ + TRACE("DVBMediaNode::LateNoticeReceived %Ld late at %Ld\n", how_much, performance_time); +} + + +void +DVBMediaNode::EnableOutput(const media_source &source, bool enabled, + int32 *_deprecated_) +{ + TRACE("DVBMediaNode::EnableOutput id = %ld, enabled = %d\n", source.id, enabled); + /* not used yet + switch (source.id) { + case ID_RAW_VIDEO: fOutputEnabledRawVideo = enabled; break; + case ID_RAW_AUDIO: fOutputEnabledRawAudio = enabled; break; + case ID_ENC_VIDEO: fOutputEnabledEncVideo = enabled; break; + case ID_ENC_AUDIO: fOutputEnabledEncAudio = enabled; break; + case ID_TS: fOutputEnabledTS = enabled; break; + default: break; + } + */ +} + + +status_t +DVBMediaNode::SetPlayRate(int32 numer, int32 denom) +{ + + return B_ERROR; +} + + +void +DVBMediaNode::AdditionalBufferRequested(const media_source &source, + media_buffer_id prev_buffer, bigtime_t prev_time, + const media_seek_tag *prev_tag) +{ + TRACE("DVBMediaNode::AdditionalBufferRequested: %s\n", SourceDefaultName(source)); +} + + +void +DVBMediaNode::LatencyChanged(const media_source &source, + const media_destination &destination, bigtime_t new_latency, + uint32 flags) +{ + TRACE("DVBMediaNode::LatencyChanged to %Ld\n", new_latency); +} + +/* DVBMediaNode */ + + +void +DVBMediaNode::HandleTimeWarp(bigtime_t performance_time) +{ + TRACE("DVBMediaNode::HandleTimeWarp at %Ld\n", performance_time); +} + + +void +DVBMediaNode::HandleSeek(bigtime_t performance_time) +{ + TRACE("DVBMediaNode::HandleSeek at %Ld\n", performance_time); +} + + +void +DVBMediaNode::InitDefaultFormats() +{ + // 720 * 576 + fDefaultFormatRawVideo.type = B_MEDIA_RAW_VIDEO; + fDefaultFormatRawVideo.u.raw_video.display.format = B_RGB32; + fDefaultFormatRawVideo.u.raw_video.display.line_width = 720; + fDefaultFormatRawVideo.u.raw_video.display.line_count = 576; + fDefaultFormatRawVideo.u.raw_video.last_active = fDefaultFormatRawVideo.u.raw_video.display.line_count - 1; + fDefaultFormatRawVideo.u.raw_video.display.bytes_per_row = fDefaultFormatRawVideo.u.raw_video.display.line_width * 4; + fDefaultFormatRawVideo.u.raw_video.field_rate = 0; // wildcard + fDefaultFormatRawVideo.u.raw_video.interlace = 1; + fDefaultFormatRawVideo.u.raw_video.first_active = 0; + fDefaultFormatRawVideo.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT; + fDefaultFormatRawVideo.u.raw_video.pixel_width_aspect = 1; + fDefaultFormatRawVideo.u.raw_video.pixel_height_aspect = 1; + fDefaultFormatRawVideo.u.raw_video.display.pixel_offset = 0; + fDefaultFormatRawVideo.u.raw_video.display.line_offset = 0; + fDefaultFormatRawVideo.u.raw_video.display.flags = 0; + + fDefaultFormatRawAudio.type = B_MEDIA_RAW_AUDIO; + fDefaultFormatRawAudio.u.raw_audio.frame_rate = 48000; + fDefaultFormatRawAudio.u.raw_audio.channel_count = 2; + fDefaultFormatRawAudio.u.raw_audio.format = 0; // wildcard +// fDefaultFormatRawAudio.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT; +// when set to 0, haiku mixer has problems when diung a format change +// set to short and debug the buffer_size problem first! + fDefaultFormatRawAudio.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN; + fDefaultFormatRawAudio.u.raw_audio.buffer_size = 0; // wildcard +// fDefaultFormatRawAudio.u.raw_audio.buffer_size = 0x1200; +// fDefaultFormatRawAudio.u.raw_audio.buffer_size = 0x1000; +// fDefaultFormatRawAudio.u.raw_audio.buffer_size = 32768; +// fDefaultFormatRawAudio.u.raw_audio.buffer_size = 333 * 8; +// fDefaultFormatRawAudio.u.raw_audio.buffer_size = 512; +// when set to anything different from 32768 haiku mixer has problems + + fDefaultFormatEncVideo.type = B_MEDIA_ENCODED_VIDEO; + fDefaultFormatEncAudio.type = B_MEDIA_ENCODED_AUDIO; + fDefaultFormatTS.type = B_MEDIA_MULTISTREAM; +} + + +bool +DVBMediaNode::VerifyFormatRawVideo(const media_raw_video_format &format) +{ + if (format.display.format != 0 && + format.display.format != B_RGB32 && + format.display.format != B_YCbCr422) + return false; + + return true; +} + + +bool +DVBMediaNode::VerifyFormatRawAudio(const media_multi_audio_format &format) +{ + if (format.format != 0 && + format.format != media_raw_audio_format::B_AUDIO_FLOAT && + format.format != media_raw_audio_format::B_AUDIO_SHORT) + return false; + + return true; +} + + +void +DVBMediaNode::SpecializeFormatRawVideo(media_raw_video_format *format) +{ + // Here we need to specialize *all* remaining wildcard + // fields that the consumer didn't set + if (format->field_rate == 0.0) + format->field_rate = 25; + + // XXX a lot is missing here... +} + + +void +DVBMediaNode::SpecializeFormatRawAudio(media_multi_audio_format *format) +{ + // Here we need to specialize *all* remaining wildcard + // fields that the consumer didn't set + if (format->format == 0) + format->format = media_raw_audio_format::B_AUDIO_SHORT; + + if (format->buffer_size == 0) + format->buffer_size = 333 * 8; + + // XXX a lot is missing here... +} + + +void +DVBMediaNode::SpecializeFormatEncVideo(media_encoded_video_format *format) +{ + // Here we need to specialize *all* remaining wildcard + // fields that the consumer didn't set +} + + +void +DVBMediaNode::SpecializeFormatEncAudio(media_encoded_audio_format *format) +{ + // Here we need to specialize *all* remaining wildcard + // fields that the consumer didn't set +} + + +void +DVBMediaNode::SpecializeFormatTS(media_multistream_format *format) +{ + // Here we need to specialize *all* remaining wildcard + // fields that the consumer didn't set +} + + +status_t +DVBMediaNode::SetOutput(const media_source &source, + const media_destination &destination, + const media_format &format) +{ + switch (source.id) { + case ID_RAW_VIDEO: + fOutputRawVideo.destination = destination; + fOutputRawVideo.format = format; + break; + + case ID_RAW_AUDIO: + fOutputRawAudio.destination = destination; + fOutputRawAudio.format = format; + break; + + case ID_ENC_VIDEO: + fOutputEncVideo.destination = destination; + fOutputEncVideo.format = format; + break; + + case ID_ENC_AUDIO: + fOutputEncAudio.destination = destination; + fOutputEncAudio.format = format; + break; + + case ID_TS: + fOutputTS.destination = destination; + fOutputTS.format = format; + break; + + default: + return B_MEDIA_BAD_SOURCE; + } + return B_OK; +} + + +status_t +DVBMediaNode::ResetOutput(const media_source &source) +{ + switch (source.id) { + case ID_RAW_VIDEO: + fOutputRawVideo.destination = media_destination::null; + fOutputRawVideo.format = fDefaultFormatRawVideo; + break; + + case ID_RAW_AUDIO: + fOutputRawAudio.destination = media_destination::null; + fOutputRawAudio.format = fDefaultFormatRawAudio; + break; + + case ID_ENC_VIDEO: + fOutputEncVideo.destination = media_destination::null; + fOutputEncVideo.format = fDefaultFormatEncVideo; + break; + + case ID_ENC_AUDIO: + fOutputEncAudio.destination = media_destination::null; + fOutputEncAudio.format = fDefaultFormatEncAudio; + break; + + case ID_TS: + fOutputTS.destination = media_destination::null; + fOutputTS.format = fDefaultFormatTS; + break; + + default: + return B_MEDIA_BAD_SOURCE; + } + return B_OK; +} + + +const char * +DVBMediaNode::SourceDefaultName(const media_source &source) +{ + switch (source.id) { + case ID_RAW_VIDEO: return "raw video"; + case ID_RAW_AUDIO: return "raw audio"; + case ID_ENC_VIDEO: return "encoded video"; + case ID_ENC_AUDIO: return "encoded audio"; + case ID_TS: return "MPEG2 TS"; + default: return NULL; + } +} + + +void +DVBMediaNode::HandleStart(bigtime_t performance_time) +{ + TRACE("DVBMediaNode::HandleStart\n"); + StartCapture(); +} + + +void +DVBMediaNode::HandleStop(void) +{ + TRACE("DVBMediaNode::HandleStop\n"); + StopCapture(); +} + + +status_t +DVBMediaNode::Tune() +{ + TRACE("DVBMediaNode::Tune enter\n"); + + TRACE("state %d, region %d, channel %d, audio %d\n", + fSelectedState, fSelectedRegion, fSelectedChannel, fSelectedAudio); + + status_t err; + + if (fSelectedChannel < 0 || fSelectedAudio < 0) { + printf("DVBMediaNode::Tune: invalid tuning info\n"); + StopCapture(); + err = B_ERROR; + goto end; +// return B_ERROR; + } + + const char *desc; + + dvb_tuning_parameters_t new_params; + int new_video_pid; + int new_audio_pid; + int new_pcr_pid; + + desc = fChannelList->ItemAt(fSelectedChannel); + err = ExtractTuningParams(desc, fSelectedAudio, &new_params, &new_video_pid, &new_audio_pid, &new_pcr_pid); + + if (err != B_OK) { + printf("DVBMediaNode::Tune: getting tuning info failed\n"); + StopCapture(); + err = B_ERROR; + goto end; +// return B_ERROR; + } +/* + if (fTuningParam.frequency == new_params.frequency) { + printf("DVBMediaNode::Tune: frequency not changed\n"); + fVideoPid = new_video_pid; + fAudioPid = new_audio_pid; + fPcrPid = new_pcr_pid; + fDemux->SetPIDs(fVideoPid, fAudioPid, fPcrPid); + return B_OK; + } +*/ + bool needs_tuning; + switch (fInterfaceType) { + case DVB_TYPE_DVB_T: + needs_tuning = (fTuningParam.u.dvb_t.frequency != new_params.u.dvb_t.frequency) || !fTuningSuccess; + needs_tuning = true; + break; + case DVB_TYPE_DVB_S: + printf("needs_tuning = %d, forcing tuning for DVB-S\n", needs_tuning); + needs_tuning = true; + break; + default: + needs_tuning = true; + break; + } + + fTuningParam = new_params; + fVideoPid = new_video_pid; + fAudioPid = new_audio_pid; + fPcrPid = new_pcr_pid; + + if (needs_tuning) { +printf("about to stop capture 1\n"); +#if STOP_CAPTURE_WHILE_TUNING +printf("about to stop capture 2\n"); + err = StopCapture(); + if (err) { + printf("Tune: StopCapture failed\n"); + goto end; + } +#endif + } else { +#if STOP_CAPTURE_WHILE_TUNING + StopCaptureThreads(); +#endif + } + + if (needs_tuning) { + err = fCard->SetTuningParameter(fTuningParam); + fTuningSuccess = err == B_OK; + } + + fDemux->SetPIDs(fVideoPid, fAudioPid, fPcrPid); + + if (needs_tuning) { + if (fTuningSuccess) { + fCard->GetTuningParameter(&fTuningParam); + err = StartCapture(); + } + } else { +#if STOP_CAPTURE_WHILE_TUNING + StartCaptureThreads(); +#endif + } + +end: + EventQueue()->AddEvent(media_timed_event(0, M_REFRESH_PARAMETER_WEB)); +// RefreshParameterWeb(); + + TRACE("DVBMediaNode::Tune finished\n"); + return err; +} + + +status_t +DVBMediaNode::StartCapture() +{ + TRACE("DVBMediaNode::StartCapture\n"); + + if (fCaptureActive) + return B_OK; + + RETURN_IF_ERROR(StopCaptureThreads()); + + if (!fTuningSuccess) { + RETURN_IF_ERROR(Tune()); + } + + RETURN_IF_ERROR(StartCaptureThreads()); + + fCard->CaptureStart(); + + fCaptureActive = true; + + RefreshParameterWeb(); + + return B_OK; +} + + +status_t +DVBMediaNode::StopCapture() +{ + TRACE("DVBMediaNode::StopCapture\n"); + if (!fCaptureActive) + return B_OK; + + StopCaptureThreads(); + + fCard->CaptureStop(); + + fCaptureActive = false; + return B_OK; +} + + +status_t +DVBMediaNode::StartCaptureThreads() +{ + TRACE("DVBMediaNode::StartCaptureThreads\n"); + + if (fCaptureThreadsActive) + return B_OK; + + fTerminateThreads = false; + + fThreadIdCardReader = spawn_thread(_card_reader_thread_, "DVB card reader", 120, this); + fThreadIdMpegDemux = spawn_thread(_mpeg_demux_thread_, "DVB MPEG demux", 110, this); + fThreadIdEncAudio = spawn_thread(_enc_audio_thread_, "DVB audio streaming", 110, this); + fThreadIdEncVideo = spawn_thread(_enc_video_thread_, "DVB video streaming", 110, this); + fThreadIdMpegTS = spawn_thread(_mpeg_ts_thread_, "DVB MPEG TS streaming", 110, this); + fThreadIdRawAudio = spawn_thread(_raw_audio_thread_, "DVB audio decode", 100, this); + fThreadIdRawVideo = spawn_thread(_raw_video_thread_, "DVB video decode", 85, this); + resume_thread(fThreadIdCardReader); + resume_thread(fThreadIdMpegDemux); + resume_thread(fThreadIdEncAudio); + resume_thread(fThreadIdEncVideo); + resume_thread(fThreadIdMpegTS); + resume_thread(fThreadIdRawAudio); + resume_thread(fThreadIdRawVideo); + + fCaptureThreadsActive = true; + return B_OK; +} + + +status_t +DVBMediaNode::StopCaptureThreads() +{ + TRACE("DVBMediaNode::StopCaptureThreads\n"); + + if (!fCaptureThreadsActive) + return B_OK; + + fTerminateThreads = true; + + fCardDataQueue->Terminate(); + fEncVideoQueue->Terminate(); + fEncAudioQueue->Terminate(); + fMpegTsQueue->Terminate(); + fRawVideoQueue->Terminate(); + fRawAudioQueue->Terminate(); + + status_t dummy; // NULL as parameter does not work + wait_for_thread(fThreadIdCardReader, &dummy); + wait_for_thread(fThreadIdMpegDemux, &dummy); + wait_for_thread(fThreadIdEncAudio, &dummy); + wait_for_thread(fThreadIdEncVideo, &dummy); + wait_for_thread(fThreadIdMpegTS, &dummy); + wait_for_thread(fThreadIdRawAudio, &dummy); + wait_for_thread(fThreadIdRawVideo, &dummy); + + fCardDataQueue->Restart(); + fEncVideoQueue->Restart(); + fEncAudioQueue->Restart(); + fMpegTsQueue->Restart(); + fRawVideoQueue->Restart(); + fRawAudioQueue->Restart(); + + fCaptureThreadsActive = false; + return B_OK; +} + + +int32 +DVBMediaNode::_card_reader_thread_(void *arg) +{ + static_cast(arg)->card_reader_thread(); + return 0; +} + + +int32 +DVBMediaNode::_mpeg_demux_thread_(void *arg) +{ + static_cast(arg)->mpeg_demux_thread(); + return 0; +} + + +int32 +DVBMediaNode::_raw_audio_thread_(void *arg) +{ + static_cast(arg)->raw_audio_thread(); + return 0; +} + + +int32 +DVBMediaNode::_raw_video_thread_(void *arg) +{ + static_cast(arg)->raw_video_thread(); + return 0; +} + + +int32 +DVBMediaNode::_enc_audio_thread_(void *arg) +{ + static_cast(arg)->enc_audio_thread(); + return 0; +} + + +int32 +DVBMediaNode::_enc_video_thread_(void *arg) +{ + static_cast(arg)->enc_video_thread(); + return 0; +} + + +int32 +DVBMediaNode::_mpeg_ts_thread_(void *arg) +{ + static_cast(arg)->mpeg_ts_thread(); + return 0; +} + + +void +DVBMediaNode::card_reader_thread() +{ + while (!fTerminateThreads) { + void *data; + size_t size; + status_t err; + bigtime_t end_time; + err = fCard->Capture(&data, &size, &end_time); + if (err != B_OK) { + TRACE("fCard->Capture failed, error %lx (%s)\n", err, strerror(err)); + continue; + } + +// TRACE("captured %ld bytes\n", size); + + Packet *packet = new Packet(data, size, end_time); + + err = fCardDataQueue->Insert(packet); + if (err != B_OK) { + delete packet; + TRACE("fCardDataQueue->Insert failed, error %lx\n", err); + continue; + } + } +} + + +void +DVBMediaNode::mpeg_demux_thread() +{ + while (!fTerminateThreads) { + status_t err; + Packet *packet; + err = fCardDataQueue->Remove(&packet); + if (err != B_OK) { + TRACE("fCardDataQueue->Remove failed, error %lx\n", err); + continue; + } + + // packet->TimeStamp() is the end time of the capture + fDemux->AddData(packet); + } +} + + +void +DVBMediaNode::mpeg_ts_thread() +{ + while (!fTerminateThreads) { + status_t err; + Packet *packet; + err = fMpegTsQueue->Remove(&packet); + if (err != B_OK) { + TRACE("fMpegTsQueue->Remove failed, error %lx\n", err); + continue; + } + +// TRACE("mpeg ts packet, size %6ld, start_time %14Ld\n", packet->Size(), packet->TimeStamp()); + + delete packet; + } +} + + +void +DVBMediaNode::enc_audio_thread() +{ + while (!fTerminateThreads) { + status_t err; + Packet *packet; + err = fEncAudioQueue->Remove(&packet); + if (err != B_OK) { + TRACE("fEncAudioQueue->Remove failed, error %lx\n", err); + continue; + } +// TRACE("enc audio packet, size %6ld, start_time %14Ld\n", packet->Size(), packet->TimeStamp()); + +#ifdef DUMP_AUDIO + const uint8 *data; + size_t size; + if (B_OK != pes_extract(packet->Data(), packet->Size(), &data, &size)) { + TRACE("audio pes_extract failed\n"); + delete packet; + return; + } + lock.Lock(); + write(fAudioFile, data, size); + lock.Unlock(); +#endif + + if (!fOutputEnabledEncAudio) { + delete packet; + continue; + } + + // send encoded audio buffer + + + delete packet; + } +} + + +void +DVBMediaNode::enc_video_thread() +{ + while (!fTerminateThreads) { + status_t err; + Packet *packet; + err = fEncVideoQueue->Remove(&packet); + if (err != B_OK) { + TRACE("fEncVideoQueue->Remove failed, error %lx\n", err); + continue; + } + +// TRACE("enc video packet, size %6ld, start_time %14Ld\n", packet->Size(), packet->TimeStamp()); + + +#ifdef DUMP_VIDEO + const uint8 *data; + size_t size; + if (B_OK != pes_extract(packet->Data(), packet->Size(), &data, &size)) { + TRACE("video pes_extract failed\n"); + delete packet; + return; + } + lock.Lock(); + write(fVideoFile, data, size); + lock.Unlock(); +#endif + + if (!fOutputEnabledEncVideo) { + delete packet; + continue; + } + + // send encoded video buffer + + delete packet; + } +} + + +void +DVBMediaNode::raw_audio_thread() +{ + media_format format; + status_t err; + err = GetStreamFormat(fRawAudioQueue, &format); + if (err) { + printf("fAudioDecoder init error %s\n", strerror(err)); + return; + } + + // create decoder interface + + if (instantiate_mpeg2_audio_decoder) + fAudioDecoder = (*instantiate_mpeg2_audio_decoder)(&_GetNextAudioChunk, this, ""); + else + fAudioDecoder = new DefaultDecoder(&_GetNextAudioChunk, this); + + err = fAudioDecoder->SetInputFormat(format); + if (err) { + printf("fAudioDecoder SetInputFormat error %s\n", strerror(err)); + return; + } + + TRACE("requested audio decoder format: "); + PrintFormat(fOutputRawAudio); + + media_format fmt = fOutputRawAudio.format; + err = fAudioDecoder->SetOutputFormat(&fmt); + if (err) { + printf("fAudioDecoder SetOutputFormat error %s\n", strerror(err)); + return; + } + + TRACE("final audio decoder format: "); + PrintFormat(fmt); + + // change format of connection + if (format_is_compatible(fmt, fOutputRawAudio.format)) { + printf("audio formats are compatible\n"); + fOutputRawAudio.format = fmt; + } else { + printf("audio formats NOT compatible\n"); + lock.Lock(); + err = ChangeFormat(fOutputRawAudio.source, + fOutputRawAudio.destination, + &fmt); + lock.Unlock(); + printf("format change result %lx (%s)\n", err, strerror(err)); + PrintFormat(fmt); + fOutputRawAudio.format = fmt; + if (err) + return; + } + + // decode data and send buffers + + delete fBufferGroupRawAudio; + fBufferGroupRawAudio = new BBufferGroup(fOutputRawAudio.format.u.raw_audio.buffer_size * 3, 25); + + while (!fTerminateThreads) { + int64 frameCount; + media_header mh; + media_header_ex *mhe = (media_header_ex *)&mh; + + if (!fOutputEnabledRawAudio) { + fRawAudioQueue->Flush(40000); + continue; + } + + BBuffer* buf; + buf = fBufferGroupRawAudio->RequestBuffer(fOutputRawAudio.format.u.raw_audio.buffer_size, AUDIO_BUFFER_REQUEST_TIMEOUT); + if (!buf) { + TRACE("audio: request buffer timout\n"); + continue; + } + + err = fAudioDecoder->Decode(buf->Data(), &frameCount, &mh, NULL); + if (err) { + buf->Recycle(); + printf("fAudioDecoder Decode error %s\n", strerror(err)); + continue; + } + +#ifdef DUMP_RAW_AUDIO + lock.Lock(); + write(fRawAudioFile, buf->Data(), mhe->size_used); + lock.Unlock(); +#endif + + if ( fOutputRawAudio.format.u.raw_audio.buffer_size != mhe->size_used + || int(fOutputRawAudio.format.u.raw_audio.frame_rate) != mhe->u.raw_audio.frame_rate + || fOutputRawAudio.format.u.raw_audio.channel_count != mhe->u.raw_audio.channel_count + ) { + TRACE("audio: decode format change: changed buffer_size from %ld to %ld\n", fOutputRawAudio.format.u.raw_audio.buffer_size, mhe->size_used); + TRACE("audio: decode format change: changed channel_count from %ld to %ld\n", fOutputRawAudio.format.u.raw_audio.channel_count, mhe->u.raw_audio.channel_count); + TRACE("audio: decode format change: changed frame_rate from %.0f to %.0f\n", fOutputRawAudio.format.u.raw_audio.frame_rate, mhe->u.raw_audio.frame_rate); + fOutputRawAudio.format.u.raw_audio.buffer_size = mhe->size_used; + fOutputRawAudio.format.u.raw_audio.frame_rate = mhe->u.raw_audio.frame_rate; + fOutputRawAudio.format.u.raw_audio.channel_count = mhe->u.raw_audio.channel_count; + lock.Lock(); + err = ChangeFormat(fOutputRawAudio.source, + fOutputRawAudio.destination, + &fOutputRawAudio.format); + lock.Unlock(); + printf("format change result %lx (%s)\n", err, strerror(err)); + PrintFormat(fOutputRawAudio.format); + if (err) { + buf->Recycle(); + return; // we are dead! + } + } + + bigtime_t ts_perf_time; + bigtime_t ts_sys_time; + bigtime_t ts_offset; + bigtime_t aud_time; + bigtime_t start_time; + + // calculate start time of audio data + + fDemux->TimesourceInfo(&ts_perf_time, &ts_sys_time); + ts_offset = ts_sys_time - ts_perf_time; + aud_time = mhe->start_time; // measured in PCR time base + start_time = TimeSource()->PerformanceTimeFor(aud_time + ts_offset); + + // calculate delay and wait + + bigtime_t delay; + delay = start_time - TimeSource()->Now(); + TRACE_TIMING("audio delay is %Ld\n", delay); + if (delay < -AUDIO_MAX_LATE) { + printf("audio: decoded packet is %Ldms too late, dropped\n", -delay / 1000); + buf->Recycle(); + continue; + } + if (delay < 0) { +// printf("audio: decoded packet is %Ldms too late\n", -delay / 1000); + } + if (delay > AUDIO_MAX_EARLY) { + printf("audio: decoded packet is %Ldms too early, dropped\n", delay / 1000); + buf->Recycle(); + continue; + } + if (delay > 0) { +// printf("audio: decoded packet is %Ldms too early\n", delay / 1000); + } + delay -= PROCESSING_LATENCY; + if (delay > 0) { + if (acquire_sem_etc(fAudioDelaySem, 1, B_RELATIVE_TIMEOUT, delay) != B_TIMED_OUT) { + printf("audio: delay sem not timed out, dropped packet\n"); + buf->Recycle(); + continue; + } + } + + TRACE_TIMING("audio playback delay %Ld\n", start_time - TimeSource()->Now()); + + media_header* hdr; + hdr = buf->Header(); +// *hdr = mh; // copy header from decoded frame + hdr->type = B_MEDIA_RAW_AUDIO; + hdr->size_used = mhe->size_used; + hdr->time_source = TimeSource()->ID(); // set time source id + hdr->start_time = start_time; // set start time + lock.Lock(); + if (B_OK != SendBuffer(buf, fOutputRawAudio.destination)) { + TRACE("audio: sending buffer failed\n"); + buf->Recycle(); + } + lock.Unlock(); + } + + delete fCurrentAudioPacket; + fCurrentAudioPacket = 0; +} + + +void +DVBMediaNode::raw_video_thread() +{ + media_format format; + status_t err; + err = GetStreamFormat(fRawVideoQueue, &format); + if (err) { + printf("fVideoDecoder init error %s\n", strerror(err)); + return; + } + + // create decoder interface + + if (instantiate_mpeg2_video_decoder) + fVideoDecoder = (*instantiate_mpeg2_video_decoder)(&_GetNextVideoChunk, this, ""); + else + fVideoDecoder = new DefaultDecoder(&_GetNextVideoChunk, this); + + err = fVideoDecoder->SetInputFormat(format); + if (err) { + printf("fVideoDecoder SetInputFormat error %s\n", strerror(err)); + return; + } + + TRACE("requested video decoder format: "); + PrintFormat(fOutputRawVideo); + + media_format fmt = fOutputRawVideo.format; + err = fVideoDecoder->SetOutputFormat(&fmt); + if (err) { + printf("fVideoDecoder SetOutputFormat error %s\n", strerror(err)); + return; + } + + TRACE("final video decoder format: "); + PrintFormat(fmt); + + // change format of connection + if (format_is_compatible(fmt, fOutputRawVideo.format)) { + printf("video formats are compatible\n"); + fOutputRawVideo.format = fmt; + } else { + printf("video formats NOT compatible\n"); + lock.Lock(); + err = ChangeFormat(fOutputRawVideo.source, + fOutputRawVideo.destination, + &fmt); + lock.Unlock(); + printf("format change result %lx (%s)\n", err, strerror(err)); + PrintFormat(fmt); + fOutputRawVideo.format = fmt; + if (err) { + printf("video format change failed\n"); + return; + } + } + + // decode data and send buffers + + uint32 video_buffer_size_max = 720 * 576 * 4; + uint32 video_buffer_size = fOutputRawVideo.format.u.raw_video.display.line_count * fOutputRawVideo.format.u.raw_video.display.bytes_per_row; + + delete fBufferGroupRawVideo; + fBufferGroupRawVideo = new BBufferGroup(video_buffer_size_max, 4); + + while (!fTerminateThreads) { + int64 frameCount; + media_header mh; + media_header_ex *mhe = (media_header_ex *)&mh; + + if (!fOutputEnabledRawVideo) { + fRawVideoQueue->Flush(40000); + continue; + } + + // fetch a new buffer (always of maximum size, as the stream may change) + + BBuffer* buf; + buf = fBufferGroupRawVideo->RequestBuffer(video_buffer_size_max, VIDEO_BUFFER_REQUEST_TIMEOUT); + if (!buf) { + TRACE("video: request buffer timout\n"); + continue; + } + + // decode one video frame into buffer + + err = fVideoDecoder->Decode(buf->Data(), &frameCount, &mh, NULL); + if (err) { + buf->Recycle(); + printf("fVideoDecoder Decode error %s\n", strerror(err)); + continue; + } + + // check if the format of the stream has changed + + if ( mhe->u.raw_video.display_line_width != fOutputRawVideo.format.u.raw_video.display.line_width + || mhe->u.raw_video.display_line_count != fOutputRawVideo.format.u.raw_video.display.line_count + || mhe->u.raw_video.bytes_per_row != fOutputRawVideo.format.u.raw_video.display.bytes_per_row + || mhe->u.raw_video.pixel_width_aspect != fOutputRawVideo.format.u.raw_video.pixel_width_aspect + || mhe->u.raw_video.pixel_height_aspect != fOutputRawVideo.format.u.raw_video.pixel_height_aspect + || mhe->size_used != video_buffer_size) + { + printf("video format changed:\n"); + printf(" line_width %ld => %ld\n", fOutputRawVideo.format.u.raw_video.display.line_width, mhe->u.raw_video.display_line_width); + printf(" line_count %ld => %ld\n", fOutputRawVideo.format.u.raw_video.display.line_count, mhe->u.raw_video.display_line_count); + printf(" bytes_per_row %ld => %ld\n", fOutputRawVideo.format.u.raw_video.display.bytes_per_row, mhe->u.raw_video.bytes_per_row); + printf(" pixel_width_aspect %d => %d\n", fOutputRawVideo.format.u.raw_video.pixel_width_aspect, mhe->u.raw_video.pixel_width_aspect); + printf(" pixel_height_aspect %d => %d\n", fOutputRawVideo.format.u.raw_video.pixel_width_aspect, mhe->u.raw_video.pixel_height_aspect); + printf(" pixel_height_aspect %d => %d\n", fOutputRawVideo.format.u.raw_video.pixel_width_aspect, mhe->u.raw_video.pixel_height_aspect); + printf(" video_buffer_size %ld => %ld\n", video_buffer_size, mhe->size_used); + + // recalculate video buffer size +// video_buffer_size = mhe->size_used; + video_buffer_size = fOutputRawVideo.format.u.raw_video.display.line_count * fOutputRawVideo.format.u.raw_video.display.bytes_per_row; + + // perform a video format change + + fOutputRawVideo.format.u.raw_video.display.line_width = mhe->u.raw_video.display_line_width; + fOutputRawVideo.format.u.raw_video.display.line_count = mhe->u.raw_video.display_line_count; + fOutputRawVideo.format.u.raw_video.display.bytes_per_row = mhe->u.raw_video.bytes_per_row; + fOutputRawVideo.format.u.raw_video.pixel_width_aspect = mhe->u.raw_video.pixel_width_aspect; + fOutputRawVideo.format.u.raw_video.pixel_width_aspect = mhe->u.raw_video.pixel_height_aspect; + fOutputRawVideo.format.u.raw_video.last_active = mhe->u.raw_video.display_line_count - 1; + lock.Lock(); + err = ChangeFormat(fOutputRawVideo.source, + fOutputRawVideo.destination, + &fOutputRawVideo.format); + lock.Unlock(); + printf("format change result %lx (%s)\n", err, strerror(err)); + PrintFormat(fOutputRawVideo.format); + if (err) { + buf->Recycle(); + printf("video format change failed\n"); + return; // we are dead + } + } + + // calculate start time for video + + bigtime_t ts_perf_time; + bigtime_t ts_sys_time; + bigtime_t ts_offset; + bigtime_t pic_time; + bigtime_t start_time; + + fDemux->TimesourceInfo(&ts_perf_time, &ts_sys_time); + ts_offset = ts_sys_time - ts_perf_time; + pic_time = mhe->start_time; // measured in PCR time base + start_time = TimeSource()->PerformanceTimeFor(pic_time + ts_offset); + + // calculate delay and wait + + bigtime_t delay; + delay = start_time - TimeSource()->Now(); + TRACE_TIMING("video delay %Ld\n", delay); + if (delay < -VIDEO_MAX_LATE) { + printf("video: decoded packet is %Ldms too late, dropped\n", -delay / 1000); + buf->Recycle(); + continue; + } + if (delay > VIDEO_MAX_EARLY) { + printf("video: decoded packet is %Ldms too early, dropped\n", delay / 1000); + buf->Recycle(); + continue; + } + delay -= PROCESSING_LATENCY; + if (delay > 0) { + if (acquire_sem_etc(fVideoDelaySem, 1, B_RELATIVE_TIMEOUT, delay) != B_TIMED_OUT) { + printf("video: delay sem not timed out, dropped packet\n"); + buf->Recycle(); + continue; + } + } + + TRACE_TIMING("video playback delay %Ld\n", start_time - TimeSource()->Now()); + + media_header* hdr; + hdr = buf->Header(); +// *hdr = mh; // copy header from decoded frame + hdr->type = B_MEDIA_RAW_VIDEO; + hdr->size_used = video_buffer_size; + hdr->time_source = TimeSource()->ID(); // set time source id + hdr->start_time = start_time; // set start time + lock.Lock(); + if (B_OK != SendBuffer(buf, fOutputRawVideo.destination)) { + TRACE("video: sending buffer failed\n"); + buf->Recycle(); + } + lock.Unlock(); + + } + + delete fCurrentVideoPacket; + fCurrentVideoPacket = 0; +} + + +inline status_t +DVBMediaNode::GetNextVideoChunk(const void **chunkData, size_t *chunkLen, media_header *mh) +{ +// TRACE("DVBMediaNode::GetNextVideoChunk\n"); + + delete fCurrentVideoPacket; + + status_t err; + err = fRawVideoQueue->Remove(&fCurrentVideoPacket); + if (err != B_OK) { + TRACE("fRawVideoQueue->Remove failed, error %lx\n", err); + fCurrentVideoPacket = 0; + return B_ERROR; + } + + const uint8 *data; + size_t size; + + if (B_OK != pes_extract(fCurrentVideoPacket->Data(), fCurrentVideoPacket->Size(), &data, &size)) { + TRACE("video pes_extract failed\n"); + return B_ERROR; + } + + *chunkData = data; + *chunkLen = size; + // measured in PCR time base + mh->start_time = fCurrentVideoPacket->TimeStamp(); + + return B_OK; +} + + +inline status_t +DVBMediaNode::GetNextAudioChunk(const void **chunkData, size_t *chunkLen, media_header *mh) +{ +// TRACE("DVBMediaNode::GetNextAudioChunk\n"); + + delete fCurrentAudioPacket; + + status_t err; + err = fRawAudioQueue->Remove(&fCurrentAudioPacket); + if (err != B_OK) { + TRACE("fRawAudioQueue->Remove failed, error %lx\n", err); + fCurrentAudioPacket = 0; + return B_ERROR; + } + + const uint8 *data; + size_t size; + + if (B_OK != pes_extract(fCurrentAudioPacket->Data(), fCurrentAudioPacket->Size(), &data, &size)) { + TRACE("audio pes_extract failed\n"); + return B_ERROR; + } + + *chunkData = data; + *chunkLen = size; + // measured in PCR time base + mh->start_time = fCurrentAudioPacket->TimeStamp(); + +// printf("GetNextAudioChunk: done start_time %Ld\n", mh->start_time); + + return B_OK; +} + + +status_t +DVBMediaNode::_GetNextVideoChunk(const void **chunkData, size_t *chunkLen, media_header *mh, void *cookie) +{ + return static_cast(cookie)->GetNextVideoChunk(chunkData, chunkLen, mh); +} + + +status_t +DVBMediaNode::_GetNextAudioChunk(const void **chunkData, size_t *chunkLen, media_header *mh, void *cookie) +{ + return static_cast(cookie)->GetNextAudioChunk(chunkData, chunkLen, mh); +} + + +status_t +DVBMediaNode::GetStreamFormat(PacketQueue *queue, media_format *format) +{ + status_t err; + Packet *packet; + const uint8 *data; + size_t size; + int stream_id; + + // get copy of the first packet from queue, and determine format + + if ((err = queue->Peek(&packet)) != B_OK) { + TRACE("queue->Peek failed, error %lx\n", err); + return err; + } + + if ((err = pes_extract(packet->Data(), packet->Size(), &data, &size)) != B_OK) { + TRACE("pes_extract failed\n"); + return err; + } + + if ((err = pes_stream_id(packet->Data(), packet->Size(), &stream_id)) != B_OK) { + TRACE("pes_stream_id failed\n"); + return err; + } + + if ((err = GetHeaderFormat(format, data, size, stream_id)) != B_OK) { + TRACE("GetHeaderFormat failed, error %lx\n", err); + return err; + } + + delete packet; + + return B_OK; +} + + +enum { + ID_STATE = 11, + ID_REGION = 12, + ID_CHANNEL = 13, + ID_AUDIO = 14, +}; + + +void +DVBMediaNode::RefreshParameterWeb() +{ + TRACE("DVBMediaNode::RefreshParameterWeb enter\n"); + fWeb = CreateParameterWeb(); + SetParameterWeb(fWeb); + TRACE("DVBMediaNode::RefreshParameterWeb finished\n"); +} + + +void +DVBMediaNode::SetAboutInfo(BParameterGroup *about) +{ + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "DVB media_addon info:", B_GENERIC); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Version " VERSION, B_GENERIC); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Revision " REVISION, B_GENERIC); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Build " BUILD, B_GENERIC); + + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "", B_GENERIC); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Driver info:", B_GENERIC); + + dvb_type_t type; + char name[200]; + char info[200]; + + fCard->GetCardType(&type); + fCard->GetCardInfo(name, sizeof(name), info, sizeof(info)); + + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, name, B_GENERIC); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, info, B_GENERIC); +} + + +BParameterWeb * +DVBMediaNode::CreateParameterWeb() +{ + /* Set up the parameter web */ + BParameterWeb *web = new BParameterWeb(); + + char n[200], i[200]; + fCard->GetCardInfo(n, sizeof(n), i, sizeof(i)); + + BString name; + name << Name() << " - " << i; + + BParameterGroup *main = web->MakeGroup(name.String()); + + BParameterGroup *ctrl = main->MakeGroup("Channel Selection"); + ctrl->MakeNullParameter(0, B_MEDIA_NO_TYPE, ctrl->Name(), B_GENERIC); + + BParameterGroup *pref = main->MakeGroup("Preferences"); + pref->MakeNullParameter(0, B_MEDIA_NO_TYPE, pref->Name(), B_GENERIC); + + BDiscreteParameter *state = pref->MakeDiscreteParameter( + ID_STATE, B_MEDIA_RAW_VIDEO, "State", B_GENERIC); + + BDiscreteParameter *region = pref->MakeDiscreteParameter( + ID_REGION, B_MEDIA_RAW_VIDEO, "Region", B_GENERIC); + + BDiscreteParameter *chan = ctrl->MakeDiscreteParameter( + ID_CHANNEL, B_MEDIA_RAW_VIDEO, "Channel", /* B_TUNER_CHANNEL */ B_GENERIC); + + BDiscreteParameter *aud = ctrl->MakeDiscreteParameter( + ID_AUDIO, B_MEDIA_RAW_VIDEO, "Audio", B_GENERIC); + + AddStateItems(state); + AddRegionItems(region); + AddChannelItems(chan); + AddAudioItems(aud); + + + if (!fTuningSuccess || !fCaptureActive) { + BParameterGroup *info = main->MakeGroup("Info"); + info->MakeNullParameter(0, B_MEDIA_NO_TYPE, info->Name(), B_GENERIC); + BParameterGroup *about = main->MakeGroup("About"); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, about->Name(), B_GENERIC); + SetAboutInfo(about); +// info->MakeNullParameter(0, B_MEDIA_NO_TYPE, fCaptureActive ? "Tuning failed" : "Node stopped", B_GENERIC); + info->MakeNullParameter(0, B_MEDIA_NO_TYPE, "Node is stopped", B_GENERIC); + info->MakeNullParameter(0, B_MEDIA_NO_TYPE, "or tuning failed.", B_GENERIC); + return web; + } + + BParameterGroup *info1 = main->MakeGroup("Info"); + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, info1->Name(), B_GENERIC); + BParameterGroup *info2 = main->MakeGroup("Info"); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, info2->Name(), B_GENERIC); + BParameterGroup *about = main->MakeGroup("About"); + about->MakeNullParameter(0, B_MEDIA_NO_TYPE, about->Name(), B_GENERIC); + SetAboutInfo(about); + + BString sInterfaceType = "Interface Type: "; + BString sFrequency = "Frequency: "; + BString sAudioPid = "Audio PID: "; + BString sVideoPid = "Video PID: "; + BString sPcrPid = "PCR PID: "; + BString sInversion = "Inversion: "; + BString sBandwidth = "Bandwith: "; + BString sModulation = "Modulation: "; + BString sHierarchy = "Hierarchy: "; + BString sCodeRateHP = "Code Rate HP: "; + BString sCodeRateLP = "Code Rate LP: "; + BString sTransmissionMode = "Transmission Mode: "; + BString sGuardInterval = "Guard Interval: "; + + BString sSymbolRate = "Symbol Rate: "; + BString sPolarity = "Polarity: "; + BString sAgcInversion = "AGC Inversion: "; + + switch (fInterfaceType) { + case DVB_TYPE_DVB_C: sInterfaceType << "DVB-C"; break; + case DVB_TYPE_DVB_H: sInterfaceType << "DVB-H"; break; + case DVB_TYPE_DVB_S: sInterfaceType << "DVB-S"; break; + case DVB_TYPE_DVB_T: sInterfaceType << "DVB-T"; break; + default: sInterfaceType << "unknown"; break; + } + + sAudioPid << fAudioPid; + sVideoPid << fVideoPid; + sPcrPid << fPcrPid; + + if (fInterfaceType == DVB_TYPE_DVB_T) { + + sFrequency << fTuningParam.u.dvb_t.frequency / 1000000 << " MHz"; + + switch (fTuningParam.u.dvb_t.inversion) { + case DVB_INVERSION_AUTO: sInversion << "auto"; break; + case DVB_INVERSION_ON: sInversion << "on"; break; + case DVB_INVERSION_OFF: sInversion << "off"; break; + default: sInversion << "unknown"; break; + } + + switch (fTuningParam.u.dvb_t.bandwidth) { + case DVB_BANDWIDTH_AUTO: sBandwidth << "auto"; break; + case DVB_BANDWIDTH_6_MHZ: sBandwidth << "6 MHz"; break; + case DVB_BANDWIDTH_7_MHZ: sBandwidth << "7 MHz"; break; + case DVB_BANDWIDTH_8_MHZ: sBandwidth << "8 MHz"; break; + default: sBandwidth << "unknown"; break; + } + + switch (fTuningParam.u.dvb_t.modulation) { + case DVB_MODULATION_AUTO: sModulation << "auto"; break; + case DVB_MODULATION_QPSK: sModulation << "QPSK"; break; + case DVB_MODULATION_16_QAM: sModulation << "16 QAM"; break; + case DVB_MODULATION_32_QAM: sModulation << "32 QAM"; break; + case DVB_MODULATION_64_QAM: sModulation << "64 QAM"; break; + case DVB_MODULATION_128_QAM: sModulation << "128 QAM"; break; + case DVB_MODULATION_256_QAM: sModulation << "256 QAM"; break; + default: sModulation << "unknown"; break; + } + + switch (fTuningParam.u.dvb_t.hierarchy) { + case DVB_HIERARCHY_AUTO: sHierarchy << "auto"; break; + case DVB_HIERARCHY_NONE: sHierarchy << "none"; break; + case DVB_HIERARCHY_1: sHierarchy << "1"; break; + case DVB_HIERARCHY_2: sHierarchy << "2"; break; + case DVB_HIERARCHY_4: sHierarchy << "4"; break; + default: sHierarchy << "unknown"; break; + } + + switch (fTuningParam.u.dvb_t.code_rate_hp) { + case DVB_FEC_AUTO: sCodeRateHP << "auto"; break; + case DVB_FEC_NONE: sCodeRateHP << "none"; break; + case DVB_FEC_1_2: sCodeRateHP << "FEC 1/2"; break; + case DVB_FEC_2_3: sCodeRateHP << "FEC 2/3"; break; + case DVB_FEC_3_4: sCodeRateHP << "FEC 3/4"; break; + case DVB_FEC_4_5: sCodeRateHP << "FEC 4/5"; break; + case DVB_FEC_5_6: sCodeRateHP << "FEC 5/6"; break; + case DVB_FEC_6_7: sCodeRateHP << "FEC 6/7"; break; + case DVB_FEC_7_8: sCodeRateHP << "FEC 7/8"; break; + case DVB_FEC_8_9: sCodeRateHP << "FEC 8/9"; break; + default: sCodeRateHP << "unknown"; break; + } + + switch (fTuningParam.u.dvb_t.code_rate_lp) { + case DVB_FEC_AUTO: sCodeRateLP << "auto"; break; + case DVB_FEC_NONE: sCodeRateLP << "none"; break; + case DVB_FEC_1_2: sCodeRateLP << "FEC 1/2"; break; + case DVB_FEC_2_3: sCodeRateLP << "FEC 2/3"; break; + case DVB_FEC_3_4: sCodeRateLP << "FEC 3/4"; break; + case DVB_FEC_4_5: sCodeRateLP << "FEC 4/5"; break; + case DVB_FEC_5_6: sCodeRateLP << "FEC 5/6"; break; + case DVB_FEC_6_7: sCodeRateLP << "FEC 6/7"; break; + case DVB_FEC_7_8: sCodeRateLP << "FEC 7/8"; break; + case DVB_FEC_8_9: sCodeRateLP << "FEC 8/9"; break; + default: sCodeRateLP << "unknown"; break; + } + + switch (fTuningParam.u.dvb_t.transmission_mode) { + case DVB_TRANSMISSION_MODE_AUTO: sTransmissionMode << "auto"; break; + case DVB_TRANSMISSION_MODE_2K: sTransmissionMode << "2K"; break; + case DVB_TRANSMISSION_MODE_4K: sTransmissionMode << "4K"; break; + case DVB_TRANSMISSION_MODE_8K: sTransmissionMode << "8K"; break; + default: sTransmissionMode << "unknown"; break; + } + + switch (fTuningParam.u.dvb_t.guard_interval) { + case DVB_GUARD_INTERVAL_AUTO: sGuardInterval << "auto"; break; + case DVB_GUARD_INTERVAL_1_4: sGuardInterval << "1/4"; break; + case DVB_GUARD_INTERVAL_1_8: sGuardInterval << "1/8"; break; + case DVB_GUARD_INTERVAL_1_16: sGuardInterval << "1/16"; break; + case DVB_GUARD_INTERVAL_1_32: sGuardInterval << "1/32"; break; + default: sGuardInterval << "unknown"; break; + } + + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, sInterfaceType.String(), B_GENERIC); + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, sFrequency.String(), B_GENERIC); + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, sBandwidth.String(), B_GENERIC); + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, sVideoPid.String(), B_GENERIC); + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, sAudioPid.String(), B_GENERIC); + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, sPcrPid.String(), B_GENERIC); + + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sModulation.String(), B_GENERIC); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sTransmissionMode.String(), B_GENERIC); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sGuardInterval.String(), B_GENERIC); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sCodeRateHP.String(), B_GENERIC); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sCodeRateLP.String(), B_GENERIC); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sInversion.String(), B_GENERIC); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sHierarchy.String(), B_GENERIC); + } + + if (fInterfaceType == DVB_TYPE_DVB_S) { + + sFrequency << fTuningParam.u.dvb_s.frequency / 1000000 << " MHz"; + sSymbolRate << fTuningParam.u.dvb_s.symbolrate; + + switch (fTuningParam.u.dvb_s.inversion) { + case DVB_INVERSION_AUTO: sInversion << "auto"; break; + case DVB_INVERSION_ON: sInversion << "on"; break; + case DVB_INVERSION_OFF: sInversion << "off"; break; + default: sInversion << "unknown"; break; + } + + switch (fTuningParam.u.dvb_s.polarity) { + case DVB_POLARITY_VERTICAL: sPolarity << "vertical"; break; + case DVB_POLARITY_HORIZONTAL: sPolarity << "horizontal"; break; + default: sPolarity << "unknown"; break; + } + + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, sInterfaceType.String(), B_GENERIC); + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, sVideoPid.String(), B_GENERIC); + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, sAudioPid.String(), B_GENERIC); + info1->MakeNullParameter(0, B_MEDIA_NO_TYPE, sPcrPid.String(), B_GENERIC); + + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sFrequency.String(), B_GENERIC); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sPolarity.String(), B_GENERIC); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sSymbolRate.String(), B_GENERIC); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sInversion.String(), B_GENERIC); + info2->MakeNullParameter(0, B_MEDIA_NO_TYPE, sAgcInversion.String(), B_GENERIC); + } + + return web; +} + + +void +DVBMediaNode::LoadSettings() +{ + TRACE("DVBMediaNode::LoadSettings\n"); + RefreshStateList(); + fSelectedState = 0; + RefreshRegionList(); + fSelectedRegion = 0; + RefreshChannelList(); + fSelectedChannel = 0; + RefreshAudioList(); + fSelectedAudio = 0; +} + + +void +DVBMediaNode::RefreshStateList() +{ + TRACE("DVBMediaNode::RefreshStateList\n"); + + fStateList->MakeEmpty(); + fSelectedState = -1; + + const char *dir; + switch (fInterfaceType) { + case DVB_TYPE_DVB_C: dir = "/boot/home/config/settings/dvb/dvb-c channels"; break; + case DVB_TYPE_DVB_H: dir = "/boot/home/config/settings/dvb/dvb-h channels"; break; + case DVB_TYPE_DVB_S: dir = "/boot/home/config/settings/dvb/dvb-s channels"; break; + case DVB_TYPE_DVB_T: dir = "/boot/home/config/settings/dvb/dvb-t channels"; break; + default: + printf("DVBMediaNode::RefreshStateList unknown interface type\n"); + return; + } + + TRACE("loading channel lists from dir = %s\n", dir); + + BDirectory d(dir); + BEntry e; + BPath p; + while (B_OK == d.GetNextEntry(&e, false)) { + if (B_OK != e.GetPath(&p)) + continue; + fStateList->AddItem(p.Path()); + } + + if (fStateList->ItemAt(0)) + fSelectedState = 0; +} + + +void +DVBMediaNode::RefreshRegionList() +{ + TRACE("DVBMediaNode::RefreshRegionList\n"); + + fRegionList->MakeEmpty(); + fSelectedRegion = -1; + + const char *dir = fStateList->ItemAt(fSelectedState); + if (!dir) + return; + + BDirectory d(dir); + BEntry e; + BPath p; + while (B_OK == d.GetNextEntry(&e, false)) { + if (B_OK != e.GetPath(&p)) + continue; + fRegionList->AddItem(p.Path()); + } + + if (fRegionList->ItemAt(0)) + fSelectedRegion = 0; +} + + +void +DVBMediaNode::RefreshChannelList() +{ + TRACE("DVBMediaNode::RefreshChannelList\n"); + + fChannelList->MakeEmpty(); + fSelectedChannel = -1; + + const char *path = fRegionList->ItemAt(fSelectedRegion); + if (!path) + return; + + TRACE("opening channel list file = %s\n", path); + + FILE *f = fopen(path, "r"); + if (!f) + return; + + char line[1024]; + while (fgets(line, sizeof(line), f)) { + if (line[0] == ':') // skip comments + continue; + if (strchr(line, ':') == NULL) // skip empty lines + continue; + fChannelList->AddItem(line); + } + + fclose(f); + + if (fChannelList->ItemAt(0)) + fSelectedChannel = 0; +} + + +void +DVBMediaNode::RefreshAudioList() +{ + TRACE("DVBMediaNode::RefreshAudioList\n"); + + fAudioList->MakeEmpty(); + fSelectedAudio = -1; + + fAudioList->AddItem("default"); // XXX test + + if (fAudioList->ItemAt(0)) + fSelectedAudio = 0; +} + + +void +DVBMediaNode::AddStateItems(BDiscreteParameter *param) +{ + TRACE("DVBMediaNode::AddStateItems\n"); + + const char *str; + for (int i = 0; (str = fStateList->ItemAt(i)); i++) { + str = strrchr(str, '/'); + if (!str) + continue; + str++; + param->AddItem(i, str); + } + if (param->CountItems() == 0) + param->AddItem(-1, "none"); +} + + +void +DVBMediaNode::AddRegionItems(BDiscreteParameter *param) +{ + TRACE("DVBMediaNode::AddRegionItems\n"); + + const char *str; + for (int i = 0; (str = fRegionList->ItemAt(i)); i++) { + str = strrchr(str, '/'); + if (!str) + continue; + str++; + param->AddItem(i, str); + } + if (param->CountItems() == 0) + param->AddItem(-1, "none"); +} + + +void +DVBMediaNode::AddChannelItems(BDiscreteParameter *param) +{ + TRACE("DVBMediaNode::AddChannelItems\n"); + + const char *str; + for (int i = 0; (str = fChannelList->ItemAt(i)); i++) { + char name[256]; +// sscanf(str, "%s:", name); + sscanf(str, "%[^:]", name); + param->AddItem(i, name); + + } + if (param->CountItems() == 0) + param->AddItem(-1, "none"); +} + + +void +DVBMediaNode::AddAudioItems(BDiscreteParameter *param) +{ + TRACE("DVBMediaNode::AddAudioItems\n"); + + if (param->CountItems() == 0) + param->AddItem(-1, "default"); +} + + +status_t +DVBMediaNode::GetParameterValue(int32 id, bigtime_t *last_change, void *value, size_t *size) +{ +// TRACE("DVBMediaNode::GetParameterValue, id 0x%lx\n", id); + + switch (id) { + case ID_STATE: *size = 4; *(int32 *)value = fSelectedState; break; + case ID_REGION: *size = 4; *(int32 *)value = fSelectedRegion; break; + case ID_CHANNEL: *size = 4; *(int32 *)value = fSelectedChannel; break; + case ID_AUDIO: *size = 4; *(int32 *)value = fSelectedAudio; break; + default: return B_ERROR; + } + return B_OK; +} + + +void +DVBMediaNode::SetParameterValue(int32 id, bigtime_t when, const void *value, size_t size) +{ + TRACE("DVBMediaNode::SetParameterValue, id 0x%lx, size %ld, value 0x%lx\n", id, size, *(const int32 *)value); + + switch (id) { + case ID_STATE: + fSelectedState = *(const int32 *)value; + StopCapture(); + RefreshRegionList(); + RefreshChannelList(); + RefreshAudioList(); + EventQueue()->AddEvent(media_timed_event(0, M_REFRESH_PARAMETER_WEB)); +// Tune(); + break; + + case ID_REGION: + fSelectedRegion = *(const int32 *)value; + StopCapture(); + RefreshChannelList(); + RefreshAudioList(); + EventQueue()->AddEvent(media_timed_event(0, M_REFRESH_PARAMETER_WEB)); +// Tune(); + break; + + case ID_CHANNEL: + fSelectedChannel = *(const int32 *)value; + RefreshAudioList(); +// EventQueue()->AddEvent(media_timed_event(0, M_REFRESH_PARAMETER_WEB)); + Tune(); + break; + + case ID_AUDIO: + fSelectedAudio = *(const int32 *)value; + Tune(); + break; + + default: + break; + } + TRACE("DVBMediaNode::SetParameterValue finished\n"); +} + + +status_t +DVBMediaNode::ExtractTuningParams(const char *description, int audio_pid_index, + dvb_tuning_parameters_t *tuning_param, + int *video_pid, int *audio_pid, int *pcr_pid) +{ + if (!description) + return B_ERROR; + + printf("ExtractTuningParams: \"%s\"\n", description); + + char name[50]; + char freq[50]; + char para[100]; + char src[50]; + char srate[50]; + char vpid[50]; + char apid[50]; + char tpid[50]; + char ca[50]; + char sid[50]; + char nid[50]; + char tid[50]; + char rid[50]; + + sscanf(description, " %[^:] : %[^:] : %[^:] : %[^:] : %[^:] : %[^:] : %[^:] : %[^:] : %[^:] : %[^:] : %[^:] : %[^:] : %[^:] ", + name, freq, para, src, srate, vpid, apid, tpid, ca, sid, nid, tid, rid); + + char *cpid = strchr(vpid, '+'); + if (cpid) cpid++; + + int _vpid = strtol(vpid, 0, 0); + int _apid = strtol(apid, 0, 0); +// int _tpid = strtol(tpid, 0, 0); + int _cpid = cpid ? strtol(cpid, 0, 0) : _vpid; + int _srate = strtol(srate, 0, 0); + int64 _freq = strtol(freq, 0, 0); + while (_freq && _freq <= 1000000) + _freq *= 1000; + + if (fInterfaceType == DVB_TYPE_DVB_S && _freq < 950000000) { + TRACE("workaround activated: type is DVB_S and frequency < 950 MHz, multiplying by 1000!\n"); + _freq *= 1000; + } + + + *video_pid = _vpid; + *audio_pid = _apid; + *pcr_pid = _cpid; + + TRACE("parsing result: params: '%s'\n", para); + + TRACE("parsing result: video pid %d\n", _vpid); + TRACE("parsing result: audio pid %d\n", _apid); + TRACE("parsing result: PCR pid %d\n", _cpid); + TRACE("parsing result: symbol rate %d\n", _srate); + TRACE("parsing result: Frequency %Ld Hz, %Ld MHz\n", _freq, _freq / 1000000); + + if (fInterfaceType == DVB_TYPE_DVB_T) { + + dvb_t_tuning_parameters_t *param = &tuning_param->u.dvb_t; + + TRACE("Interface is DVB-T\n"); + param->frequency = _freq; + param->inversion = DVB_INVERSION_OFF; + param->bandwidth = DVB_BANDWIDTH_8_MHZ; + param->modulation = DVB_MODULATION_16_QAM; + param->hierarchy = DVB_HIERARCHY_NONE; + param->code_rate_hp = DVB_FEC_2_3; + param->code_rate_lp = DVB_FEC_2_3; + param->transmission_mode = DVB_TRANSMISSION_MODE_8K; + param->guard_interval = DVB_GUARD_INTERVAL_1_4; + } + + if (fInterfaceType == DVB_TYPE_DVB_S) { + dvb_s_tuning_parameters_t *param = &tuning_param->u.dvb_s; + + TRACE("Interface is DVB-S\n"); + + const char *pInv = strchr(para, 'I'); + if (!pInv) + pInv = strchr(para, 'i'); + if (pInv && pInv[1] == '0') { + TRACE("DVB_INVERSION_OFF\n"); + param->inversion = DVB_INVERSION_OFF; + } else if (pInv && pInv[1] == '1') { + TRACE("DVB_INVERSION_ON\n"); + param->inversion = DVB_INVERSION_ON; + } else { + TRACE("parse error, assuming DVB_INVERSION_OFF\n"); + param->inversion = DVB_INVERSION_OFF; + } + + const char *pPolH = strchr(para, 'H'); + if (!pPolH) + pPolH = strchr(para, 'h'); + const char *pPolV = strchr(para, 'V'); + if (!pPolV) + pPolV = strchr(para, 'v'); + if (pPolH && !pPolV) { + TRACE("DVB_POLARITY_HORIZONTAL\n"); + param->polarity = DVB_POLARITY_HORIZONTAL; + } else if (!pPolH && pPolV) { + TRACE("DVB_POLARITY_VERTICAL\n"); + param->polarity = DVB_POLARITY_VERTICAL; + } else { + TRACE("parse error, assuming DVB_POLARITY_HORIZONTAL\n"); + param->polarity = DVB_POLARITY_HORIZONTAL; + } + + param->frequency = _freq; + param->symbolrate = _srate; + } + + return B_OK; +} diff --git a/src/add-ons/media/media-add-ons/dvb/DVBMediaNode.h b/src/add-ons/media/media-add-ons/dvb/DVBMediaNode.h new file mode 100644 index 0000000000..1382820096 --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/DVBMediaNode.h @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __DVB_MEDIA_NODE_H +#define __DVB_MEDIA_NODE_H + +#include +#include +#include +#include +#include +#include +#include + +#include "ts_demux.h" +#include "DVBCard.h" +#include "StringList.h" +#include "DecoderInterface.h" + +class BDiscreteParameter; +class PacketQueue; +class DVBCard; + +class DVBMediaNode : + public virtual BBufferProducer, + public virtual BControllable, + public virtual BMediaEventLooper +{ +public: + DVBMediaNode(BMediaAddOn *addon, + const char *name, int32 internal_id, DVBCard *card, bool *active); // bool *active is a workaround for Zeta +virtual ~DVBMediaNode(); + +virtual status_t InitCheck() const { return fInitStatus; } + +/* BMediaNode */ +public: +virtual BMediaAddOn *AddOn(int32 * internal_id) const; +virtual status_t HandleMessage(int32 message, const void *data, + size_t size); +protected: +virtual void Preroll(); +virtual void SetTimeSource(BTimeSource * time_source); +virtual void SetRunMode(run_mode mode); + +/* BMediaEventLooper */ +protected: +virtual void NodeRegistered(); +virtual void Stop(bigtime_t performance_time, bool immediate); + +virtual void HandleEvent(const media_timed_event *event, + bigtime_t lateness, bool realTimeEvent = false); + +/* BBufferProducer */ +protected: +virtual status_t FormatSuggestionRequested(media_type type, int32 quality, + media_format * format); +virtual status_t FormatProposal(const media_source &source, + media_format *format); +virtual status_t FormatChangeRequested(const media_source &source, + const media_destination &destination, + media_format *io_format, int32 *_deprecated_); +virtual status_t GetNextOutput(int32 * cookie, media_output * out_output); +virtual status_t DisposeOutputCookie(int32 cookie); +virtual status_t SetBufferGroup(const media_source &for_source, + BBufferGroup * group); +virtual status_t VideoClippingChanged(const media_source &for_source, + int16 num_shorts, int16 *clip_data, + const media_video_display_info &display, + int32 * _deprecated_); +virtual status_t GetLatency(bigtime_t * out_latency); +virtual status_t PrepareToConnect(const media_source &what, + const media_destination &where, + media_format *format, + media_source *out_source, char *out_name); +virtual void Connect(status_t error, const media_source &source, + const media_destination &destination, + const media_format & format, char *io_name); +virtual void Disconnect(const media_source & what, + const media_destination & where); +virtual void LateNoticeReceived(const media_source & what, + bigtime_t how_much, bigtime_t performance_time); +virtual void EnableOutput(const media_source & what, bool enabled, + int32 * _deprecated_); +virtual status_t SetPlayRate(int32 numer,int32 denom); +virtual void AdditionalBufferRequested(const media_source & source, + media_buffer_id prev_buffer, bigtime_t prev_time, + const media_seek_tag * prev_tag); +virtual void LatencyChanged(const media_source & source, + const media_destination & destination, + bigtime_t new_latency, uint32 flags); + +/* BControllable */ +protected: +virtual status_t GetParameterValue(int32 id, bigtime_t *last_change, + void *value, size_t *size); +virtual void SetParameterValue(int32 id, bigtime_t when, + const void *value, size_t size); + +/* state */ +private: + void HandleStart(bigtime_t performance_time); + void HandleStop(); + void HandleTimeWarp(bigtime_t performance_time); + void HandleSeek(bigtime_t performance_time); + + void InitDefaultFormats(); + void SpecializeFormatRawVideo(media_raw_video_format *format); + void SpecializeFormatRawAudio(media_multi_audio_format *format); + void SpecializeFormatEncVideo(media_encoded_video_format *format); + void SpecializeFormatEncAudio(media_encoded_audio_format *format); + void SpecializeFormatTS(media_multistream_format *format); + + bool VerifyFormatRawVideo(const media_raw_video_format &format); + bool VerifyFormatRawAudio(const media_multi_audio_format &format); + + status_t GetStreamFormat(PacketQueue *queue, media_format *format); + + status_t SetOutput(const media_source &source, const media_destination &destination, const media_format &format); + status_t ResetOutput(const media_source &source); + const char * SourceDefaultName(const media_source &source); + + BParameterWeb * CreateParameterWeb(); + void SetAboutInfo(BParameterGroup *about); + + + void RefreshParameterWeb(); + + void LoadSettings(); + + void LoadAddons(); + void UnloadAddons(); + image_id LoadAddon(const char *path, const char *name, void **ptr); + + void AddStateItems(BDiscreteParameter *param); + void AddRegionItems(BDiscreteParameter *param); + void AddChannelItems(BDiscreteParameter *param); + void AddAudioItems(BDiscreteParameter *param); + + void RefreshStateList(); + void RefreshRegionList(); + void RefreshChannelList(); + void RefreshAudioList(); + + status_t StartCapture(); + status_t StopCapture(); + status_t StartCaptureThreads(); + status_t StopCaptureThreads(); + status_t Tune(); + + status_t ExtractTuningParams(const char *description, int audio_pid_index, + dvb_tuning_parameters_t *param, + int *video_pid, int *audio_pid, int *pcr_pid); + + static int32 _card_reader_thread_(void *arg); + static int32 _mpeg_demux_thread_(void *arg); + static int32 _enc_audio_thread_(void *arg); + static int32 _enc_video_thread_(void *arg); + static int32 _mpeg_ts_thread_(void *arg); + static int32 _raw_audio_thread_(void *arg); + static int32 _raw_video_thread_(void *arg); + + void card_reader_thread(); + void mpeg_demux_thread(); + void enc_audio_thread(); + void enc_video_thread(); + void mpeg_ts_thread(); + void raw_audio_thread(); + void raw_video_thread(); + + status_t GetNextVideoChunk(const void **chunkData, size_t *chunkLen, media_header *mh); + status_t GetNextAudioChunk(const void **chunkData, size_t *chunkLen, media_header *mh); + + static status_t _GetNextVideoChunk(const void **chunkData, size_t *chunkLen, media_header *mh, void *cookie); + static status_t _GetNextAudioChunk(const void **chunkData, size_t *chunkLen, media_header *mh, void *cookie); + + status_t fInitStatus; + + int32 fInternalID; + BMediaAddOn *fAddOn; + + bool fStopDisabled; + + bool fOutputEnabledRawVideo; + bool fOutputEnabledRawAudio; + bool fOutputEnabledEncVideo; + bool fOutputEnabledEncAudio; + bool fOutputEnabledTS; + + media_output fOutputRawVideo; + media_output fOutputRawAudio; + media_output fOutputEncVideo; + media_output fOutputEncAudio; + media_output fOutputTS; + + media_format fDefaultFormatRawVideo; + media_format fDefaultFormatRawAudio; + media_format fDefaultFormatEncVideo; + media_format fDefaultFormatEncAudio; + media_format fDefaultFormatTS; + + media_format fRequiredFormatRawVideo; + media_format fRequiredFormatRawAudio; + media_format fRequiredFormatEncVideo; + media_format fRequiredFormatEncAudio; + media_format fRequiredFormatTS; + + PacketQueue * fCardDataQueue; // holds MPEG TS data read from the card + PacketQueue * fRawVideoQueue; // holds encoded PES frames for video decoding + PacketQueue * fRawAudioQueue; // holds encoded PES frames for audio decoding + PacketQueue * fEncVideoQueue; // holds encoded PES frames for enc video out + PacketQueue * fEncAudioQueue; // holds encoded PES frames for enc audio out + PacketQueue * fMpegTsQueue; // holds encoded PES frames for MPEG TS out + + DVBCard * fCard; + + bool fCaptureThreadsActive; + + thread_id fThreadIdCardReader; + thread_id fThreadIdMpegDemux; + thread_id fThreadIdRawAudio; + thread_id fThreadIdRawVideo; + thread_id fThreadIdEncAudio; + thread_id fThreadIdEncVideo; + thread_id fThreadIdMpegTS; + + volatile bool fTerminateThreads; + + TSDemux * fDemux; + + BBufferGroup * fBufferGroupRawVideo; + BBufferGroup * fBufferGroupRawAudio; + + BParameterWeb * fWeb; + + dvb_tuning_parameters_t fTuningParam; + dvb_type_t fInterfaceType; + + int fAudioPid; + int fVideoPid; + int fPcrPid; + + bool fTuningSuccess; + bool fCaptureActive; + + sem_id fVideoDelaySem; + sem_id fAudioDelaySem; + + int fSelectedState; + int fSelectedRegion; + int fSelectedChannel; + int fSelectedAudio; + + StringList * fStateList; + StringList * fRegionList; + StringList * fChannelList; + StringList * fAudioList; + + DecoderInterface * fVideoDecoder; + DecoderInterface * fAudioDecoder; + + Packet * fCurrentVideoPacket; + Packet * fCurrentAudioPacket; + + image_id fMpeg2VideoDecoderImage; + image_id fMpeg2AudioDecoderImage; + image_id fAC3DecoderImage; + image_id fDeinterlaceFilterImage; + + DecoderInterface * (*instantiate_mpeg2_video_decoder)(get_next_chunk_func, void *, const char *); + DecoderInterface * (*instantiate_mpeg2_audio_decoder)(get_next_chunk_func, void *, const char *); + DecoderInterface * (*instantiate_ac3_audio_decoder)(get_next_chunk_func, void *, const char *); + DecoderInterface * (*instantiate_deinterlace_filter)(get_next_chunk_func, void *, const char *); + + BLocker lock; + + bool * fActive; // workaround for Zeta + + // used only for debugging: + int fVideoFile; + int fAudioFile; + int fRawAudioFile; +}; + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/MediaFormat.cpp b/src/add-ons/media/media-add-ons/dvb/MediaFormat.cpp new file mode 100644 index 0000000000..1561b995bb --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/MediaFormat.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "MediaFormat.h" + +void +PrintFormat(const media_output &output) +{ + PrintFormat(output.format); +} + + +void +PrintFormat(const media_format &format) +{ + char s[200]; + string_for_format(format, s, sizeof(s)); + printf("%s\n", s); +} + + +status_t +GetHeaderFormat(media_format *out_format, const void *header, size_t size, int stream_id) +{ + + const uint8 *d = (const uint8 *)header; + printf("GetHeaderFormat: stream_id %02x\n", stream_id); + printf("frame header: " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + + if (d[0] == 0xff && d[1] == 0xfc) { + + printf("GetHeaderFormat: assuming mepeg audio\n"); + + status_t err; + + media_format_description desc; + desc.family = B_MPEG_FORMAT_FAMILY; + desc.u.mpeg.id = B_MPEG_1_AUDIO_LAYER_3; + + BMediaFormats formats; + media_format format; + + err = formats.InitCheck(); + if (err) + return err; + + err = formats.GetFormatFor(desc, out_format); + if (err) + return err; + + out_format->u.encoded_audio.output.frame_rate = 48000; + out_format->u.encoded_audio.output.channel_count = 2; + out_format->u.encoded_audio.output.buffer_size = 1024; + + printf("GetHeaderFormat: "); + PrintFormat(*out_format); + } else { + + printf("GetHeaderFormat: assuming mepeg video\n"); + + status_t err; + + media_format_description desc; + desc.family = B_MPEG_FORMAT_FAMILY; + desc.u.mpeg.id = B_MPEG_1_VIDEO; + + BMediaFormats formats; + media_format format; + + err = formats.InitCheck(); + if (err) + return err; + + err = formats.GetFormatFor(desc, out_format); + if (err) + return err; + + } + + return B_OK; +} diff --git a/src/add-ons/media/media-add-ons/dvb/MediaFormat.h b/src/add-ons/media/media-add-ons/dvb/MediaFormat.h new file mode 100644 index 0000000000..df1a9cd710 --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/MediaFormat.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __MEDIA_FORMAT_H +#define __MEDIA_FORMAT_H + +class media_output; +class media_format; + +void PrintFormat(const media_output &output); +void PrintFormat(const media_format &format); + +status_t GetHeaderFormat(media_format *out_format, const void *header, size_t size, int stream_id); + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/Packet.cpp b/src/add-ons/media/media-add-ons/dvb/Packet.cpp new file mode 100644 index 0000000000..5af909beb7 --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/Packet.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "packet.h" + +#define PACKET +#ifdef PACKET + #define TRACE printf +#else + #define TRACE(a...) +#endif + + +Packet::Packet(size_t init_size) + : fBuffer(malloc(init_size)) + , fBufferSize(0) + , fBufferSizeMax(init_size) + , fTimeStamp(0) +{ +} + + +Packet::Packet(const Packet &clone) +{ + fBufferSize = clone.Size(); + fBufferSizeMax = fBufferSize; + fBuffer = malloc(fBufferSize); + fTimeStamp = clone.TimeStamp(); + memcpy(fBuffer, clone.Data(), fBufferSize); +} + + +Packet::Packet(const void *data, size_t size, bigtime_t time_stamp) + : fBuffer(malloc(size)) + , fBufferSize(size) + , fBufferSizeMax(size) + , fTimeStamp(time_stamp) +{ + memcpy(fBuffer, data, size); +} + + +Packet::~Packet() +{ + free(fBuffer); +} + + +void +Packet::AddData(const void *data, size_t size) +{ + if (fBufferSize + size > fBufferSizeMax) { + fBufferSizeMax = (fBufferSize + size + 8191) & ~8191; + fBuffer = realloc(fBuffer, fBufferSizeMax); + } + + memcpy((char *)fBuffer + fBufferSize, data, size); + fBufferSize += size; +} diff --git a/src/add-ons/media/media-add-ons/dvb/Packet.h b/src/add-ons/media/media-add-ons/dvb/Packet.h new file mode 100644 index 0000000000..3de2401f9e --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/Packet.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __PACKET_H +#define __PACKET_H + +#include + +class Packet +{ +public: + Packet(size_t init_size = 8192); + Packet(const Packet &clone); + Packet(const void *data, size_t size, bigtime_t time_stamp = 0); + ~Packet(); + + void AddData(const void *data, size_t size); + + void MakeEmpty(); + + const uint8 * Data() const; + size_t Size() const; + + void SetTimeStamp(bigtime_t time_stamp); + bigtime_t TimeStamp() const; + + +private: + // not implemented + Packet & operator= (const Packet &clone); + +private: + void * fBuffer; + size_t fBufferSize; + size_t fBufferSizeMax; + bigtime_t fTimeStamp; +}; + + +inline const uint8 * +Packet::Data() const +{ + return (uint8 *)fBuffer; +} + + +inline size_t +Packet::Size() const +{ + return fBufferSize; +} + + +inline void +Packet::SetTimeStamp(bigtime_t time_stamp) +{ + fTimeStamp = time_stamp; +} + + +inline bigtime_t +Packet::TimeStamp() const +{ + return fTimeStamp; +} + + +inline void +Packet::MakeEmpty() +{ + fBufferSize = 0; + fTimeStamp = 0; +} + + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/PacketQueue.cpp b/src/add-ons/media/media-add-ons/dvb/PacketQueue.cpp new file mode 100644 index 0000000000..16ba73de3b --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/PacketQueue.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "Packet.h" +#include "PacketQueue.h" + +#define TRACE_PACKET_QUEUE +#ifdef TRACE_PACKET_QUEUE + #define TRACE printf +#else + #define TRACE(a...) +#endif + + +PacketQueue::PacketQueue(int max_packets) + : fQueue(new Packet* [max_packets]) + , fSem(create_sem(0, "packet queue sem")) + , fTerminate(false) + , fWriteIndex(0) + , fReadIndex(0) + , fMaxPackets(max_packets) + , fPacketCount(0) +{ +} + + +PacketQueue::~PacketQueue() +{ + Empty(); + delete_sem(fSem); + delete [] fQueue; +} + + +void +PacketQueue::Empty() +{ + while (fPacketCount--) { + delete fQueue[fReadIndex]; + fReadIndex = (fReadIndex + 1) % fMaxPackets; + } +} + + +status_t +PacketQueue::Insert(Packet *packet) +{ + if (fTerminate) { + return B_NOT_ALLOWED; + } + if (atomic_add(&fPacketCount, 1) == fMaxPackets) { + atomic_add(&fPacketCount, -1); + return B_WOULD_BLOCK; + } + fQueue[fWriteIndex] = packet; + fWriteIndex = (fWriteIndex + 1) % fMaxPackets; + release_sem(fSem); + return B_OK; +} + + +status_t +PacketQueue::Remove(Packet **packet) +{ + if (fTerminate) { + return B_NOT_ALLOWED; + } + if (acquire_sem(fSem) != B_OK) + return B_ERROR; + if (fTerminate) { + return B_NOT_ALLOWED; + } + *packet = fQueue[fReadIndex]; + atomic_add(&fPacketCount, -1); + fReadIndex = (fReadIndex + 1) % fMaxPackets; + return B_OK; +} + + +void +PacketQueue::Flush(bigtime_t timeout) +{ + if (fTerminate) { + return; + } + + timeout += system_time(); + + while (acquire_sem_etc(fSem, 1, B_ABSOLUTE_TIMEOUT, timeout) == B_OK) { + if (fTerminate) { + return; + } + Packet *packet = fQueue[fReadIndex]; + atomic_add(&fPacketCount, -1); + fReadIndex = (fReadIndex + 1) % fMaxPackets; + delete packet; + } +} + + +// peeks into queue and delivers a copy +status_t +PacketQueue::Peek(Packet **packet) +{ + if (fTerminate) { + return B_NOT_ALLOWED; + } + if (acquire_sem(fSem) != B_OK) + return B_ERROR; + if (fTerminate) { + return B_NOT_ALLOWED; + } + *packet = new Packet(*fQueue[fReadIndex]); + release_sem(fSem); + return B_OK; +} + + +void +PacketQueue::Terminate() +{ + fTerminate = true; + release_sem(fSem); +} + + +void +PacketQueue::Restart() +{ + Empty(); + + delete_sem(fSem); + fSem = create_sem(0, "packet queue sem"); + fTerminate = false; + fWriteIndex = 0; + fReadIndex = 0; + fPacketCount = 0; +} diff --git a/src/add-ons/media/media-add-ons/dvb/PacketQueue.h b/src/add-ons/media/media-add-ons/dvb/PacketQueue.h new file mode 100644 index 0000000000..bc8075bc8f --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/PacketQueue.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __PACKET_QUEUE_H +#define __PACKET_QUEUE_H + +#include + +class Packet; + +class PacketQueue +{ +public: + PacketQueue(int max_packets = 5); + ~PacketQueue(); + + status_t Insert(Packet *packet); + status_t Remove(Packet **packet); + + // peeks into queue and delivers a copy + status_t Peek(Packet **packet); + + void Flush(bigtime_t timeout); + + void Terminate(); + void Restart(); + +private: + void Empty(); + + // not implemented + PacketQueue(const PacketQueue & clone); + PacketQueue & operator=(PacketQueue & clone); + +private: + + Packet **fQueue; + sem_id fSem; + volatile bool fTerminate; + + int fWriteIndex; + int fReadIndex; + int fMaxPackets; + int32 fPacketCount; +}; + + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/StringList.h b/src/add-ons/media/media-add-ons/dvb/StringList.h new file mode 100644 index 0000000000..3d434f4816 --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/StringList.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __STRING_LIST_H +#define __STRING_LIST_H + +#include +#include +#include +#include + +class StringList +{ +public: + StringList(); + ~StringList(); + + // AddItem makes a copy of the data + void AddItem(const char *string); + + // either the String or NULL + const char *ItemAt(int index) const; + + void MakeEmpty(); + +private: + BList list; +}; + + +inline +StringList::StringList() + : list() +{ +} + + +inline +StringList::~StringList() +{ + MakeEmpty(); +} + + +inline void +StringList::AddItem(const char *string) +{ + list.AddItem(strdup(string)); +} + + +inline const char * +StringList::ItemAt(int index) const +{ + return (const char *)list.ItemAt(index); +} + + +inline void +StringList::MakeEmpty() +{ + int i = list.CountItems(); + while (--i > -1) + free(list.RemoveItem(i)); +} + + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/TransportStreamDemux.cpp b/src/add-ons/media/media-add-ons/dvb/TransportStreamDemux.cpp new file mode 100644 index 0000000000..3fcf5851cf --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/TransportStreamDemux.cpp @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "ts_demux.h" +#include "packet.h" +#include "packet_queue.h" + +#define TRACE_TS_DEMUX +#ifdef TRACE_TS_DEMUX + #define TRACE printf +#else + #define TRACE(a...) +#endif + +#define CLOCK_TO_USEC(clck) (bigtime_t((clck) + 13) / 27) +#define USEC_TO_CLOCK(usec) (bigtime_t((usec) * 27)) + + +TransportStreamDemux::TransportStreamDemux( + PacketQueue *vid_queue, PacketQueue *aud_queue, + PacketQueue *vid_queue2, PacketQueue *aud_queue2, + PacketQueue *mpeg_ts_queue) + : fCount(0) + , fSystemTime(0) + , fPerformanceTime(0) + , fLastEndTime(0) + , fVidPid(-1) + , fAudPid(-1) + , fPcrPid(-1) + , fVidQueue(vid_queue) + , fVidQueue2(vid_queue2) + , fAudQueue(aud_queue) + , fAudQueue2(aud_queue2) + , fMpegTsQueue(mpeg_ts_queue) + , fVidPacket(new Packet) + , fAudPacket(new Packet) + , fVidPacketValid(false) + , fAudPacketValid(false) +{ +} + + +TransportStreamDemux::~TransportStreamDemux() +{ + delete fVidPacket; + delete fAudPacket; +} + + +void +TransportStreamDemux::SetPIDs(int vid_pid, int aud_pid, int pcr_pid) +{ + fVidPacket->MakeEmpty(); + fAudPacket->MakeEmpty(); + fVidPacketValid = false; + fAudPacketValid = false; + fVidPid = vid_pid; + fAudPid = aud_pid; + fPcrPid = pcr_pid; +} + + +void +TransportStreamDemux::ProcessPacket(const mpeg_ts_packet *pkt, bigtime_t start_time) +{ + fCount++; + +// printf("ProcessPacket %Ld\n", fCount); + + if (pkt->SyncByte() != 0x47) { +// fCountInvalid++; + printf("packet %Ld sync byte error " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", fCount, + pkt->header[0], pkt->header[1], pkt->header[2], pkt->header[3], + pkt->data[0], pkt->data[1], pkt->data[2], pkt->data[3], + pkt->data[4], pkt->data[5], pkt->data[6], pkt->data[7], + pkt->data[8], pkt->data[9], pkt->data[10], pkt->data[11]); + return; + } + +// if (pkt->TransportError()) { +// fCountInvalid++; +// printf("invalid packet %Ld (transport_error_indicator)\n", fCount); +// return; +// } + + int pid = pkt->PID(); + + if (pid == 0) { + ProcessPAT(pkt); + return; + } + + if (pid == fPcrPid) { + ProcessPCR(pkt, start_time); + } + + if (pid == fAudPid) { + ProcessAUD(pkt); + } + + if (pid == fVidPid) { + ProcessVID(pkt); + } +} + + +void +TransportStreamDemux::ProcessPAT(const mpeg_ts_packet *pkt) +{ +} + + +void +TransportStreamDemux::ProcessPCR(const mpeg_ts_packet *pkt, bigtime_t start_time) +{ + if (!(pkt->AdaptationFieldControl() & 0x2)) // adaptation field present? + return; + if (pkt->data[0] < 7) // long enough? + return; + if (!(pkt->data[1] & 0x10)) // PCR present? + return; + int64 pcr; + pcr = (uint64(pkt->data[2]) << 25) + | (uint32(pkt->data[3]) << 17) + | (uint32(pkt->data[4]) << 9) + | (uint32(pkt->data[5]) << 1) + | (pkt->data[6] >> 7); + pcr *= 300; + pcr += (pkt->data[6] & 1) | pkt->data[7]; +// printf(" pcr = %Ld\n", pcr); + +// bigtime_t now = system_time(); +// printf("system_time %.3f, PCR-time %.3f, delta %.06f, PCR %Ld\n", now / 1e6, CLOCK_TO_USEC(pcr) / 1e6, (CLOCK_TO_USEC(pcr) - now) / 1e6, pcr); + +// atomic_set64(&fCurrentTime, CLOCK_TO_USEC(pcr)); + + fTimeSourceLocker.Lock(); + fSystemTime = start_time; + fPerformanceTime = CLOCK_TO_USEC(pcr); + fTimeSourceLocker.Unlock(); +} + + +void +TransportStreamDemux::ProcessAUD(const mpeg_ts_packet *pkt) +{ + // flush old packet + if (pkt->PayloadUnitStart()) { + if (fAudPacketValid) { + Packet *clone = new Packet(*fAudPacket); + status_t err = fAudQueue->Insert(fAudPacket); + if (err != B_OK) { + delete fAudPacket; + if (err == B_WOULD_BLOCK) { + printf("fAudQueue->Insert failed (would block)\n"); + } + } + err = fAudQueue2->Insert(clone); + if (err != B_OK) { + delete clone; + if (err == B_WOULD_BLOCK) { + printf("fAudQueue2->Insert failed (would block)\n"); + } + } + fAudPacket = new Packet; + } else { + fAudPacket->MakeEmpty(); + fAudPacketValid = true; + } + } + + int skip; + switch (pkt->AdaptationFieldControl()) { + case 0: // illegal + skip = 184; + break; + case 1: // payload only + skip = 0; + break; + case 2: // adaptation field only + skip = 184; + break; + case 3: // adaptation field followed by payload + skip = pkt->data[0] + 1; + if (skip > 184) + skip = 184; + break; + default: + skip = 0; // stupid compiler, impossible case + } + + const uint8 *data = pkt->data + skip; + int size = 184 - skip; + +// if (skip != 0) +// printf("aud skip %d\n", skip); + + if (pkt->PayloadUnitStart()) { + + if (pkt->TransportError()) { + printf("error in audio packet %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3]); + fAudPacketValid = false; + return; + } + + if (data[0] || data[1] || data[2] != 0x01 || data[3] <= 0xbf || data[3] >= 0xf0) { + printf("invalid audio packet %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3]); + fAudPacketValid = false; + return; + } + + if (data[7] & 0x80) { // PTS + int64 pts; + int64 dts; + + pts = (uint64((data[9] >> 1) & 0x7) << 30) + | (data[10] << 22) | ((data[11] >> 1) << 15) + | (data[12] << 7) | (data[13] >> 1); + pts *= 300; + +// printf("vid pts = %Ld\n", pts); + + if (data[7] & 0x40) { // DTS + + dts = (uint64((data[14] >> 1) & 0x7) << 30) + | (data[15] << 22) | ((data[16] >> 1) << 15) + | (data[17] << 7) | (data[18] >> 1); + dts *= 300; + +// printf("aud dts = %Ld\n", dts); + } else { + dts = pts; + } + + fAudPacket->SetTimeStamp(CLOCK_TO_USEC(dts)); + } + + +// printf("aud %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", +// data[0], data[1], data[2], data[3], data[4], +// data[5], data[6], data[7], data[8], data[9]); + } + + fAudPacket->AddData(data, size); +} + + +void +TransportStreamDemux::ProcessVID(const mpeg_ts_packet *pkt) +{ + // flush old packet +// if (pkt->PayloadUnitStart() || pkt->TransportError()) { + if (pkt->PayloadUnitStart()) { + if (fVidPacketValid) { + Packet *clone = new Packet(*fVidPacket); + status_t err = fVidQueue->Insert(fVidPacket); + if (err != B_OK) { + delete fVidPacket; + if (err == B_WOULD_BLOCK) { + printf("fVidQueue->Insert failed (would block)\n"); + } + } + err = fVidQueue2->Insert(clone); + if (err != B_OK) { + delete clone; + if (err == B_WOULD_BLOCK) { + printf("fVidQueue2->Insert failed (would block)\n"); + } + } + fVidPacket = new Packet; + } else { + fVidPacket->MakeEmpty(); + fVidPacketValid = true; + } + } + +// if (pkt->TransportError()) { +// printf("transport error\n"); +// fVidPacketValid = false; +// return; +// } + + int skip; + switch (pkt->AdaptationFieldControl()) { + case 0: // illegal + skip = 184; + break; + case 1: // payload only + skip = 0; + break; + case 2: // adaptation field only + skip = 184; + break; + case 3: // adaptation field followed by payload + skip = pkt->data[0] + 1; + if (skip > 184) + skip = 184; + break; + default: + skip = 0; // stupid compiler, impossible case + } + + const uint8 *data = pkt->data + skip; + int size = 184 - skip; + +// if (skip != 0) +// printf("vid skip %d\n", skip); + + if (pkt->PayloadUnitStart()) { + + if (pkt->TransportError()) { + printf("error in video packet %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3]); + fVidPacketValid = false; + return; + } + + if (data[0] || data[1] || data[2] != 0x01 || data[3] <= 0xbf || data[3] >= 0xf0) { + printf("invalid video packet %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3]); + fVidPacketValid = false; + return; + } + + if (data[7] & 0x80) { // PTS + int64 pts; + int64 dts; + + pts = (uint64((data[9] >> 1) & 0x7) << 30) + | (data[10] << 22) | ((data[11] >> 1) << 15) + | (data[12] << 7) | (data[13] >> 1); + pts *= 300; + +// printf("vid pts = %Ld\n", pts); + + if (data[7] & 0x40) { // DTS + + dts = (uint64((data[14] >> 1) & 0x7) << 30) + | (data[15] << 22) | ((data[16] >> 1) << 15) + | (data[17] << 7) | (data[18] >> 1); + dts *= 300; + +// printf("vid dts = %Ld\n", dts); +// printf("dts = %Ld, pts = %Ld, delta %Ld ### dts = %Ld, pts = %Ld, delta %Ld\n", +// dts, pts, pts - dts, CLOCK_TO_USEC(dts), CLOCK_TO_USEC(pts), CLOCK_TO_USEC(pts - dts)); + } else { + dts = pts; + } + +// printf("clocks: dts = %14Ld, pts = %14Ld, delta %8Ld ### usec: dts = %14Ld, pts = %14Ld, delta %8Ld\n", +// dts, pts, pts - dts, CLOCK_TO_USEC(dts), CLOCK_TO_USEC(pts), CLOCK_TO_USEC(pts - dts)); + + fVidPacket->SetTimeStamp(CLOCK_TO_USEC(dts)); + } + +// printf("vid %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", +// data[0], data[1], data[2], data[3], data[4], +// data[5], data[6], data[7], data[8], data[9]); + } + + fVidPacket->AddData(data, size); +} + + +inline void +TransportStreamDemux::ProcessData(const void *data, int size, bigtime_t start_time, bigtime_t delta) +{ + const uint8 *d = (const uint8 *)data; + if (d[0] != 0x47 && size > 376 && d[188] != 0x47 && d[376] != 0x47) { + printf("TransportStreamDemux::ProcessData: input sync error: " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + return; + } + + const mpeg_ts_packet *pkt = (const mpeg_ts_packet *)data; + int count = size / 188; + for (int i = 0; i < count; i++) { + ProcessPacket(pkt++, start_time + (i * delta) / count); + } +} + + +void +TransportStreamDemux::AddData(Packet *packet) +{ + bigtime_t end_time = packet->TimeStamp(); + + if (fLastEndTime == 0) { + #define DEFAULT_DELTA 36270 + // need something at the start, 36270 usec is the packet length + // in Germany, and should be ok for other countries, too + fLastEndTime = end_time - DEFAULT_DELTA; + } + + bigtime_t delta = end_time - fLastEndTime; + + // sanity check + if (delta > (3 * DEFAULT_DELTA)) { + printf("TransportStreamDemux::ProcessData: delta %.6f is too large\n", delta / 1E6); + fLastEndTime = end_time - DEFAULT_DELTA; + delta = DEFAULT_DELTA; + } + + ProcessData(packet->Data(), packet->Size(), fLastEndTime, delta); + + packet->SetTimeStamp(fLastEndTime); + + fLastEndTime = end_time; + + status_t err = fMpegTsQueue->Insert(packet); + if (err != B_OK) { + delete packet; + if (err == B_WOULD_BLOCK) { + printf("fMpegTsQueue->Insert failed (would block)\n"); + } + } +} + diff --git a/src/add-ons/media/media-add-ons/dvb/TransportStreamDemux.h b/src/add-ons/media/media-add-ons/dvb/TransportStreamDemux.h new file mode 100644 index 0000000000..e054617789 --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/TransportStreamDemux.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __TRANSPORT_STREAM_DEMUX_H +#define __TRANSPORT_STREAM_DEMUX_H + +#include +#include "mpeg_ts_packet.h" + +class PacketQueue; +class Packet; + +class TransportStreamDemux +{ +public: + TransportStreamDemux( + PacketQueue *vid_queue, PacketQueue *aud_queue, + PacketQueue *vid_queue2, PacketQueue *aud_queue2, + PacketQueue *mpeg_ts_queue); + virtual ~TransportStreamDemux(); + + void SetPIDs(int vid_pid, int aud_pid, int pcr_pid); + + void AddData(Packet *packet); + + void TimesourceInfo(bigtime_t *perf_time, bigtime_t *sys_time); + +private: + void ProcessData(const void *data, int size, bigtime_t start_time, bigtime_t delta); + void ProcessPacket(const mpeg_ts_packet *pkt, bigtime_t start_time); + +protected: + virtual void ProcessPCR(const mpeg_ts_packet *pkt, bigtime_t start_time); + virtual void ProcessPAT(const mpeg_ts_packet *pkt); + virtual void ProcessAUD(const mpeg_ts_packet *pkt); + virtual void ProcessVID(const mpeg_ts_packet *pkt); + +private: + int64 fCount; + bigtime_t fSystemTime; + bigtime_t fPerformanceTime; + bigtime_t fLastEndTime; + int fVidPid; + int fAudPid; + int fPcrPid; + PacketQueue * fVidQueue; + PacketQueue * fVidQueue2; + PacketQueue * fAudQueue; + PacketQueue * fAudQueue2; + PacketQueue * fMpegTsQueue; + Packet * fVidPacket; + Packet * fAudPacket; + bool fVidPacketValid; + bool fAudPacketValid; + BLocker fTimeSourceLocker; +}; + + +inline void +TransportStreamDemux::TimesourceInfo(bigtime_t *perf_time, bigtime_t *sys_time) +{ + fTimeSourceLocker.Lock(); + *perf_time = fPerformanceTime; + *sys_time = fSystemTime; + fTimeSourceLocker.Unlock(); +} + + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/config.h b/src/add-ons/media/media-add-ons/dvb/config.h new file mode 100644 index 0000000000..2f05dea7cd --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/config.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DVB_MEDIA_ADDON_CONFIG_H_ +#define _DVB_MEDIA_ADDON_CONFIG_H_ + +#include "revision.h" + +#define VERSION "1.0" +#define BUILD __DATE__ " "__TIME__ + +#define BUILD_FOR_HAIKU 1 + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/media_header_ex.h b/src/add-ons/media/media-add-ons/dvb/media_header_ex.h new file mode 100644 index 0000000000..c49e6e86bb --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/media_header_ex.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __MEDIA_HEADER_EX_H_ +#define __MEDIA_HEADER_EX_H_ + +#include + +struct media_audio_header_ex { + int32 _reserved_[13]; + float frame_rate; // NEW! + uint32 channel_count; // NEW! +} _PACKED; + + +struct media_video_header_ex { + uint32 _reserved_[8]; + uint32 display_line_width; // NEW! + uint32 display_line_count; // NEW! + uint32 bytes_per_row; // NEW! + uint16 pixel_width_aspect; // NEW! + uint16 pixel_height_aspect; // NEW! + float field_gamma; + uint32 field_sequence; + uint16 field_number; + uint16 pulldown_number; + uint16 first_active_line; + uint16 line_count; +} _PACKED; + + +struct media_header_ex { + media_type type; + media_buffer_id buffer; + int32 destination; + media_node_id time_source; + uint32 _reserved1_; + uint32 size_used; + bigtime_t start_time; + area_id owner; + type_code user_data_type; + uchar user_data[64]; + uint32 _reserved2_[2]; + off_t file_pos; + size_t orig_size; + uint32 data_offset; + union { + media_audio_header_ex raw_audio; + media_video_header_ex raw_video; + media_multistream_header multistream; + media_encoded_audio_header encoded_audio; + media_encoded_video_header encoded_video; + char _reserved_[64]; + } u; +}; + + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/mpeg_ts_packet.h b/src/add-ons/media/media-add-ons/dvb/mpeg_ts_packet.h new file mode 100644 index 0000000000..82801ea2bb --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/mpeg_ts_packet.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _MPEG_TS_PACKET_H +#define _MPEG_TS_PACKET_H + +#include + +class mpeg_ts_packet +{ +public: + int SyncByte() const; + int TransportError() const; + int PayloadUnitStart() const; + int PID() const; + int AdaptationFieldControl() const; + int ContinuityCounter() const; + +public: + uint8 header[4]; + uint8 data[184]; +} _PACKED; + + +inline int mpeg_ts_packet::SyncByte() const +{ + return header[0]; +} + +inline int mpeg_ts_packet::TransportError() const +{ + return header[1] & 0x80; +} + +inline int mpeg_ts_packet::PayloadUnitStart() const +{ + return header[1] & 0x40; +} + +inline int mpeg_ts_packet::PID() const +{ + return ((header[1] << 8) | header[2]) & 0x1fff; +} + +inline int mpeg_ts_packet::AdaptationFieldControl() const +{ + return (header[3] >> 4) & 0x3; +} + +inline int mpeg_ts_packet::ContinuityCounter() const +{ + return header[3] & 0x0f; +} + +#endif diff --git a/src/add-ons/media/media-add-ons/dvb/pes.cpp b/src/add-ons/media/media-add-ons/dvb/pes.cpp new file mode 100644 index 0000000000..14b2a72e93 --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/pes.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "pes.h" + + +status_t +pes_extract(const uint8 *pes_data, size_t pes_size, const uint8 **data, size_t *size) +{ + if (pes_size <= 9) + return B_ERROR; + + if (pes_data[0] || pes_data[1] || pes_data[2] != 1) + return B_ERROR; + + size_t header_size = 9 + pes_data[8]; + + if (pes_size <= header_size) + return B_ERROR; + +// printf("header size %d\n", header_size); + + pes_data += header_size; + pes_size -= header_size; + + *data = pes_data; + *size = pes_size; + + return B_OK; +} + + +status_t +pes_stream_id(const uint8 *pes_data, size_t pes_size, int *stream_id) +{ + if (pes_size <= 9) + return B_ERROR; + + if (pes_data[0] || pes_data[1] || pes_data[2] != 1) + return B_ERROR; + + *stream_id = pes_data[3]; + + return B_OK; +} + diff --git a/src/add-ons/media/media-add-ons/dvb/pes.h b/src/add-ons/media/media-add-ons/dvb/pes.h new file mode 100644 index 0000000000..a672267a0b --- /dev/null +++ b/src/add-ons/media/media-add-ons/dvb/pes.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-2007 Marcus Overhagen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __PES_H +#define __PES_H + +#include + +status_t pes_extract(const uint8 *pes_data, size_t pes_size, const uint8 **data, size_t *size); + +status_t pes_stream_id(const uint8 *pes_data, size_t pes_size, int *stream_id); + +#endif