/* * 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 "MediaFormat.h" #include "Packet.h" #include "PacketQueue.h" #include "pes.h" #include "config.h" //#define DUMP_VIDEO //#define DUMP_AUDIO //#define DUMP_RAW_AUDIO //#define DUMP_MPEG_TS #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 M_REFRESH_PARAMETER_WEB (BTimedEventQueue::B_USER_EVENT + 1) DVBMediaNode::DVBMediaNode( BMediaAddOn *addon, const char *name, int32 internal_id, DVBCard *card) : BMediaNode(name) , BBufferProducer(B_MEDIA_RAW_VIDEO) , BControllable() , BMediaEventLooper() , 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 TransportStreamDemux(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) { TRACE("DVBMediaNode::DVBMediaNode\n"); AddNodeKind(B_PHYSICAL_INPUT); fInternalID = internal_id; fAddOn = addon; fInitStatus = B_OK; InitDefaultFormats(); // in the beginning, the required formats are the same as the defaults fRequiredFormatRawVideo = fDefaultFormatRawVideo; fRequiredFormatRawAudio = fDefaultFormatRawAudio; fRequiredFormatEncVideo = fDefaultFormatEncVideo; fRequiredFormatEncAudio = fDefaultFormatEncAudio; fRequiredFormatTS = fDefaultFormatTS; 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 #ifdef DUMP_MPEG_TS fMpegTsFile = open("/boot/home/dvb-mpeg.ts", 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; delete fBufferGroupRawVideo; delete fBufferGroupRawAudio; delete fStateList; delete fRegionList; delete fChannelList; delete fAudioList; #ifdef DUMP_VIDEO close(fVideoFile); #endif #ifdef DUMP_AUDIO close(fAudioFile); #endif #ifdef DUMP_RAW_AUDIO close(fRawAudioFile); #endif #ifdef DUMP_MPEG_TS close(fMpegTsFile); #endif } /* 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 = %lld\n", _m_recordDelay); } void DVBMediaNode::SetRunMode(run_mode mode) { TRACE("DVBMediaNode::SetRunMode: %d\n", mode); TRACE("current RunMode = %d\n", RunMode()); //printf("_m_recordDelay = %lld\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 = %lld\n", _m_recordDelay); Run(); } 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: %lld\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 %lld\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 %lld late at %lld\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 %lld\n", new_latency); } /* DVBMediaNode */ void DVBMediaNode::HandleTimeWarp(bigtime_t performance_time) { TRACE("DVBMediaNode::HandleTimeWarp at %lld\n", performance_time); } void DVBMediaNode::HandleSeek(bigtime_t performance_time) { TRACE("DVBMediaNode::HandleSeek at %lld\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; // XXX broken in Haiku... // 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; bool needs_tuning = false; 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; } */ 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()); #ifdef DUMP_MPEG_TS lock.Lock(); write(fMpegTsFile, packet->Data(), packet->Size()); lock.Unlock(); #endif 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 int8 *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 fAudioDecoder = new MediaStreamDecoder(&_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; 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(), mh.size_used); lock.Unlock(); #endif if (fOutputRawAudio.format.u.raw_audio.buffer_size != mh.size_used || int(fOutputRawAudio.format.u.raw_audio.frame_rate) != mh.u.raw_audio.frame_rate || fOutputRawAudio.format.u.raw_audio.channel_count != mh.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, mh.size_used); TRACE("audio: decode format change: changed channel_count from %ld" " to %ld\n", fOutputRawAudio.format.u.raw_audio.channel_count, mh.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, mh.u.raw_audio.frame_rate); fOutputRawAudio.format.u.raw_audio.buffer_size = mh.size_used; fOutputRawAudio.format.u.raw_audio.frame_rate = mh.u.raw_audio.frame_rate; fOutputRawAudio.format.u.raw_audio.channel_count = mh.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 = mh.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 %lld\n", delay); if (delay < -AUDIO_MAX_LATE) { printf("audio: decoded packet is %lldms too late, dropped\n", -delay / 1000); buf->Recycle(); continue; } if (delay < 0) // printf("audio: decoded packet is %lldms too late\n", -delay / 1000); if (delay > AUDIO_MAX_EARLY) { printf("audio: decoded packet is %lldms too early, dropped\n", delay / 1000); buf->Recycle(); continue; } if (delay > 0) // printf("audio: decoded packet is %lldms 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 %lld\n", start_time - TimeSource()->Now()); media_header* hdr; hdr = buf->Header(); hdr->type = B_MEDIA_RAW_AUDIO; hdr->size_used = mh.size_used; hdr->time_source = TimeSource()->ID(); hdr->start_time = start_time; lock.Lock(); if (SendBuffer(buf, fOutputRawAudio.source, fOutputRawAudio.destination) != B_OK) { 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 fVideoDecoder = new MediaStreamDecoder(&_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; 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 (mh.u.raw_video.display_line_width != fOutputRawVideo.format.u.raw_video.display.line_width || mh.u.raw_video.display_line_count != fOutputRawVideo.format.u.raw_video.display.line_count || mh.u.raw_video.bytes_per_row != fOutputRawVideo.format.u.raw_video.display.bytes_per_row || mh.u.raw_video.pixel_width_aspect != fOutputRawVideo.format.u.raw_video.pixel_width_aspect || mh.u.raw_video.pixel_height_aspect != fOutputRawVideo.format.u.raw_video.pixel_height_aspect || mh.size_used != video_buffer_size) { printf("video format changed:\n"); printf(" line_width %ld => %ld\n", fOutputRawVideo.format.u.raw_video.display.line_width, mh.u.raw_video.display_line_width); printf(" line_count %ld => %ld\n", fOutputRawVideo.format.u.raw_video.display.line_count, mh.u.raw_video.display_line_count); printf(" bytes_per_row %ld => %ld\n", fOutputRawVideo.format.u.raw_video.display.bytes_per_row, mh.u.raw_video.bytes_per_row); printf(" pixel_width_aspect %d => %d\n", fOutputRawVideo.format.u.raw_video.pixel_width_aspect, mh.u.raw_video.pixel_width_aspect); printf(" pixel_height_aspect %d => %d\n", fOutputRawVideo.format.u.raw_video.pixel_height_aspect, mh.u.raw_video.pixel_height_aspect); printf(" video_buffer_size %ld => %ld\n", video_buffer_size, mh.size_used); // recalculate video buffer size 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 = mh.u.raw_video.display_line_width; fOutputRawVideo.format.u.raw_video.display.line_count = mh.u.raw_video.display_line_count; fOutputRawVideo.format.u.raw_video.display.bytes_per_row = mh.u.raw_video.bytes_per_row; fOutputRawVideo.format.u.raw_video.pixel_width_aspect = mh.u.raw_video.pixel_width_aspect; fOutputRawVideo.format.u.raw_video.pixel_height_aspect = mh.u.raw_video.pixel_height_aspect; fOutputRawVideo.format.u.raw_video.last_active = mh.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 = mh.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 %lld\n", delay); if (delay < -VIDEO_MAX_LATE) { printf("video: decoded packet is %lldms too late, dropped\n", -delay / 1000); buf->Recycle(); continue; } if (delay > VIDEO_MAX_EARLY) { printf("video: decoded packet is %lldms 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 %lld\n", start_time - TimeSource()->Now()); media_header* hdr; hdr = buf->Header(); hdr->type = B_MEDIA_RAW_VIDEO; hdr->size_used = video_buffer_size; hdr->time_source = TimeSource()->ID(); hdr->start_time = start_time; lock.Lock(); if (SendBuffer(buf, fOutputRawVideo.source, fOutputRawVideo.destination) != B_OK) { 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 %lld\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 status; Packet *packet; const uint8 *data; size_t size; int stream_id; // get copy of the first packet from queue, and determine format status = queue->Peek(&packet); if (status != B_OK) { TRACE("queue->Peek failed, error %lx\n", status); return status; } status = pes_extract(packet->Data(), packet->Size(), &data, &size); if (status != B_OK) { TRACE("pes_extract failed\n"); goto done; } status = pes_stream_id(packet->Data(), packet->Size(), &stream_id); if (status != B_OK) { TRACE("pes_stream_id failed\n"); goto done; } status = GetHeaderFormat(format, data, size, stream_id); if (status != B_OK) { TRACE("GetHeaderFormat failed, error %lx\n", status); goto done; } done: delete packet; return status; } 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/Media/dvb/dvb-c channels"; break; case DVB_TYPE_DVB_H: dir = "/boot/home/config/settings/Media/dvb/dvb-h channels"; break; case DVB_TYPE_DVB_S: dir = "/boot/home/config/settings/Media/dvb/dvb-s channels"; break; case DVB_TYPE_DVB_T: dir = "/boot/home/config/settings/Media/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 %lld Hz, %lld 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 == NULL) pInv = strchr(para, 'i'); if (pInv != NULL && pInv[1] == '0') { TRACE("DVB_INVERSION_OFF\n"); param->inversion = DVB_INVERSION_OFF; } else if (pInv != NULL && 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 == NULL) pPolH = strchr(para, 'h'); const char *pPolV = strchr(para, 'V'); if (pPolV == NULL) pPolV = strchr(para, 'v'); if (pPolH != NULL && pPolV == NULL) { TRACE("DVB_POLARITY_HORIZONTAL\n"); param->polarity = DVB_POLARITY_HORIZONTAL; } else if (pPolH == NULL && pPolV != NULL) { 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; }