* Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
*
* 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 <fcntl.h>
#include <malloc.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
#include <MediaRoster.h>
#include <Buffer.h>
#include <BufferGroup.h>
#include <ParameterWeb.h>
#include <TimeSource.h>
#include <String.h>
#include <Autolock.h>
#include <Debug.h>
#include <Directory.h>
#include <Entry.h>
#include <Path.h>
#include "MediaFormat.h"
#include "Packet.h"
#include "PacketQueue.h"
#include "pes.h"
#include "config.h"
#include "DVBMediaNode.h"
#define ENABLE_TRACE
#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
#define VIDEO_BUFFER_REQUEST_TIMEOUT 20000
#define AUDIO_BUFFER_REQUEST_TIMEOUT 10000
#define VIDEO_MAX_EARLY 3000000
#define VIDEO_MAX_LATE 50000
#define AUDIO_MAX_EARLY 3000000
#define AUDIO_MAX_LATE 50000
#define PROCESSING_LATENCY 1500
#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();
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);
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
}
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()
{
* a chance to start. */
}
void
DVBMediaNode::SetTimeSource(BTimeSource *time_source)
{
TRACE("DVBMediaNode::SetTimeSource\n");
}
void
DVBMediaNode::SetRunMode(run_mode mode)
{
TRACE("DVBMediaNode::SetRunMode: %d\n", mode);
TRACE("current RunMode = %d\n", RunMode());
}
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);
SetPriority(110);
LoadSettings();
RefreshParameterWeb();
BMediaRoster::Roster()->SetRunModeNode(Node(), B_RECORDING);
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;
}
}
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));
* 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:
if (fOutputRawVideo.destination != media_destination::null)
goto _bad_source;
color_space c;
c = format->u.raw_video.display.format;
format->SpecializeTo(&fRequiredFormatRawVideo);
format->u.raw_video.display.format = c;
if (!VerifyFormatRawVideo(format->u.raw_video))
goto _bad_format_2;
break;
case ID_RAW_AUDIO:
if (fOutputRawAudio.destination != media_destination::null)
goto _bad_source;
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:
if (fOutputEncVideo.destination != media_destination::null)
goto _bad_source;
format->SpecializeTo(&fRequiredFormatEncVideo);
if (!format->Matches(&fRequiredFormatEncVideo))
goto _bad_format_1;
break;
case ID_ENC_AUDIO:
if (fOutputEncAudio.destination != media_destination::null)
goto _bad_source;
format->SpecializeTo(&fRequiredFormatEncAudio);
if (!format->Matches(&fRequiredFormatEncAudio))
goto _bad_format_1;
break;
case ID_TS:
if (fOutputTS.destination != media_destination::null)
goto _bad_source;
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)
{
* 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
if (source.port != ControlPort())
goto _bad_source;
switch (source.id) {
case ID_RAW_VIDEO:
if (fOutputRawVideo.destination != media_destination::null)
goto _already_connected;
SpecializeFormatRawVideo(&format->u.raw_video);
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
SetOutput(source, destination, *format);
*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)
{
* 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");
ResetOutput(source);
return;
}
#ifdef DEBUG
TRACE("connected format: ");
PrintFormat(format);
#endif
SetOutput(source, destination, format);
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 (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));
ResetOutput(source);
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);
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);
}
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()
{
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;
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
= media_raw_audio_format::B_AUDIO_SHORT;
fDefaultFormatRawAudio.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
fDefaultFormatRawAudio.u.raw_audio.buffer_size = 32768;
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)
{
if (format->field_rate == 0.0)
format->field_rate = 25;
}
void
DVBMediaNode::SpecializeFormatRawAudio(media_multi_audio_format *format)
{
if (format->format == 0)
format->format = media_raw_audio_format::B_AUDIO_SHORT;
if (format->buffer_size == 0)
format->buffer_size = 333 * 8;
}
void
DVBMediaNode::SpecializeFormatEncVideo(media_encoded_video_format *format)
{
}
void
DVBMediaNode::SpecializeFormatEncAudio(media_encoded_audio_format *format)
{
}
void
DVBMediaNode::SpecializeFormatTS(media_multistream_format *format)
{
}
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;
}
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;
}
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));
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;
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<DVBMediaNode *>(arg)->card_reader_thread();
return 0;
}
int32
DVBMediaNode::_mpeg_demux_thread_(void *arg)
{
static_cast<DVBMediaNode *>(arg)->mpeg_demux_thread();
return 0;
}
int32
DVBMediaNode::_raw_audio_thread_(void *arg)
{
static_cast<DVBMediaNode *>(arg)->raw_audio_thread();
return 0;
}
int32
DVBMediaNode::_raw_video_thread_(void *arg)
{
static_cast<DVBMediaNode *>(arg)->raw_video_thread();
return 0;
}
int32
DVBMediaNode::_enc_audio_thread_(void *arg)
{
static_cast<DVBMediaNode *>(arg)->enc_audio_thread();
return 0;
}
int32
DVBMediaNode::_enc_video_thread_(void *arg)
{
static_cast<DVBMediaNode *>(arg)->enc_video_thread();
return 0;
}
int32
DVBMediaNode::_mpeg_ts_thread_(void *arg)
{
static_cast<DVBMediaNode *>(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;
}
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;
}
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;
}
#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;
}
#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;
}
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;
}
#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;
}
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;
}
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);
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;
}
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;
}
}
bigtime_t ts_perf_time;
bigtime_t ts_sys_time;
bigtime_t ts_offset;
bigtime_t aud_time;
bigtime_t start_time;
fDemux->TimesourceInfo(&ts_perf_time, &ts_sys_time);
ts_offset = ts_sys_time - ts_perf_time;
aud_time = mh.start_time;
start_time = TimeSource()->PerformanceTimeFor(aud_time + ts_offset);
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)
if (delay > AUDIO_MAX_EARLY) {
printf("audio: decoded packet is %lldms too early, dropped\n",
delay / 1000);
buf->Recycle();
continue;
}
if (delay > 0)
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;
}
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);
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;
}
}
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;
}
BBuffer* buf;
buf = fBufferGroupRawVideo->RequestBuffer(video_buffer_size_max,
VIDEO_BUFFER_REQUEST_TIMEOUT);
if (!buf) {
TRACE("video: request buffer timout\n");
continue;
}
err = fVideoDecoder->Decode(buf->Data(), &frameCount, &mh, NULL);
if (err) {
buf->Recycle();
printf("fVideoDecoder Decode error %s\n", strerror(err));
continue;
}
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);
video_buffer_size
= fOutputRawVideo.format.u.raw_video.display.line_count
* fOutputRawVideo.format.u.raw_video.display.bytes_per_row;
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;
}
}
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;
start_time = TimeSource()->PerformanceTimeFor(pic_time + ts_offset);
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)
{
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;
mh->start_time = fCurrentVideoPacket->TimeStamp();
return B_OK;
}
inline status_t
DVBMediaNode::GetNextAudioChunk(const void **chunkData, size_t *chunkLen,
media_header *mh)
{
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;
mh->start_time = fCurrentAudioPacket->TimeStamp();
return B_OK;
}
status_t
DVBMediaNode::_GetNextVideoChunk(const void **chunkData, size_t *chunkLen,
media_header *mh, void *cookie)
{
return static_cast<DVBMediaNode *>(cookie)->GetNextVideoChunk(chunkData,
chunkLen, mh);
}
status_t
DVBMediaNode::_GetNextAudioChunk(const void **chunkData, size_t *chunkLen,
media_header *mh, void *cookie)
{
return static_cast<DVBMediaNode *>(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;
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()
{
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_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, "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] == ':')
continue;
if (strchr(line, ':') == NULL)
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");
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, "%[^:]", 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)
{
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));
break;
case ID_REGION:
fSelectedRegion = *(const int32 *)value;
StopCapture();
RefreshChannelList();
RefreshAudioList();
EventQueue()->AddEvent(media_timed_event(0,
M_REFRESH_PARAMETER_WEB));
break;
case ID_CHANNEL:
fSelectedChannel = *(const int32 *)value;
RefreshAudioList();
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 _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;
}