Fastcgi wrapper not working with root permisions - wrapper

i have problem setting fastcgi wrapper to execute commands from web as root
only settings i have changed
FCGI_USER="root"
FCGI_GROUP="root"
[....] Starting FastCGI wrapper: fcgiwrapspawn-fcgi: I will not set uid to 0
failed!
i want to manipulate with gpio through web with wiringPi, but wiringPisetup requires root access
any ideas?

spawn-fcgi (https://github.com/lighttpd/spawn-fcgi) is the problem.
Apply this patch to src/spawn-fcgi.c and recompile spawn-fcgi. In my case I wanted to run nmap -sS which requires root. It really grinds my gears when developers decide to make running as root impossible because it's just a waste of everyone's time. Always add a flag, call it --super-insecure if you want to but at least put it there!
diff --git a/src/spawn-fcgi.c b/src/spawn-fcgi.c
index 2f320f7..27176d5 100644
--- a/src/spawn-fcgi.c
+++ b/src/spawn-fcgi.c
## -385,10 +385,10 ## static int find_user_group(const char *user, const char *group, uid_t *uid, gid_
}
my_uid = my_pwd->pw_uid;
- if (my_uid == 0) {
+ /*if (my_uid == 0) {
fprintf(stderr, "spawn-fcgi: I will not set uid to 0\n");
return -1;
- }
+ }*/
if (username) *username = user;
} else {
## -407,18 +407,18 ## static int find_user_group(const char *user, const char *group, uid_t *uid, gid_
}
my_gid = my_grp->gr_gid;
- if (my_gid == 0) {
+ /*if (my_gid == 0) {
fprintf(stderr, "spawn-fcgi: I will not set gid to 0\n");
return -1;
- }
+ }*/
}
} else if (my_pwd) {
my_gid = my_pwd->pw_gid;
- if (my_gid == 0) {
+ /*if (my_gid == 0) {
fprintf(stderr, "spawn-fcgi: I will not set gid to 0\n");
return -1;
- }
+ }*/
}
*uid = my_uid;

Related

Dovecot Failed to init dict: dict redis: Invalid IP

I'm using Dovecot v2.3.9.3. By looking to the dovecot docs I see that specification for Redis is the following:
redis:param=value:param2=value2:...
Here one of the parameters is host: Redis server host (default: 127.0.0.1).
In my configuration I have specified uri = redis:host=redis:port=6379:
Feb 23 20:48:32 auth: Fatal: dict /etc/dovecot/dovecot-dict-auth.conf.ext: Failed to init dict: dict redis: Invalid IP: redis
redis hostname on my server resolves to the IP without any problem:
# getent hosts redis
192.168.48.2 redis redis
Is there a way to use hostname (maybe some hidden setting which enables the resolution), or they just bluntly did not implement a support for that? :/
TL;DR
Dovecot redis dict driver accepts undocumented parameter path in which you can specify the unix socket. You may then create a proxy to the hostname redis serving on the tcp port 6379 via the unix socket /run/redis.soc:
socat unix-listen:/run/redis.soc,reuseaddr,fork,perm=0644,user=dovecot tcp:redis:6379 &
Dovecot config becomes:
# Dictionary URI
uri = redis:path=/run/redis.soc
The following is my analysis of the problem. I don't code in C, so my understanding was limited.
Code which covers my error (Invalid IP: redis) is the following:
} else if (str_begins(*args, "host=")) {
if (net_addr2ip(*args+5, &ip) < 0) {
*error_r = t_strdup_printf("Invalid IP: %s",
*args+5);
ret = -1;
}
}
It relies on the net_addr2ip function which depends on the net_addr2ip_inet4_fast. Both of these functions do not seem to do, what their names are suggesting (they do not turn addr to ip):
static bool net_addr2ip_inet4_fast(const char *addr, struct ip_addr *ip)
{
uint8_t *saddr = (void *)&ip->u.ip4.s_addr;
unsigned int i, num;
if (str_parse_uint(addr, &num, &addr) < 0)
return FALSE;
if (*addr == '\0' && num <= 0xffffffff) {
/* single-number IPv4 address */
ip->u.ip4.s_addr = htonl(num);
ip->family = AF_INET;
return TRUE;
}
/* try to parse as a.b.c.d */
i = 0;
for (;;) {
if (num >= 256)
return FALSE;
saddr[i] = num;
if (i == 3)
break;
i++;
if (*addr != '.')
return FALSE;
addr++;
if (str_parse_uint(addr, &num, &addr) < 0)
return FALSE;
}
if (*addr != '\0')
return FALSE;
ip->family = AF_INET;
return TRUE;
}
int net_addr2ip(const char *addr, struct ip_addr *ip)
{
int ret;
if (net_addr2ip_inet4_fast(addr, ip))
return 0;
if (strchr(addr, ':') != NULL) {
/* IPv6 */
T_BEGIN {
if (addr[0] == '[') {
/* allow [ipv6 addr] */
size_t len = strlen(addr);
if (addr[len-1] == ']')
addr = t_strndup(addr+1, len-2);
}
ret = inet_pton(AF_INET6, addr, &ip->u.ip6);
} T_END;
if (ret == 0)
return -1;
ip->family = AF_INET6;
} else {
/* IPv4 */
if (inet_aton(addr, &ip->u.ip4) == 0)
return -1;
ip->family = AF_INET;
}
return 0;
}
Therefore host parameter in dovecot's redis dict driver cannot be anything else but an IP address ¯_(ツ)_/¯

