blob: 71de57b07447d9856545ffc069d68b941a885c9f [file] [log] [blame]
#include "nexus_platform.h"
#include "nexus_video_decoder.h"
#include "nexus_stc_channel.h"
#include "nexus_display.h"
#include "nexus_video_window.h"
#include "nexus_video_input.h"
#include "nexus_video_adj.h"
#include "nexus_spdif_output.h"
#include "nexus_component_output.h"
#if NEXUS_HAS_HDMI_OUTPUT
#include "nexus_hdmi_output.h"
#endif
#if NEXUS_HAS_PLAYBACK && NEXUS_HAS_STREAM_MUX
#include "nexus_playback.h"
#include "nexus_record.h"
#include "nexus_file.h"
#include "nexus_video_encoder.h"
#include "nexus_audio_encoder.h"
#include "nexus_audio_mixer.h"
#include "nexus_stream_mux.h"
#include "nexus_recpump.h"
#include "nexus_record.h"
#endif
#if NEXUS_HAS_SYNC_CHANNEL
#include "nexus_sync_channel.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include "bstd.h"
#include "bkni.h"
#include "bdbg.h"
#include <pthread.h>
#include <malloc.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
////////////////////////////////////////////////////////////////////////////////
/* Hard-Coded For Now */
#define TRANSPORT_TYPE NEXUS_TransportType_eTs
#define VIDEO_PID 0x1e1
#define VIDEO_CODEC NEXUS_VideoCodec_eH264
#define AUDIO_PID 0x1e2
#define AUDIO_CODEC NEXUS_AudioCodec_eAc3
#define FORMAT NEXUS_VideoFormat_e720p
#define FRAME_RATE NEXUS_VideoFrameRate_e30
#define VIDEO_CODEC_PROFILE NEXUS_VideoCodecProfile_eBaseline
#define VIDEO_CODEC_LEVEL NEXUS_VideoCodecLevel_e31
////////////////////////////////////////////////////////////////////////////////
#define MAX_DSP_ENCODER_WIDTH 416
#define MAX_DSP_ENCODER_HEIGHT 224
#define POLLING_PERIOD 1;
bool volatile transcodeComplete = false;
BDBG_MODULE(PROBATRON);
/* ============= utilities ==============*/
// Wait for a key press or transcode complete
void waitForTranscodeComplete(void)
{
fd_set rfds;
struct timespec tv;
int rv = 0;
tv.tv_sec = POLLING_PERIOD;
tv.tv_nsec = 0;
while (!transcodeComplete) {
// Monitor stdin
FD_ZERO(&rfds);
FD_SET(0, &rfds);
rv = pselect(1, &rfds, NULL, NULL, &tv, NULL); // Returns 0 on timeout, >0 on input
if (rv != 0) {
// Key Press!
BDBG_ERR(("Key Pressed, stopping early!"));
transcodeComplete = true;
}
}
}
/* Generate a CRC for the specified data/length */
/* Initialize crc to 0 for new calculation. Use an old result for subsequent calls. */
static uint32_t CRC32_mpeg(uint32_t crc, uint8_t *data, int length)
{
int j;
crc = ~crc;
while (length--)
{
for (j=0; j<8; j++)
crc = (crc<<1) ^ ((((*data >> (7-j)) ^ (crc >> 31)) & 1) ? 0x04c11db7 : 0);
data++;
}
return crc;
}
#if BTST_ENABLE_TS_LAYER_USER_DATA_PASS_THRU
static void message_callback(void *context, int param)
{
BSTD_UNUSED(context);
BDBG_ERR(("message buffer %d overflows!", param));
}
#endif
static void endOfInputStreamCallback(void *context, int param)
{
BSTD_UNUSED(context);
BDBG_ERR(("Input Complete!"));
transcodeComplete = true;
}
static void transcoderFinishCallback(void *context, int param)
{
BKNI_EventHandle finishEvent = (BKNI_EventHandle)context;
BSTD_UNUSED(param);
BDBG_WRN(("Transcoding Complete!"));
BKNI_SetEvent(finishEvent);
}
struct cmdSettings {
NEXUS_VideoFormat displayFormat;
NEXUS_VideoFrameRate encoderFrameRate;
unsigned encoderBitrate;
unsigned encoderGopStructureFramesP;
unsigned encoderGopStructureFramesB;
unsigned encoderGopStructureDuration;
NEXUS_VideoCodec encoderVideoCodec;
NEXUS_VideoCodecProfile encoderProfile;
NEXUS_VideoCodecLevel encoderLevel;
} cmdSettings[] = {
{FORMAT, FRAME_RATE, 2*1000*1000, 29, 0, 1000, VIDEO_CODEC, VIDEO_CODEC_PROFILE, VIDEO_CODEC_LEVEL}
};
struct streamSettings {
const char *fname;
uint16_t videoPid;
NEXUS_VideoCodec videoCodec;
uint16_t audioPid;
NEXUS_AudioCodec audioCodec;
} streamSettings = { "raw.ts", VIDEO_PID, VIDEO_CODEC, AUDIO_PID, AUDIO_CODEC };
int main(int argc, char *argv[])
{
char* inputFilename;
char* outputFilename;
int duration = 0; // Only used for display purposes to the user
if (argc == 3 || argc == 4)
{
inputFilename = argv[1];
outputFilename = argv[2];
if (argc == 4) duration = atol(argv[3]);
} else {
fprintf(stderr, "Please specify both an input and output file, in that order!\n");
return 1;
}
BDBG_WRN(("\n\n *** All your transcoders are belong to us. *** \n"));
BDBG_WRN(("Reading from file %s and writing to file %s", inputFilename, outputFilename));
transcodeComplete = false;
NEXUS_PlatformSettings platformSettings;
NEXUS_PlatformConfiguration platformConfig;
NEXUS_StcChannelHandle stcChannel;
NEXUS_StcChannelSettings stcSettings;
NEXUS_PidChannelHandle videoPidChannel;
NEXUS_DisplayHandle display;
NEXUS_DisplaySettings displaySettings;
NEXUS_VideoWindowHandle window;
NEXUS_VideoWindowMadSettings windowMadSettings;
#ifdef NEXUS_NUM_DSP_VIDEO_ENCODERS
NEXUS_VideoWindowScalerSettings sclSettings;
NEXUS_VideoWindowSettings windowSettings;
#endif
NEXUS_VideoDecoderHandle videoDecoder;
NEXUS_VideoDecoderStartSettings videoProgram;
#if NEXUS_NUM_HDMI_OUTPUTS
NEXUS_HdmiOutputStatus hdmiStatus;
NEXUS_Error rc;
#endif
NEXUS_AudioMixerSettings audioMixerSettings;
NEXUS_AudioMixerHandle audioMixer;
NEXUS_AudioDecoderHandle audioDecoder;
NEXUS_AudioDecoderStartSettings audioProgram;
NEXUS_PidChannelHandle audioPidChannel;
NEXUS_AudioMuxOutputHandle audioMuxOutput;
NEXUS_AudioMuxOutputDelayStatus audioDelayStatus;
NEXUS_AudioMuxOutputStartSettings audioMuxStartSettings;
NEXUS_PlaypumpHandle playpumpTranscodeAudio;
NEXUS_PidChannelHandle pidChannelTranscodeAudio;
NEXUS_AudioEncoderSettings encoderSettings;
NEXUS_AudioEncoderHandle audioEncoder;
NEXUS_AudioCodec audioCodec;
NEXUS_FilePlayHandle file;
NEXUS_PlaypumpHandle playpump;
NEXUS_PlaybackHandle playback;
NEXUS_PlaybackSettings playbackSettings;
NEXUS_PlaybackPidChannelSettings playbackPidSettings;
NEXUS_DisplayHandle displayTranscode;
NEXUS_VideoWindowHandle windowTranscode;
NEXUS_VideoEncoderHandle videoEncoder;
NEXUS_VideoEncoderSettings videoEncoderConfig;
NEXUS_VideoEncoderStartSettings videoEncoderStartConfig;
NEXUS_VideoEncoderDelayRange videoDelay;
NEXUS_PlaypumpHandle playpumpTranscodeVideo;
NEXUS_PidChannelHandle pidChannelTranscodeVideo;
NEXUS_RecordPidChannelSettings recordPidSettings;
NEXUS_StreamMuxHandle streamMux;
NEXUS_StreamMuxCreateSettings muxCreateSettings;
BKNI_EventHandle finishEvent;
NEXUS_StreamMuxStartSettings muxConfig;
NEXUS_StreamMuxOutput muxOutput;
NEXUS_PlaypumpOpenSettings playpumpConfig;
NEXUS_PlaypumpHandle playpumpTranscodePcr;
NEXUS_FileRecordHandle fileTranscode;
NEXUS_StcChannelHandle stcChannelTranscode;
#if BTST_ENABLE_TS_LAYER_USER_DATA_PASS_THRU
NEXUS_MessageSettings messageSettings;
NEXUS_MessageStartSettings messageStartSettings;
NEXUS_PidChannelHandle pidChannelUserData[2], pidChannelTranscodeUserData[2];
#endif
#if NEXUS_HAS_SYNC_CHANNEL
NEXUS_SyncChannelSettings syncChannelSettings;
NEXUS_SyncChannelHandle syncChannel;
#endif
NEXUS_RecpumpHandle recpump;
NEXUS_RecordHandle record;
NEXUS_RecordSettings recordSettings;
NEXUS_PidChannelHandle pidChannelTranscodePcr;
NEXUS_PidChannelHandle pidChannelTranscodePat;
NEXUS_PidChannelHandle pidChannelTranscodePmt;
void *pat;
void *pmt;
int i = 0;
//int iteration = 1;
//char key;
NEXUS_Platform_GetDefaultSettings(&platformSettings);
platformSettings.openFrontend = false;
NEXUS_Platform_Init(&platformSettings);
NEXUS_Platform_GetConfiguration(&platformConfig);
BDBG_WRN(("Setting up transcode pipeline: format %d, fr %d, bitrate %u, duration %d, codec %d, NEXUS_HAS_SYNC_CHANNEL %x",
cmdSettings[i].displayFormat,
cmdSettings[i].encoderFrameRate,
cmdSettings[i].encoderBitrate,
cmdSettings[i].encoderGopStructureDuration,
cmdSettings[i].encoderVideoCodec,
NEXUS_HAS_SYNC_CHANNEL));
playpump = NEXUS_Playpump_Open(0, NULL);
assert(playpump);
playback = NEXUS_Playback_Create();
assert(playback);
file = NEXUS_FilePlay_OpenPosix(inputFilename, NULL);
if (!file) {
fprintf(stderr, "can't open file: %s\n", inputFilename);
return -1;
}
#if NEXUS_HAS_SYNC_CHANNEL
/* create a sync channel */
NEXUS_SyncChannel_GetDefaultSettings(&syncChannelSettings);
syncChannel = NEXUS_SyncChannel_Create(&syncChannelSettings);
#endif
NEXUS_StcChannel_GetDefaultSettings(0, &stcSettings);
stcSettings.timebase = NEXUS_Timebase_e0;
stcSettings.mode = NEXUS_StcChannelMode_eAuto;
stcChannel = NEXUS_StcChannel_Open(0, &stcSettings);
/* encoders/mux require different STC broadcast mode from decoder */
NEXUS_StcChannel_GetDefaultSettings(1, &stcSettings);
stcSettings.timebase = NEXUS_Timebase_e0;/* should be the same timebase for end-to-end locking */
stcSettings.mode = NEXUS_StcChannelMode_eAuto;
stcSettings.pcrBits = NEXUS_StcChannel_PcrBits_eFull42;/* ViCE2 requires 42-bit STC broadcast */
stcChannelTranscode = NEXUS_StcChannel_Open(1, &stcSettings);
NEXUS_Playback_GetSettings(playback, &playbackSettings);
playbackSettings.playpump = playpump;
/* set a stream format, it could be any audio video transport type or file format, i.e NEXUS_TransportType_eMp4, NEXUS_TransportType_eAvi ... */
playbackSettings.playpumpSettings.transportType = TRANSPORT_TYPE;
playbackSettings.stcChannel = stcChannel;
playbackSettings.endOfStreamCallback.callback = endOfInputStreamCallback;
NEXUS_Playback_SetSettings(playback, &playbackSettings);
/* Bring up video display and outputs */
NEXUS_Display_GetDefaultSettings(&displaySettings);
displaySettings.format = NEXUS_VideoFormat_e480p;
display = NEXUS_Display_Open(0, &displaySettings);
window = NEXUS_VideoWindow_Open(display, 0);
#ifdef NEXUS_NUM_DSP_VIDEO_ENCODERS
NEXUS_VideoWindow_GetMadSettings(window, &windowMadSettings);
windowMadSettings.deinterlace = false;
NEXUS_VideoWindow_SetMadSettings(window, &windowMadSettings);
#endif
#if NEXUS_NUM_COMPONENT_OUTPUTS
NEXUS_Display_AddOutput(display, NEXUS_ComponentOutput_GetConnector(platformConfig.outputs.component[0]));
#endif
#if NEXUS_NUM_HDMI_OUTPUTS
NEXUS_Display_AddOutput(display, NEXUS_HdmiOutput_GetVideoConnector(platformConfig.outputs.hdmi[0]));
rc = NEXUS_HdmiOutput_GetStatus(platformConfig.outputs.hdmi[0], &hdmiStatus);
if ( !rc && hdmiStatus.connected )
{
/* If current display format is not supported by monitor, switch to monitor's preferred format.
If other connected outputs do not support the preferred format, a harmless error will occur. */
NEXUS_Display_GetSettings(display, &displaySettings);
if ( !hdmiStatus.videoFormatSupported[displaySettings.format] ) {
displaySettings.format = hdmiStatus.preferredVideoFormat;
NEXUS_Display_SetSettings(display, &displaySettings);
}
}
#endif
/* app assumes that deinterlacer for main window is enabled by default */
/* Open the audio decoder */
audioDecoder = NEXUS_AudioDecoder_Open(0, NULL);
/* Open the audio and pcr pid channel */
NEXUS_Playback_GetDefaultPidChannelSettings(&playbackPidSettings);
playbackPidSettings.pidSettings.pidType = NEXUS_PidType_eAudio;
playbackPidSettings.pidTypeSettings.audio.primary = audioDecoder; /* must be told codec for correct handling */
audioPidChannel = NEXUS_Playback_OpenPidChannel(playback, streamSettings.audioPid, &playbackPidSettings);
/* Set up decoder Start structures now. We need to know the audio codec to properly set up
the audio outputs. */
NEXUS_AudioDecoder_GetDefaultStartSettings(&audioProgram);
audioProgram.codec = streamSettings.audioCodec;
audioProgram.pidChannel = audioPidChannel;
audioProgram.stcChannel = stcChannel;
/* Connect audio decoders to outputs */
NEXUS_AudioOutput_AddInput(
NEXUS_AudioDac_GetConnector(platformConfig.outputs.audioDacs[0]),
NEXUS_AudioDecoder_GetConnector(audioDecoder, NEXUS_AudioDecoderConnectorType_eStereo));
#if NEXUS_NUM_HDMI_OUTPUTS
NEXUS_AudioOutput_AddInput(
NEXUS_HdmiOutput_GetAudioConnector(platformConfig.outputs.hdmi[0]),
NEXUS_AudioDecoder_GetConnector(audioDecoder, NEXUS_AudioDecoderConnectorType_eStereo));
#endif
/* Open audio mixer. The mixer can be left running at all times to provide continuous audio output despite input discontinuities. */
NEXUS_AudioMixer_GetDefaultSettings(&audioMixerSettings);
audioMixerSettings.mixUsingDsp = true;
audioMixer = NEXUS_AudioMixer_Open(&audioMixerSettings);
assert(audioMixer);
/* Open audio mux output */
audioMuxOutput = NEXUS_AudioMuxOutput_Create(NULL);
assert(audioMuxOutput);
/* Open audio encoder */
NEXUS_AudioEncoder_GetDefaultSettings(&encoderSettings);
encoderSettings.codec = NEXUS_AudioCodec_eAac;
audioCodec = encoderSettings.codec;
audioEncoder = NEXUS_AudioEncoder_Open(&encoderSettings);
assert(audioEncoder);
/* Connect decoder to mixer and set as master */
NEXUS_AudioMixer_AddInput(audioMixer,
NEXUS_AudioDecoder_GetConnector(audioDecoder, NEXUS_AudioDecoderConnectorType_eStereo));
audioMixerSettings.master = NEXUS_AudioDecoder_GetConnector(audioDecoder, NEXUS_AudioDecoderConnectorType_eStereo);
NEXUS_AudioMixer_SetSettings(audioMixer, &audioMixerSettings);
/* Connect mixer to encoder */
NEXUS_AudioEncoder_AddInput(audioEncoder, NEXUS_AudioMixer_GetConnector(audioMixer));
/* Connect mux to encoder */
NEXUS_AudioOutput_AddInput(
NEXUS_AudioMuxOutput_GetConnector(audioMuxOutput), NEXUS_AudioEncoder_GetConnector(audioEncoder));
NEXUS_AudioOutput_AddInput(
NEXUS_AudioDummyOutput_GetConnector(platformConfig.outputs.audioDummy[0]),
NEXUS_AudioMixer_GetConnector(audioMixer));
/* bring up decoder and connect to local display */
videoDecoder = NEXUS_VideoDecoder_Open(0, NULL); /* take default capabilities */
/* NOTE: must open video encoder before display; otherwise open will init ViCE2 core
* which might cause encoder display GISB error since encoder display would
* trigger RDC to program mailbox registers in ViCE2;
*/
videoEncoder = NEXUS_VideoEncoder_Open(0, NULL);
assert(videoEncoder);
/* Bring up video encoder display */
NEXUS_Display_GetDefaultSettings(&displaySettings);
displaySettings.displayType = NEXUS_DisplayType_eAuto;
displaySettings.timingGenerator = NEXUS_DisplayTimingGenerator_eEncoder;
displaySettings.format = cmdSettings[i].displayFormat;/* source is 60hz */
displaySettings.frameRateMaster = NULL;/* disable frame rate tracking for now */
displaySettings.format = cmdSettings[i].displayFormat;
displayTranscode = NEXUS_Display_Open(NEXUS_ENCODER_DISPLAY_IDX, &displaySettings);/* cmp3 for transcoder */
assert(displayTranscode);
windowTranscode = NEXUS_VideoWindow_Open(displayTranscode, 0);
assert(windowTranscode);
#ifdef NEXUS_NUM_DSP_VIDEO_ENCODERS
NEXUS_VideoWindow_GetSettings(windowTranscode, &windowSettings);
windowSettings.position.width = MAX_DSP_ENCODER_WIDTH;
windowSettings.position.height = MAX_DSP_ENCODER_HEIGHT;
windowSettings.pixelFormat = NEXUS_PixelFormat_eCr8_Y18_Cb8_Y08;
windowSettings.visible = false;
NEXUS_VideoWindow_SetSettings(windowTranscode, &windowSettings);
NEXUS_VideoWindow_GetScalerSettings(windowTranscode, &sclSettings);
sclSettings.bandwidthEquationParams.bias = NEXUS_ScalerCaptureBias_eScalerBeforeCapture;
sclSettings.bandwidthEquationParams.delta = 1000000;
NEXUS_VideoWindow_SetScalerSettings(windowTranscode, &sclSettings);
#endif
/* enable deinterlacer to improve quality */
NEXUS_VideoWindow_GetMadSettings(windowTranscode, &windowMadSettings);
windowMadSettings.deinterlace = true;
windowMadSettings.enable22Pulldown = true; /* actually reverse 22 pulldown */
windowMadSettings.enable32Pulldown = true; /* actually reverse 32 pulldown */
NEXUS_VideoWindow_SetMadSettings(windowTranscode, &windowMadSettings);
/* connect same decoder to the encoder display;
* NOTE: simul display + transcode mode might have limitation in audio pathre;
* here is for video transcode bringup purpose;
*/
NEXUS_VideoWindow_AddInput(windowTranscode, NEXUS_VideoDecoder_GetConnector(videoDecoder));
NEXUS_VideoWindow_AddInput(window, NEXUS_VideoDecoder_GetConnector(videoDecoder));
/* Open the video pid channel */
NEXUS_Playback_GetDefaultPidChannelSettings(&playbackPidSettings);
playbackPidSettings.pidSettings.pidType = NEXUS_PidType_eVideo;
playbackPidSettings.pidTypeSettings.video.codec = streamSettings.videoCodec; /* must be told codec for correct handling */
playbackPidSettings.pidTypeSettings.video.index = true;
playbackPidSettings.pidTypeSettings.video.decoder = videoDecoder;
videoPidChannel = NEXUS_Playback_OpenPidChannel(playback, streamSettings.videoPid, &playbackPidSettings);
/* Set up decoder Start structures now. We need to know the audio codec to properly set up
the audio outputs. */
NEXUS_VideoDecoder_GetDefaultStartSettings(&videoProgram);
videoProgram.codec = streamSettings.videoCodec;
videoProgram.pidChannel = videoPidChannel;
videoProgram.stcChannel = stcChannel;
NEXUS_VideoEncoder_GetSettings(videoEncoder, &videoEncoderConfig);
videoEncoderConfig.variableFrameRate = true; /* encoder can detect film content and follow CET */
videoEncoderConfig.frameRate = NEXUS_VideoFrameRate_e30;
videoEncoderConfig.frameRate = cmdSettings[i].encoderFrameRate;
videoEncoderConfig.bitrateMax = cmdSettings[i].encoderBitrate;
videoEncoderConfig.streamStructure.framesP = cmdSettings[i].encoderGopStructureFramesP;
videoEncoderConfig.streamStructure.framesB = cmdSettings[i].encoderGopStructureFramesB;
videoEncoderConfig.streamStructure.duration = cmdSettings[i].encoderGopStructureDuration;
NEXUS_VideoEncoder_GetDefaultStartSettings(&videoEncoderStartConfig);
videoEncoderStartConfig.codec = cmdSettings[i].encoderVideoCodec;
videoEncoderStartConfig.profile = cmdSettings[i].encoderProfile;
videoEncoderStartConfig.level = cmdSettings[i].encoderLevel;
videoEncoderStartConfig.input = displayTranscode;
videoEncoderStartConfig.stcChannel = stcChannelTranscode;
#ifdef NEXUS_NUM_DSP_VIDEO_ENCODERS
videoEncoderStartConfig.bounds.inputDimension.max.width = windowSettings.position.width;
videoEncoderStartConfig.bounds.inputDimension.max.height = windowSettings.position.height;
#else
{ /* set proper encoder interlaced/progressive format. NOTE: fw currently doesn't support dynamic switch between interlaced and progressive. */
#include "nexus_core_utils.h"
NEXUS_VideoFormatInfo fmtInfo;
NEXUS_VideoFormat_GetInfo(cmdSettings[i].displayFormat, &fmtInfo);
videoEncoderStartConfig.interlaced = fmtInfo.interlaced;
}
videoEncoderStartConfig.encodeUserData = true;
/******************************************
* add configurable delay to video path
*/
/* NOTE: ITFP is encoder feature to detect and lock on 3:2/2:2 cadence in the video content to help
* efficient coding for interlaced formats; disabling ITFP will impact the bit efficiency but reduce the encode delay. */
videoEncoderConfig.enableFieldPairing = true;
/* 0 to use default 750ms rate buffer delay; TODO: allow user to adjust it to lower encode delay at cost of quality reduction! */
videoEncoderStartConfig.rateBufferDelay = 0;
/* to allow 23.976p passthru; TODO: allow user to configure minimum framerate to achieve lower delay!
* Note: lower minimum framerate means longer encode delay */
videoEncoderStartConfig.bounds.inputFrameRate.min = NEXUS_VideoFrameRate_e23_976;
/* to allow 24 ~ 60p dynamic frame rate coding TODO: allow user to config higher minimum frame rate for lower delay! */
videoEncoderStartConfig.bounds.outputFrameRate.min = NEXUS_VideoFrameRate_e23_976;
videoEncoderStartConfig.bounds.outputFrameRate.max = NEXUS_VideoFrameRate_e60;
/* max encode size allows 1080p encode; TODO: allow user to choose lower max resolution for lower encode delay */
videoEncoderStartConfig.bounds.inputDimension.max.width = 1920;
videoEncoderStartConfig.bounds.inputDimension.max.height = 1088;
/* encode setting and startSetting to be set after end-to-end delay is determined */
/* get end-to-end delay (Dee) for audio and video encoders;
* TODO: match AV delay! In other words,
* if (aDee > vDee) {
* vDee' = aDee' = aDee;
* }
* else {
* vDee' = aDee' = vDee;
* }
*/
{
unsigned Dee;
/* NOTE: video encoder delay is in 27MHz ticks */
NEXUS_VideoEncoder_GetDelayRange(videoEncoder, &videoEncoderConfig, &videoEncoderStartConfig, &videoDelay);
printf("\n\tVideo encoder end-to-end delay = %u ms; maximum allowed: %u ms\n", videoDelay.min/27000, videoDelay.max/27000);
NEXUS_AudioMuxOutput_GetDelayStatus(audioMuxOutput, audioCodec, &audioDelayStatus);
printf("\tAudio codec %d end-to-end delay = %u ms\n", audioCodec, audioDelayStatus.endToEndDelay);
Dee = audioDelayStatus.endToEndDelay * 27000; /* in 27MHz ticks */
if(Dee > videoDelay.min)
{
if(Dee > videoDelay.max)
{
BDBG_ERR(("\tAudio Dee is way too big! Use video Dee max!"));
Dee = videoDelay.max;
}
else
{
printf("\tUse audio Dee %u ms %u ticks@27Mhz!\n", Dee/27000, Dee);
}
}
else
{
Dee = videoDelay.min;
printf("\tUse video Dee %u ms or %u ticks@27Mhz!\n\n", Dee/27000, Dee);
}
videoEncoderConfig.encoderDelay = Dee;
/* Start audio mux output */
NEXUS_AudioMuxOutput_GetDefaultStartSettings(&audioMuxStartSettings);
audioMuxStartSettings.stcChannel = stcChannelTranscode;
audioMuxStartSettings.presentationDelay = Dee/27000;/* in ms */
NEXUS_AudioMuxOutput_Start(audioMuxOutput, &audioMuxStartSettings);
}
/* Note: video encoder SetSettings needs to be called after the encoder delay is determined; */
NEXUS_VideoEncoder_SetSettings(videoEncoder, &videoEncoderConfig);
NEXUS_Playpump_GetDefaultOpenSettings(&playpumpConfig);
playpumpConfig.fifoSize = 16384; /* reduce FIFO size allocated for playpump */
playpumpConfig.numDescriptors = 64; /* set number of descriptors */
playpumpConfig.streamMuxCompatible = true;
playpumpTranscodeVideo = NEXUS_Playpump_Open(1, &playpumpConfig);
assert(playpumpTranscodeVideo);
#endif
NEXUS_Playpump_GetDefaultOpenSettings(&playpumpConfig);
playpumpConfig.fifoSize = 16384; /* reduce FIFO size allocated for playpump */
playpumpConfig.numDescriptors = 64; /* set number of descriptors */
playpumpConfig.streamMuxCompatible = true;
playpumpTranscodeAudio = NEXUS_Playpump_Open(2, &playpumpConfig);
assert(playpumpTranscodeAudio);
playpumpTranscodePcr = NEXUS_Playpump_Open(3, &playpumpConfig);
assert(playpumpTranscodePcr);
BKNI_CreateEvent(&finishEvent);
NEXUS_StreamMux_GetDefaultCreateSettings(&muxCreateSettings);
muxCreateSettings.finished.callback = transcoderFinishCallback;
muxCreateSettings.finished.context = finishEvent;
streamMux = NEXUS_StreamMux_Create(&muxCreateSettings);
NEXUS_StreamMux_GetDefaultStartSettings(&muxConfig);
muxConfig.transportType = NEXUS_TransportType_eTs;
muxConfig.stcChannel = stcChannelTranscode;
muxConfig.video[0].pid = 0x11;
muxConfig.video[0].encoder = videoEncoder;
muxConfig.video[0].playpump = playpumpTranscodeVideo;
muxConfig.audio[0].pid = 0x12;
muxConfig.audio[0].muxOutput = audioMuxOutput;
muxConfig.audio[0].playpump = playpumpTranscodeAudio;
muxConfig.pcr.pid = 0x13;
muxConfig.pcr.playpump = playpumpTranscodePcr;
muxConfig.pcr.interval = 50;
#if NEXUS_HAS_SYNC_CHANNEL
/* connect sync channel */
NEXUS_SyncChannel_GetSettings(syncChannel, &syncChannelSettings);
syncChannelSettings.videoInput = NEXUS_VideoDecoder_GetConnector(videoDecoder);
syncChannelSettings.audioInput[0] = NEXUS_AudioDecoder_GetConnector(audioDecoder, NEXUS_AudioDecoderConnectorType_eStereo);
NEXUS_SyncChannel_SetSettings(syncChannel, &syncChannelSettings);
#endif
/* Start decoder */
NEXUS_VideoDecoder_Start(videoDecoder, &videoProgram);
NEXUS_AudioDecoder_Start(audioDecoder, &audioProgram);
/* Start playback */
NEXUS_Playback_Start(playback, file, NULL);
recpump = NEXUS_Recpump_Open(0, NULL);
assert(recpump);
record = NEXUS_Record_Create();
assert(record);
NEXUS_Record_GetSettings(record, &recordSettings);
recordSettings.recpump = recpump;
NEXUS_Record_SetSettings(record, &recordSettings);
#if BTST_ENABLE_TS_LAYER_USER_DATA_PASS_THRU
{
unsigned userDataPid[2] = {2501, 2514};
NEXUS_Message_GetDefaultSettings(&messageSettings);
/* SCTE 270 spec max TS VBI user data bitrate=270Kbps, 256KB buffer can hold 7.5 seconds;
worthy user data for video synchronization; TODO: may be reduced if unnecessary */
messageSettings.bufferSize = 256*1024;
messageSettings.overflow.callback = message_callback; /* report overflow error */
messageSettings.overflow.param = 0;
muxConfig.userdata[0].message = NEXUS_Message_Open(&messageSettings);
BDBG_ASSERT(muxConfig.userdata[0].message);
messageSettings.overflow.param = 1;
muxConfig.userdata[1].message = NEXUS_Message_Open(&messageSettings);
BDBG_ASSERT(muxConfig.userdata[1].message);
/* open source user data PID channel */
NEXUS_Playback_GetDefaultPidChannelSettings(&playbackPidSettings);
playbackPidSettings.pidSettings.pidType = NEXUS_PidType_eOther; /* capture the TS packets with the user data PES */
playbackPidSettings.pidSettings.pidSettings.pidChannelIndex = NEXUS_PID_CHANNEL_OPEN_MESSAGE_CAPABLE;
playbackPidSettings.pidSettings.pidSettings.remap.enabled = true;
playbackPidSettings.pidSettings.pidSettings.remap.pid = 0x14;/* optional PID remap */
pidChannelUserData[0] = NEXUS_Playback_OpenPidChannel(playback, userDataPid[0], &playbackPidSettings);
assert(pidChannelUserData[0]);
/* open second user data PID */
playbackPidSettings.pidSettings.pidSettings.remap.pid = 0x15;/* optional PID remap */
pidChannelUserData[1] = NEXUS_Playback_OpenPidChannel(playback, userDataPid[1], &playbackPidSettings);
assert(pidChannelUserData[1]);
NEXUS_Message_GetDefaultStartSettings(muxConfig.userdata[0].message, &messageStartSettings);
messageStartSettings.format = NEXUS_MessageFormat_eTs;
messageStartSettings.pidChannel = pidChannelUserData[0];
/* must start message before stream mux starts */
NEXUS_Message_Start(muxConfig.userdata[0].message, &messageStartSettings);
messageStartSettings.pidChannel = pidChannelUserData[1];
NEXUS_Message_Start(muxConfig.userdata[1].message, &messageStartSettings);
/* open transcode mux output user data PidChannels */
pidChannelTranscodeUserData[0] = NEXUS_Playpump_OpenPidChannel(playpumpTranscodePcr, 0x14, NULL);
assert(pidChannelTranscodeUserData[0]);
pidChannelTranscodeUserData[1] = NEXUS_Playpump_OpenPidChannel(playpumpTranscodePcr, 0x15, NULL);
assert(pidChannelTranscodeUserData[1]);
NEXUS_Record_AddPidChannel(record, pidChannelTranscodeUserData[0], NULL);
NEXUS_Record_AddPidChannel(record, pidChannelTranscodeUserData[1], NULL);
}
#endif
#define BTST_PMT_PID 0x0055
/* open PidChannels */
pidChannelTranscodePcr = NEXUS_Playpump_OpenPidChannel(playpumpTranscodePcr, muxConfig.pcr.pid, NULL);
assert(pidChannelTranscodePcr);
pidChannelTranscodePmt = NEXUS_Playpump_OpenPidChannel(playpumpTranscodePcr, BTST_PMT_PID, NULL);
assert(pidChannelTranscodePmt);
pidChannelTranscodePat = NEXUS_Playpump_OpenPidChannel(playpumpTranscodePcr, 0, NULL);
assert(pidChannelTranscodePat);
/* start mux */
NEXUS_StreamMux_Start(streamMux,&muxConfig, &muxOutput);
pidChannelTranscodeVideo = muxOutput.video[0];
pidChannelTranscodeAudio = muxOutput.audio[0];
/* configure the video pid for indexing */
NEXUS_Record_GetDefaultPidChannelSettings(&recordPidSettings);
recordPidSettings.recpumpSettings.pidType = NEXUS_PidType_eVideo;
recordPidSettings.recpumpSettings.pidTypeSettings.video.index = true;
recordPidSettings.recpumpSettings.pidTypeSettings.video.codec = cmdSettings[i].encoderVideoCodec;
/* add multiplex data to the same record */
NEXUS_Record_AddPidChannel(record, pidChannelTranscodeVideo, &recordPidSettings);
NEXUS_Record_AddPidChannel(record, pidChannelTranscodeAudio, NULL);
NEXUS_Record_AddPidChannel(record, pidChannelTranscodePcr, NULL);
NEXUS_Record_AddPidChannel(record, pidChannelTranscodePat, NULL);
NEXUS_Record_AddPidChannel(record, pidChannelTranscodePmt, NULL);
fileTranscode = NEXUS_FileRecord_OpenPosix(outputFilename, "/var/media/videos/transcoded.nav");
assert(fileTranscode);
/* Start record of stream mux output */
NEXUS_Record_Start(record, fileTranscode);
{
static const uint8_t s_auiTSPacket_PAT[188] =
{
0x47,0x40,0x00,0x30,0xa6,0x40,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,'P', 'A', 'T',
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0xb0,0x0d,0x00,
0x00,0x81,0x00,0x00,0x00,0x01,0xe0,0x55,
0x65,0x80,0x5e,0xdc,
};
#define BTST_PMT_TRANSPORT_IDX BTST_SYSTEM_TRANSPORT_IDX
static const uint8_t s_auiTSPacket_PMT[188] =
{
0x47,0x40,BTST_PMT_PID,0x30,
#if BTST_ENABLE_TS_LAYER_USER_DATA_PASS_THRU
0x66,/* adaptation field length */
#else
0x9c,/* adaptation field length */
#endif
0x40,/* AF flags */
0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,'P', 'M', 'T',
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,
/* placeholder for VBI user data PMT section and descriptors */
0x00,/* pointer */
0x02,0xb0,0x4d,0x00,0x01,0xc1,0x00,0x00,0xe0,0x13,0xf0,0x00,
/* VBI user data PID */
0x06,0xe0,0x14,0xf0,0x19,
0x52,0x01,0x0e,0x56,0x14,0x64,0x75,0x74,
0x10,0x88,0x6e,0x6f,0x72,0x17,0x77,0x64,
0x75,0x74,0x08,0x88,0x73,0x77,0x65,0x16,
0x91,
/* VBI user data PID */
0x06,0xe0,0x15,0xf0,0x13,
0x0a,0x04,0x73,0x77,0x65,0x00,
#if BTST_ENABLE_TS_LAYER_USER_DATA_PASS_THRU
0x52,0x01,
0x12,0x59,0x08,0x73,0x77,0x65,0x10,0x00,
0x02,0x00,0x02,
#else
0x00,/* pointer */
0x02,0xb0,0x17,0x00,0x01,0xc1,0x00,0x00,0xe0,0x13,0xf0,0x00,
#endif
0x1b,0xe0,0x11,0xf0,0x00,/* video */
0x81,0xe0,0x12,0xf0,0x00,/* audio */
0x3d,0x19,0x07,0x2f
};
NEXUS_StreamMuxSystemData psi[2];
/* Get CRC right to be playable by VLCplayer etc 3rd party SW */
uint32_t uiCRC = CRC32_mpeg(0, (uint8_t *) s_auiTSPacket_PAT + 184 - (8+4*1), 8+4*1);
fprintf(stderr, "PAT crc=%x\n", uiCRC);
NEXUS_Memory_Allocate(188, NULL, &pat);
NEXUS_Memory_Allocate(188, NULL, &pmt);
BKNI_Memcpy(pat, s_auiTSPacket_PAT, sizeof(s_auiTSPacket_PAT));
BKNI_Memcpy(pmt, s_auiTSPacket_PMT, sizeof(s_auiTSPacket_PMT));
((uint8_t*)pat)[184] = (uiCRC >> 24) & 0xFF;
((uint8_t*)pat)[185] = (uiCRC >> 16) & 0xFF;
((uint8_t*)pat)[186] = (uiCRC >> 8) & 0xFF;
((uint8_t*)pat)[187] = (uiCRC >> 0) & 0xFF;
/* video codec */
switch(videoEncoderStartConfig.codec)
{
case NEXUS_VideoCodec_eMpeg2: ((uint8_t *) pmt)[184-2*5] = 0x2; break;
case NEXUS_VideoCodec_eMpeg4Part2: ((uint8_t *) pmt)[184-2*5] = 0x10; break;
case NEXUS_VideoCodec_eH264: ((uint8_t *) pmt)[184-2*5] = 0x1b; break;
case NEXUS_VideoCodec_eVc1SimpleMain: ((uint8_t *) pmt)[184-2*5] = 0xea; break;
default:
BDBG_ERR(("Video encoder codec %d is not supported!\n", videoEncoderStartConfig.codec));
BDBG_ASSERT(0);
}
/* audio stream type */
switch(audioCodec)
{
case NEXUS_AudioCodec_eMpeg: ((uint8_t *) pmt)[184-5] = 0x4; break;
case NEXUS_AudioCodec_eMp3: ((uint8_t *) pmt)[184-5] = 0x4; break;
case NEXUS_AudioCodec_eAac : ((uint8_t *) pmt)[184-5] = 0xf; break; /* ADTS */
case NEXUS_AudioCodec_eAacPlus: ((uint8_t *) pmt)[184-5] = 0x11; break;/* LOAS */
/* MP2TS doesn't allow 14496-3 AAC+ADTS; here is placeholder to test AAC-HE before LOAS encode is supported; */
case NEXUS_AudioCodec_eAacPlusAdts: ((uint8_t *) pmt)[184-5] = 0x11; break;
case NEXUS_AudioCodec_eAc3: ((uint8_t *) pmt)[184-5] = 0x81; break;
default:
BDBG_ERR(("Audio encoder codec %d is not supported!\n", audioCodec));
}
/* A+V two ES */
#if BTST_ENABLE_TS_LAYER_USER_DATA_PASS_THRU
uiCRC = CRC32_mpeg(0, (uint8_t *) pmt + 184 - (12+5*2)-54, 12+5*2+54);
#else
uiCRC = CRC32_mpeg(0, (uint8_t *) pmt + 184 - (12+5*2), 12+5*2);
#endif
fprintf(stderr, "PMT crc=%x\n", uiCRC);
((uint8_t*)pmt)[184] = (uiCRC >> 24) & 0xFF;
((uint8_t*)pmt)[185] = (uiCRC >> 16) & 0xFF;
((uint8_t*)pmt)[186] = (uiCRC >> 8) & 0xFF;
((uint8_t*)pmt)[187] = (uiCRC >> 0) & 0xFF;
NEXUS_Memory_FlushCache(pat, sizeof(s_auiTSPacket_PAT));
NEXUS_Memory_FlushCache(pmt, sizeof(s_auiTSPacket_PMT));
BKNI_Memset(psi, 0, sizeof(psi));
psi[0].size = 188;
psi[0].pData = pat;
psi[0].timestampDelta = 100;
psi[1].size = 188;
psi[1].pData = pmt;
psi[1].timestampDelta = 100;
NEXUS_StreamMux_AddSystemDataBuffer(streamMux, &psi[0]);
NEXUS_StreamMux_AddSystemDataBuffer(streamMux, &psi[1]);
}
NEXUS_VideoEncoder_Start(videoEncoder, &videoEncoderStartConfig);
if (duration == 0) {
BDBG_WRN(("\n\nTranscoding, press any key to stop early...\n"));
} else {
BDBG_WRN(("\n\nTranscoding for %d seconds, press any key to stop early...\n", duration));
}
waitForTranscodeComplete();
/* Done, bring down system */
NEXUS_Playback_Stop(playback);
NEXUS_VideoDecoder_Stop(videoDecoder);
NEXUS_AudioDecoder_Stop(audioDecoder);
NEXUS_AudioMixer_Stop(audioMixer);
NEXUS_AudioMuxOutput_Stop(audioMuxOutput);
#if NEXUS_HAS_SYNC_CHANNEL
/* disconnect sync channel */
NEXUS_SyncChannel_GetSettings(syncChannel, &syncChannelSettings);
syncChannelSettings.videoInput = NULL;
syncChannelSettings.audioInput[0] = NULL;
syncChannelSettings.audioInput[1] = NULL;
NEXUS_SyncChannel_SetSettings(syncChannel, &syncChannelSettings);
#endif
NEXUS_VideoEncoder_Stop(videoEncoder, NULL);
NEXUS_StreamMux_Finish(streamMux);
// wait for the encoder buffer model's data to be drained //
if(BKNI_WaitForEvent(finishEvent, (videoEncoderConfig.encoderDelay/27000)*2)!=BERR_SUCCESS) {
fprintf(stderr, "TIMEOUT\n");
}
BKNI_DestroyEvent(finishEvent);
NEXUS_Record_Stop(record);
/* Note: remove all record PID channels before stream mux stop since streammux would close the A/V PID channels */
NEXUS_Record_RemoveAllPidChannels(record);
NEXUS_StreamMux_Stop(streamMux);
#if BTST_ENABLE_TS_LAYER_USER_DATA_PASS_THRU /* stop message after stream mux stops */
NEXUS_Message_Stop(muxConfig.userdata[0].message);
NEXUS_Message_Close(muxConfig.userdata[0].message);
NEXUS_Message_Stop(muxConfig.userdata[1].message);
NEXUS_Message_Close(muxConfig.userdata[1].message);
#endif
NEXUS_Record_Destroy(record);
NEXUS_Recpump_Close(recpump);
NEXUS_FileRecord_Close(fileTranscode);
NEXUS_Playback_CloseAllPidChannels(playback);
NEXUS_Playpump_CloseAllPidChannels(playpumpTranscodePcr);
NEXUS_FilePlay_Close(file);
NEXUS_Playback_Destroy(playback);
NEXUS_Playpump_Close(playpump);
NEXUS_VideoWindow_RemoveInput(window, NEXUS_VideoDecoder_GetConnector(videoDecoder));
NEXUS_VideoWindow_RemoveInput(windowTranscode, NEXUS_VideoDecoder_GetConnector(videoDecoder));
NEXUS_VideoInput_Shutdown(NEXUS_VideoDecoder_GetConnector(videoDecoder));
NEXUS_VideoDecoder_Close(videoDecoder);
NEXUS_VideoWindow_Close(window);
NEXUS_VideoWindow_Close(windowTranscode);
NEXUS_Display_Close(display);
NEXUS_Display_Close(displayTranscode);
NEXUS_StreamMux_Destroy(streamMux);
NEXUS_Playpump_Close(playpumpTranscodePcr);
NEXUS_Playpump_Close(playpumpTranscodeVideo);
NEXUS_VideoEncoder_Close(videoEncoder);
NEXUS_AudioOutput_RemoveAllInputs(NEXUS_AudioMuxOutput_GetConnector(audioMuxOutput));
NEXUS_AudioEncoder_RemoveAllInputs(audioEncoder);
NEXUS_AudioInput_Shutdown(NEXUS_AudioEncoder_GetConnector(audioEncoder));
NEXUS_AudioEncoder_Close(audioEncoder);
NEXUS_AudioMixer_RemoveAllInputs(audioMixer);
NEXUS_AudioOutput_RemoveAllInputs(NEXUS_AudioDummyOutput_GetConnector(platformConfig.outputs.audioDummy[0]));
NEXUS_AudioOutput_Shutdown(NEXUS_AudioDummyOutput_GetConnector(platformConfig.outputs.audioDummy[0]));
NEXUS_AudioInput_Shutdown(NEXUS_AudioMixer_GetConnector(audioMixer));
NEXUS_AudioMixer_Close(audioMixer);
NEXUS_AudioOutput_RemoveAllInputs(NEXUS_AudioDac_GetConnector(platformConfig.outputs.audioDacs[0]));
NEXUS_AudioOutput_RemoveAllInputs(NEXUS_SpdifOutput_GetConnector(platformConfig.outputs.spdif[0]));
#if NEXUS_NUM_HDMI_OUTPUTS
NEXUS_AudioOutput_RemoveAllInputs(NEXUS_HdmiOutput_GetAudioConnector(platformConfig.outputs.hdmi[0]));
#endif
NEXUS_Playpump_Close(playpumpTranscodeAudio);
NEXUS_AudioOutput_Shutdown(NEXUS_AudioMuxOutput_GetConnector(audioMuxOutput));
NEXUS_AudioMuxOutput_Destroy(audioMuxOutput);
NEXUS_AudioInput_Shutdown(NEXUS_AudioDecoder_GetConnector(audioDecoder, NEXUS_AudioDecoderConnectorType_eStereo));
NEXUS_AudioOutput_Shutdown(NEXUS_AudioDac_GetConnector(platformConfig.outputs.audioDacs[0]));
NEXUS_AudioOutput_Shutdown(NEXUS_SpdifOutput_GetConnector(platformConfig.outputs.spdif[0]));
NEXUS_AudioDecoder_Close(audioDecoder);
#if NEXUS_HAS_SYNC_CHANNEL
NEXUS_SyncChannel_Destroy(syncChannel);
#endif
NEXUS_StcChannel_Close(stcChannel);
NEXUS_StcChannel_Close(stcChannelTranscode);
NEXUS_Memory_Free(pat);
NEXUS_Memory_Free(pmt);
NEXUS_Platform_Uninit();
return 0;
}
//#endif