How to redirect console output into a file in VxWorks shell?

In Windows or Linux, it's very often we redirect console output to a file as below:
Windows:
dir > text
Linux:
ls -l > text
I wonder how to do the similar thing in VxWorks shell.
You can try something like this:
-> saveFd = open("myfile.txt",0x102, 0777 )
-> oldFd = ioGlobalStdGet(1)
-> ioGlobalStdSet(1, saveFd)
-> runmytest()
...
-> ioGlobalStdSet(1, oldFd)
This will redirect all code to the file that you have opened, this case myfile.txt
I changed "0x102" in "saveFd = open("myfile.txt",0x102, 0777 )" into "0x202", then it works. All console display was redirected into "myfile.txt".
In previous post, I got a mistake. I thought it hung after "ioGlobalStdSet(1, saveFd)". It's not hung, but redirecting all display into "myfile.txt" and I used "CTL-C" to stop the redirection.
The following code snippets show how to save all serial console output to a file.
Try -> tyco0_write_to_file 4096
#include <tyLib.h>
#include <private/iosLibP.h>
int tyco0_log_max_size = -1;
int tyco0_log_fd = -1;
int tyco0_write_hook(TY_DEV_ID pTyDev, char *buffer, int nbytes)
{
FD_ENTRY *p_fd_entry;
static int bytes_written = 0;
if ('0' == pTyDev->devHdr.name[6]) { /* /tyCo/0 */
if ((bytes_written+nbytes)<tyco0_log_max_size) {
(void)write(tyco0_log_fd,buffer,nbytes);
bytes_written += nbytes;
}
else {
(void)write(tyco0_log_fd,buffer,tyco0_log_max_size-bytes_written);
p_fd_entry = iosFdMap(1);
p_fd_entry->pDrvEntry->de_write = tyWrite;
close(tyco0_log_fd);
bytes_written = 0;
}
}
return tyWrite(pTyDev,buffer,nbytes);
}
int tyco0_write_to_file(int file_max_size)
{
FD_ENTRY *p_fd_entry;
p_fd_entry = iosFdMap(1); /* /tyCo/0 */
if (NULL==p_fd_entry) {
perror("iosFdMap");
return -1;
}
tyco0_log_fd = open("/ram/tyco0.log",O_CREAT|O_RDWR,0);
if (tyco0_log_fd == -1) {
perror("open");
return -1;
}
if (file_max_size <= 0) {
file_max_size = 1024;
}
tyco0_log_max_size = file_max_size;
if (p_fd_entry->pDrvEntry->de_write == tyWrite) {
p_fd_entry->pDrvEntry->de_write = tyco0_write_hook;
}
else {
printf("tyWrite not found\n");
close(tyco0_log_fd);
return -1;
}
return 0;
}

OpenLDAP - Enabling CRL check for LDAP TLS connections

I have a client that connects to LDAP server using TLS. For this connection, I want to enable CRL check and reject the connection only if any server/client certificates are revoked.
In special cases (like CRL missing, CRL expired) I want to ignore the error and establish the connection.
So I though to overwrite the default SSL verify call back to ignore the specific errors.
But the call back is not called at all. Always only default call-back is called.
Here is my call back:
static int verify_callback(int ok, X509_STORE_CTX *ctx)
{
X509* cert = X509_STORE_CTX_get_current_cert(ctx);
if (ok)
return ok;
int sslRet = X509_STORE_CTX_get_error(ctx);
const char* err = NULL;
switch (sslRet)
{
case X509_V_ERR_UNABLE_TO_GET_CRL:
case X509_V_ERR_CRL_HAS_EXPIRED:
case X509_V_ERR_CRL_NOT_YET_VALID:
printf( "CRL: Verification failed... but ignored : %d\n", sslRet);
return 1;
default:
err = X509_verify_cert_error_string(sslRet);
if (err)
printf( "CRL: Failed to verify : %s\n",err);
return 0;
}
return sslRet;
}
Default verify call-back is overwritten using the ldap call-back set option:
void ldap_tls_cb(LDAP * ld, SSL * ssl, SSL_CTX * ctx, void * arg)
{
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER , verify_callback);
printf("verify call back is set...\n");
return;
}
Main Program:
int main( int argc, char **argv )
{
LDAP *ldap;
int auth_method = LDAP_AUTH_SIMPLE; //LDAP_AUTH_SASL
int ldap_version = LDAP_VERSION3;
char *ldap_host = "10.104.40.35";
int ldap_port = 389;
if ( (ldap = ldap_init(ldap_host, ldap_port)) == NULL ) {
perror( "ldap_init failed" );
return( EXIT_FAILURE );
}
int result = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option failed!");
return(EXIT_FAILURE);
}
int requireCert = LDAP_OPT_X_TLS_DEMAND;
result = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &requireCert);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option - req cert -failed!");
return(EXIT_FAILURE);
}
result = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, "/etc/certs/Cert.pem");
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option - cert file - failed!");
return(EXIT_FAILURE);
}
int crlvalue = LDAP_OPT_X_TLS_CRL_ALL;
result =ldap_set_option(NULL, LDAP_OPT_X_TLS_CRLCHECK, &crlvalue);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option failed!");
return(EXIT_FAILURE);
}
int debug = 7;
ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug);
result = ldap_set_option(ldap, LDAP_OPT_X_TLS_CONNECT_CB, (void *)ldap_tls_cb);
if (result != LDAP_SUCCESS) {
fprintf(stderr, "ldap_set_option(LDAP_OPT_X_TLS_CONNECT_CB): %s\n", ldap_err2string(result));
return(1);
}
int msgidp = 0;
result = ldap_start_tls(ldap,NULL,NULL,&msgidp);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "start tls failed!");
return result;
} else {
printf("Start tls success.\n");
}
LDAPMessage *resultm;
struct timeval timeout;
result = ldap_result(ldap, msgidp, 0, &timeout, &resultm );
if ( result == -1 || result == 0 ) {
printf("ldap_result failed;retC=%d \n", result);
return result;
}
result = ldap_parse_extended_result(ldap, resultm, NULL, NULL, 0 );
if ( result == LDAP_SUCCESS ) {
result = ldap_install_tls (ldap);
printf("installing tls... %s\n", ldap_err2string(result));
}
int request_id = 0;
result = ldap_sasl_bind(ldap, "", LDAP_SASL_SIMPLE, NULL, 0, 0, &request_id);
if ( result != LDAP_SUCCESS ) {
fprintf(stderr, "ldap_x_bind_s: %s\n", ldap_err2string(result));
printf("LDAP bind error .. %d\n", result);
return(EXIT_FAILURE);
} else {
printf("LDAP connection successful.\n");
}
ldap_unbind(ldap);
return(EXIT_SUCCESS);
}
can someone help to check why my verify call-back is not called?
I think you need to set the callback on the SSL object directly instead of the context, so
void ldap_tls_cb(LDAP * ld, SSL * ssl, SSL_CTX * ctx, void * arg)
{
SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_callback);
printf("verify call back is set...\n");
return;
}
The reason for this is that the SSL handle has already been initialised by the time your connect callback is called (see the OpenLDAP code), and
it's too late to set this callback through the context at that point:
If no special callback was set before, the default callback for the underlying ctx is used, that was valid at the time ssl was created with SSL_new(3).
OpenLDAP can be built with GnuTLS, so you may need to check that it's using OpenSSL before setting the callback. The LDAP_OPT_X_TLS_PACKAGE option could be used for this (note that I haven't tested this code):
char* package = NULL;
int result = ldap_get_option(NULL, LDAP_OPT_X_TLS_PACKAGE, (void *)&package);
if (result != LDAP_OPT_SUCCESS) {
ldap_perror(ldap, "ldap_get_option failed!");
return(EXIT_FAILURE);
} else {
if (strcmp(package, "OpenSSL") == 0) {
// Set your callback
}
ldap_memfree(package);
}

Record rtsp stream with ffmpeg in iOS

I've followed iFrameExtractor to successfully stream rtsp in my swift project. In this project, it also has recording function. It basically use avformat_write_header
, av_interleaved_write_frame and av_write_trailer to save the rtsp source into mp4 file.
When I used this project in my device, the rtsp streaming works fine, but recording function will always generate a blank mp4 file with no image and sound.
Could anyone tell me what step that I miss?
I'm using iPhone5 with iOS 9.1 and XCode 7.1.1.
The ffmpeg is 2.8.3 version and followed the compile instruction by CompilationGuide – FFmpeg
Following is the sample code in this project
The function that generate every frame:
-(BOOL)stepFrame {
// AVPacket packet;
int frameFinished=0;
static bool bFirstIFrame=false;
static int64_t vPTS=0, vDTS=0, vAudioPTS=0, vAudioDTS=0;
while(!frameFinished && av_read_frame(pFormatCtx, &packet)>=0) {
// Is this a packet from the video stream?
if(packet.stream_index==videoStream) {
// 20130525 albert.liao modified start
// Initialize a new format context for writing file
if(veVideoRecordState!=eH264RecIdle)
{
switch(veVideoRecordState)
{
case eH264RecInit:
{
if ( !pFormatCtx_Record )
{
int bFlag = 0;
//NSString *videoPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/test.mp4"];
NSString *videoPath = #"/Users/liaokuohsun/iFrameTest.mp4";
const char *file = [videoPath UTF8String];
pFormatCtx_Record = avformat_alloc_context();
bFlag = h264_file_create(file, pFormatCtx_Record, pCodecCtx, pAudioCodecCtx,/*fps*/0.0, packet.data, packet.size );
if(bFlag==true)
{
veVideoRecordState = eH264RecActive;
fprintf(stderr, "h264_file_create success\n");
}
else
{
veVideoRecordState = eH264RecIdle;
fprintf(stderr, "h264_file_create error\n");
}
}
}
//break;
case eH264RecActive:
{
if((bFirstIFrame==false) &&(packet.flags&AV_PKT_FLAG_KEY)==AV_PKT_FLAG_KEY)
{
bFirstIFrame=true;
vPTS = packet.pts ;
vDTS = packet.dts ;
#if 0
NSRunLoop *pRunLoop = [NSRunLoop currentRunLoop];
[pRunLoop addTimer:RecordingTimer forMode:NSDefaultRunLoopMode];
#else
[NSTimer scheduledTimerWithTimeInterval:5.0//2.0
target:self
selector:#selector(StopRecording:)
userInfo:nil
repeats:NO];
#endif
}
// Record audio when 1st i-Frame is obtained
if(bFirstIFrame==true)
{
if ( pFormatCtx_Record )
{
#if PTS_DTS_IS_CORRECT==1
packet.pts = packet.pts - vPTS;
packet.dts = packet.dts - vDTS;
#endif
h264_file_write_frame( pFormatCtx_Record, packet.stream_index, packet.data, packet.size, packet.dts, packet.pts);
}
else
{
NSLog(#"pFormatCtx_Record no exist");
}
}
}
break;
case eH264RecClose:
{
if ( pFormatCtx_Record )
{
h264_file_close(pFormatCtx_Record);
#if 0
// 20130607 Test
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
{
ALAssetsLibrary *library = [[ALAssetsLibrary alloc]init];
NSString *filePathString = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/test.mp4"];
NSURL *filePathURL = [NSURL fileURLWithPath:filePathString isDirectory:NO];
if(1)// ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:filePathURL])
{
[library writeVideoAtPathToSavedPhotosAlbum:filePathURL completionBlock:^(NSURL *assetURL, NSError *error){
if (error) {
// TODO: error handling
NSLog(#"writeVideoAtPathToSavedPhotosAlbum error");
} else {
// TODO: success handling
NSLog(#"writeVideoAtPathToSavedPhotosAlbum success");
}
}];
}
[library release];
});
#endif
vPTS = 0;
vDTS = 0;
vAudioPTS = 0;
vAudioDTS = 0;
pFormatCtx_Record = NULL;
NSLog(#"h264_file_close() is finished");
}
else
{
NSLog(#"fc no exist");
}
bFirstIFrame = false;
veVideoRecordState = eH264RecIdle;
}
break;
default:
if ( pFormatCtx_Record )
{
h264_file_close(pFormatCtx_Record);
pFormatCtx_Record = NULL;
}
NSLog(#"[ERROR] unexpected veVideoRecordState!!");
veVideoRecordState = eH264RecIdle;
break;
}
}
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
}
else if(packet.stream_index==audioStream)
{
// 20131024 albert.liao modfied start
static int vPktCount=0;
BOOL bIsAACADTS = FALSE;
int ret = 0;
if(aPlayer.vAACType == eAAC_UNDEFINED)
{
tAACADTSHeaderInfo vxAACADTSHeaderInfo = {0};
bIsAACADTS = [AudioUtilities parseAACADTSHeader:(uint8_t *)packet.data ToHeader:&vxAACADTSHeaderInfo];
}
#synchronized(aPlayer)
{
if(aPlayer==nil)
{
aPlayer = [[AudioPlayer alloc]initAudio:nil withCodecCtx:(AVCodecContext *) pAudioCodecCtx];
NSLog(#"aPlayer initAudio");
if(bIsAACADTS)
{
aPlayer.vAACType = eAAC_ADTS;
//NSLog(#"is ADTS AAC");
}
}
else
{
if(vPktCount<5) // The voice is listened once image is rendered
{
vPktCount++;
}
else
{
if([aPlayer getStatus]!=eAudioRunning)
{
dispatch_async(dispatch_get_main_queue(), ^(void) {
#synchronized(aPlayer)
{
NSLog(#"aPlayer start play");
[aPlayer Play];
}
});
}
}
}
};
#synchronized(aPlayer)
{
int ret = 0;
ret = [aPlayer putAVPacket:&packet];
if(ret <= 0)
NSLog(#"Put Audio Packet Error!!");
}
// 20131024 albert.liao modfied end
if(bFirstIFrame==true)
{
switch(veVideoRecordState)
{
case eH264RecActive:
{
if ( pFormatCtx_Record )
{
h264_file_write_audio_frame(pFormatCtx_Record, pAudioCodecCtx, packet.stream_index, packet.data, packet.size, packet.dts, packet.pts);
}
else
{
NSLog(#"pFormatCtx_Record no exist");
}
}
}
}
}
else
{
//fprintf(stderr, "packet len=%d, Byte=%02X%02X%02X%02X%02X\n",\
packet.size, packet.data[0],packet.data[1],packet.data[2],packet.data[3], packet.data[4]);
}
// 20130525 albert.liao modified end
}
return frameFinished!=0;
}
avformat_write_header:
int h264_file_create(const char *pFilePath, AVFormatContext *fc, AVCodecContext *pCodecCtx,AVCodecContext *pAudioCodecCtx, double fps, void *p, int len )
{
int vRet=0;
AVOutputFormat *of=NULL;
AVStream *pst=NULL;
AVCodecContext *pcc=NULL, *pAudioOutputCodecContext=NULL;
avcodec_register_all();
av_register_all();
av_log_set_level(AV_LOG_VERBOSE);
if(!pFilePath)
{
fprintf(stderr, "FilePath no exist");
return -1;
}
if(!fc)
{
fprintf(stderr, "AVFormatContext no exist");
return -1;
}
fprintf(stderr, "file=%s\n",pFilePath);
// Create container
of = av_guess_format( 0, pFilePath, 0 );
fc->oformat = of;
strcpy( fc->filename, pFilePath );
// Add video stream
pst = avformat_new_stream( fc, 0 );
vVideoStreamIdx = pst->index;
fprintf(stderr,"Video Stream:%d",vVideoStreamIdx);
pcc = pst->codec;
avcodec_get_context_defaults3( pcc, AVMEDIA_TYPE_VIDEO );
// Save the stream as origin setting without convert
pcc->codec_type = pCodecCtx->codec_type;
pcc->codec_id = pCodecCtx->codec_id;
pcc->bit_rate = pCodecCtx->bit_rate;
pcc->width = pCodecCtx->width;
pcc->height = pCodecCtx->height;
if(fps==0)
{
double fps=0.0;
AVRational pTimeBase;
pTimeBase.num = pCodecCtx->time_base.num;
pTimeBase.den = pCodecCtx->time_base.den;
fps = 1.0/ av_q2d(pCodecCtx->time_base)/ FFMAX(pCodecCtx->ticks_per_frame, 1);
fprintf(stderr,"fps_method(tbc): 1/av_q2d()=%g",fps);
pcc->time_base.num = 1;
pcc->time_base.den = fps;
}
else
{
pcc->time_base.num = 1;
pcc->time_base.den = fps;
}
// reference ffmpeg\libavformat\utils.c
// For SPS and PPS in avcC container
pcc->extradata = malloc(sizeof(uint8_t)*pCodecCtx->extradata_size);
memcpy(pcc->extradata, pCodecCtx->extradata, pCodecCtx->extradata_size);
pcc->extradata_size = pCodecCtx->extradata_size;
// For Audio stream
if(pAudioCodecCtx)
{
AVCodec *pAudioCodec=NULL;
AVStream *pst2=NULL;
pAudioCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
// Add audio stream
pst2 = avformat_new_stream( fc, pAudioCodec );
vAudioStreamIdx = pst2->index;
pAudioOutputCodecContext = pst2->codec;
avcodec_get_context_defaults3( pAudioOutputCodecContext, pAudioCodec );
fprintf(stderr,"Audio Stream:%d",vAudioStreamIdx);
fprintf(stderr,"pAudioCodecCtx->bits_per_coded_sample=%d",pAudioCodecCtx->bits_per_coded_sample);
pAudioOutputCodecContext->codec_type = AVMEDIA_TYPE_AUDIO;
pAudioOutputCodecContext->codec_id = AV_CODEC_ID_AAC;
// Copy the codec attributes
pAudioOutputCodecContext->channels = pAudioCodecCtx->channels;
pAudioOutputCodecContext->channel_layout = pAudioCodecCtx->channel_layout;
pAudioOutputCodecContext->sample_rate = pAudioCodecCtx->sample_rate;
pAudioOutputCodecContext->bit_rate = 12000;//pAudioCodecCtx->sample_rate * pAudioCodecCtx->bits_per_coded_sample;
pAudioOutputCodecContext->bits_per_coded_sample = pAudioCodecCtx->bits_per_coded_sample;
pAudioOutputCodecContext->profile = pAudioCodecCtx->profile;
//FF_PROFILE_AAC_LOW;
// pAudioCodecCtx->bit_rate;
// AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P
//pAudioOutputCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;//pAudioCodecCtx->sample_fmt;
pAudioOutputCodecContext->sample_fmt = pAudioCodecCtx->sample_fmt;
//pAudioOutputCodecContext->sample_fmt = AV_SAMPLE_FMT_U8;
pAudioOutputCodecContext->sample_aspect_ratio = pAudioCodecCtx->sample_aspect_ratio;
pAudioOutputCodecContext->time_base.num = pAudioCodecCtx->time_base.num;
pAudioOutputCodecContext->time_base.den = pAudioCodecCtx->time_base.den;
pAudioOutputCodecContext->ticks_per_frame = pAudioCodecCtx->ticks_per_frame;
pAudioOutputCodecContext->frame_size = 1024;
fprintf(stderr,"profile:%d, sample_rate:%d, channles:%d", pAudioOutputCodecContext->profile, pAudioOutputCodecContext->sample_rate, pAudioOutputCodecContext->channels);
AVDictionary *opts = NULL;
av_dict_set(&opts, "strict", "experimental", 0);
if (avcodec_open2(pAudioOutputCodecContext, pAudioCodec, &opts) < 0) {
fprintf(stderr, "\ncould not open codec\n");
}
av_dict_free(&opts);
#if 0
// For Audio, this part is no need
if(pAudioCodecCtx->extradata_size!=0)
{
NSLog(#"extradata_size !=0");
pAudioOutputCodecContext->extradata = malloc(sizeof(uint8_t)*pAudioCodecCtx->extradata_size);
memcpy(pAudioOutputCodecContext->extradata, pAudioCodecCtx->extradata, pAudioCodecCtx->extradata_size);
pAudioOutputCodecContext->extradata_size = pAudioCodecCtx->extradata_size;
}
else
{
// For WMA test only
pAudioOutputCodecContext->extradata_size = 0;
NSLog(#"extradata_size ==0");
}
#endif
}
if(fc->oformat->flags & AVFMT_GLOBALHEADER)
{
pcc->flags |= CODEC_FLAG_GLOBAL_HEADER;
pAudioOutputCodecContext->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
if ( !( fc->oformat->flags & AVFMT_NOFILE ) )
{
vRet = avio_open( &fc->pb, fc->filename, AVIO_FLAG_WRITE );
if(vRet!=0)
{
fprintf(stderr,"avio_open(%s) error", fc->filename);
}
}
// dump format in console
av_dump_format(fc, 0, pFilePath, 1);
vRet = avformat_write_header( fc, NULL );
if(vRet==0)
return 1;
else
return 0;
}
av_interleaved_write_frame:
void h264_file_write_frame(AVFormatContext *fc, int vStreamIdx, const void* p, int len, int64_t dts, int64_t pts )
{
AVStream *pst = NULL;
AVPacket pkt;
if ( 0 > vVideoStreamIdx )
return;
// may be audio or video
pst = fc->streams[ vStreamIdx ];
// Init packet
av_init_packet( &pkt );
if(vStreamIdx ==vVideoStreamIdx)
{
pkt.flags |= ( 0 >= getVopType( p, len ) ) ? AV_PKT_FLAG_KEY : 0;
//pkt.flags |= AV_PKT_FLAG_KEY;
pkt.stream_index = pst->index;
pkt.data = (uint8_t*)p;
pkt.size = len;
pkt.dts = AV_NOPTS_VALUE;
pkt.pts = AV_NOPTS_VALUE;
// TODO: mark or unmark the log
//fprintf(stderr, "dts=%lld, pts=%lld\n",dts,pts);
// av_write_frame( fc, &pkt );
}
av_interleaved_write_frame( fc, &pkt );
}
av_write_trailer:
void h264_file_close(AVFormatContext *fc)
{
if ( !fc )
return;
av_write_trailer( fc );
if ( fc->oformat && !( fc->oformat->flags & AVFMT_NOFILE ) && fc->pb )
avio_close( fc->pb );
av_free( fc );
}
Thanks.
It looks like you're using the same AVFormatContext for both the input and output?
In the line
pst = fc->streams[ vStreamIdx ];
You're assigning the AVStream* from your AVFormatContext connected with your input (RTSP stream). But then later on you're trying to write the packet back to the same context av_interleaved_write_frame( fc, &pkt );. I kind of think of a context as a file which has helped me navagate this type of thing better. I do something identicial to what you're doing (not iOS though) where I use a separate AVFormatContext for each of the input (RTSP stream) and output (mp4 file). If I'm correct, I think what you just need to do is initialize an AVFormatContext and properly.
The following code (without error checking everything) is what I do to take an AVFormatContext * output_format_context = NULL and the AVFormatContext * input_format_context that I had associated with the RTSP stream and write from one to the other. This is after I have fetched a packet, etc., which in your case it looks like you're populating (I just take the packet from av_read_frame and re-package it.
This is code that could be in your write frame function (but it also does include the writing of the header).
AVFormatContext * output_format_context;
AVStream * in_stream_2;
AVStream * out_stream_2;
// Allocate the context with the output file
avformat_alloc_output_context2(&output_format_context, NULL, NULL, out_filename.c_str());
// Point to AVOutputFormat * output_format for manipulation
output_format = output_format_context->oformat;
// Loop through all streams
for (i = 0; i < input_format_context->nb_streams; i++) {
// Create a pointer to the input stream that was allocated earlier in the code
AVStream *in_stream = input_format_context->streams[i];
// Create a pointer to a new stream that will be part of the output
AVStream *out_stream = avformat_new_stream(output_format_context, in_stream->codec->codec);
// Set time_base of the new output stream to equal the input stream one since I'm not changing anything (can avoid but get a deprecation warning)
out_stream->time_base = in_stream->time_base;
// This is the non-deprecated way of copying all the parameters from the input stream into the output stream since everything stays the same
avcodec_parameters_from_context(out_stream->codecpar, in_stream->codec);
// I don't remember what this is for :)
out_stream->codec->codec_tag = 0;
// This just sets a flag from the format context to the stream relating to the header
if (output_format_context->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
// Check NOFILE flag and open the output file context (previously the output file was associated with the format context, now it is actually opened.
if (!(output_format->flags & AVFMT_NOFILE))
avio_open(&output_format_context->pb, out_filename.c_str(), AVIO_FLAG_WRITE);
// Write the header (not sure if this is always needed but h264 I believe it is.
avformat_write_header(output_format_context,NULL);
// Re-getting the appropriate stream that was populated above (this should allow for both audio/video)
in_stream_2 = input_format_context->streams[packet.stream_index];
out_stream_2 = output_format_context->streams[packet.stream_index];
// Rescaling pts and dts, duration and pos - you would do as you need in your code.
packet.pts = av_rescale_q_rnd(packet.pts, in_stream_2->time_base, out_stream_2->time_base, (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
packet.dts = av_rescale_q_rnd(packet.dts, in_stream_2->time_base, out_stream_2->time_base, (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
packet.duration = av_rescale_q(packet.duration, in_stream_2->time_base, out_stream_2->time_base);
packet.pos = -1;
// The first packet of my stream always gives me negative dts/pts so this just protects that first one for my purposes. You probably don't need.
if (packet.dts < 0) packet.dts = 0;
if (packet.pts < 0) packet.pts = 0;
// Finally write the frame
av_interleaved_write_frame(output_format_context, &packet);
// ....
// Write header, close/cleanup... etc
// ....
This code is fairly bare bones and doesn't include the setup (which it sounds like you're doing correctly anyway). I would also imagine this code could be cleaned up and tweaked for your purposes, but this works for me to re-write the RTSP stream into a file (in my case many files but code not shown).
The code is C code, so you might need to do minor tweaks for making it Swift compatible (for some of the library function calls maybe). I think overall it should be compatible though.
Hopefully this helps point you to the right direction. This was cobbled together thanks to several sample code sources (I don't remember where), along with warning prompts from the libraries themselves.

Resolving SRV records with iOS SDK

I want to resolve DNS SRV records using the iOS SDK.
I've already tried the high-level Bonjour APIs Apple is providing, but they're not what I need. Now I'm using DNS SD.
void *processQueryForSRVRecord(void *record) {
DNSServiceRef sdRef;
int context;
printf("Setting up query for record: %s\n", record);
DNSServiceQueryRecord(&sdRef, 0, 0, record, kDNSServiceType_SRV, kDNSServiceClass_IN, callback, &context);
printf("Processing query for record: %s\n", record);
DNSServiceProcessResult(sdRef);
printf("Deallocating query for record: %s\n", record);
DNSServiceRefDeallocate(sdRef);
return NULL;
}
This works as long as it gets only correct SRV records (for example: _xmpp-server._tcp.gmail.com), but when the record is typed wrong, DNSServiceProcessResult(sdRef) goes into an infinite loop.
Is there a way to stop DNSServiceProcessResult or must I cancel the thread calling it?
Use good old select(). This is what I have at the moment:
- (void)updateDnsRecords
{
if (self.dnsUpdatePending == YES)
{
return;
}
else
{
self.dnsUpdatePending = YES;
}
NSLog(#"DNS update");
DNSServiceRef sdRef;
DNSServiceErrorType err;
const char* host = [self.dnsHost UTF8String];
if (host != NULL)
{
NSTimeInterval remainingTime = self.dnsUpdateTimeout;
NSDate* startTime = [NSDate date];
err = DNSServiceQueryRecord(&sdRef, 0, 0,
host,
kDNSServiceType_SRV,
kDNSServiceClass_IN,
processDnsReply,
&remainingTime);
// This is necessary so we don't hang forever if there are no results
int dns_sd_fd = DNSServiceRefSockFD(sdRef);
int nfds = dns_sd_fd + 1;
fd_set readfds;
int result;
while (remainingTime > 0)
{
FD_ZERO(&readfds);
FD_SET(dns_sd_fd, &readfds);
struct timeval tv;
tv.tv_sec = (time_t)remainingTime;
tv.tv_usec = (remainingTime - tv.tv_sec) * 1000000;
result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
if (result == 1)
{
if (FD_ISSET(dns_sd_fd, &readfds))
{
err = DNSServiceProcessResult(sdRef);
if (err != kDNSServiceErr_NoError)
{
NSLog(#"There was an error reading the DNS SRV records.");
break;
}
}
}
else if (result == 0)
{
NBLog(#"DNS SRV select() timed out");
break;
}
else
{
if (errno == EINTR)
{
NBLog(#"DNS SRV select() interrupted, retry.");
}
else
{
NBLog(#"DNS SRV select() returned %d errno %d %s.", result, errno, strerror(errno));
break;
}
}
NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:startTime];
remainingTime -= elapsed;
}
DNSServiceRefDeallocate(sdRef);
}
}
static void processDnsReply(DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char* fullname,
uint16_t rrtype,
uint16_t rrclass,
uint16_t rdlen,
const void* rdata,
uint32_t ttl,
void* context)
{
NSTimeInterval* remainingTime = (NSTimeInterval*)context;
// If a timeout occurs the value of the errorCode argument will be
// kDNSServiceErr_Timeout.
if (errorCode != kDNSServiceErr_NoError)
{
return;
}
// The flags argument will have the kDNSServiceFlagsAdd bit set if the
// callback is being invoked when a record is received in response to
// the query.
//
// If kDNSServiceFlagsAdd bit is clear then callback is being invoked
// because the record has expired, in which case the ttl argument will
// be 0.
if ((flags & kDNSServiceFlagsMoreComing) == 0)
{
*remainingTime = 0;
}
// Record parsing code below was copied from Apple SRVResolver sample.
NSMutableData * rrData = [NSMutableData data];
dns_resource_record_t * rr;
uint8_t u8;
uint16_t u16;
uint32_t u32;
u8 = 0;
[rrData appendBytes:&u8 length:sizeof(u8)];
u16 = htons(kDNSServiceType_SRV);
[rrData appendBytes:&u16 length:sizeof(u16)];
u16 = htons(kDNSServiceClass_IN);
[rrData appendBytes:&u16 length:sizeof(u16)];
u32 = htonl(666);
[rrData appendBytes:&u32 length:sizeof(u32)];
u16 = htons(rdlen);
[rrData appendBytes:&u16 length:sizeof(u16)];
[rrData appendBytes:rdata length:rdlen];
rr = dns_parse_resource_record([rrData bytes], (uint32_t) [rrData length]);
// If the parse is successful, add the results.
if (rr != NULL)
{
NSString *target;
target = [NSString stringWithCString:rr->data.SRV->target encoding:NSASCIIStringEncoding];
if (target != nil)
{
uint16_t priority = rr->data.SRV->priority;
uint16_t weight = rr->data.SRV->weight;
uint16_t port = rr->data.SRV->port;
[[FailoverWebInterface sharedInterface] addDnsServer:target priority:priority weight:weight port:port ttl:ttl]; // You'll have to do this in with your own method.
}
}
dns_free_resource_record(rr);
}
Here's the Apple SRVResolver sample from which I got the RR parsing.
This Apple sample mentions that it may block forever, but strange enough suggest to use NSTimer when trying to add a timeout yourself. But I think using select() is a much better way.
I have one to-do: Implement flushing cache with DNSServiceReconfirmRecord. But won't do that now.
Be aware, this code is working, but I'm still testing it.
You need to add libresolv.dylib to your Xcode project's 'Linked Frameworks and Libraries'.