sb_module_t * module_load() { static sb_module_t m; struct RTSPConf *rtspconf = rtspconf_global(); // bzero(&m, sizeof(m)); m.type = SB_MODULE_TYPE_VENCODER; m.name = strdup("Broadcom-VideoCore-H.264-encoder"); m.mimetype = strdup("video/H264"); m.init = vencoder_init; m.start = vencoder_start; //m.threadproc = vencoder_threadproc; m.stop = vencoder_stop; m.deinit = vencoder_deinit; // m.ioctl = vencoder_ioctl; return &m; }
static int vencoder_init(void *arg) { int iid; char *pipefmt = (char*) arg; struct RTSPConf *rtspconf = rtspconf_global(); // if(rtspconf == NULL) { ga_error("video encoder: no configuration found\n"); return -1; } if(vencoder_initialized != 0) return 0; // for(iid = 0; iid < video_source_channels(); iid++) { char pipename[64]; int outputW, outputH; pipeline *pipe; // _sps[iid] = _pps[iid] = NULL; _spslen[iid] = _ppslen[iid] = 0; snprintf(pipename, sizeof(pipename), pipefmt, iid); outputW = video_source_out_width(iid); outputH = video_source_out_height(iid); if((pipe = pipeline::lookup(pipename)) == NULL) { ga_error("video encoder: pipe %s is not found\n", pipename); goto init_failed; } ga_error("video encoder: video source #%d from '%s' (%dx%d).\n", iid, pipe->name(), outputW, outputH, iid); // if(vpu_encoder_init(&vpu[iid], outputW, outputH, rtspconf->video_fps, 1, ga_conf_mapreadint("video-specific", "b") / 1000, ga_conf_mapreadint("video-specific", "g")) < 0) goto init_failed; } vencoder_initialized = 1; ga_error("video encoder: initialized (%d channels).\n", iid); return 0; init_failed: vencoder_deinit(NULL); return -1; }
static int ca_create_swrctx(WAVEFORMATEX *w) { struct RTSPConf *rtspconf = rtspconf_global(); int bufreq, samples; // if(swrctx != NULL) swr_free(&swrctx); if(audio_buf != NULL) free(audio_buf); // ga_error("CoreAudio: create swr context - format[%x] freq[%d] channels[%d]\n", w->wFormatTag, w->nSamplesPerSec, w->nChannels); // swrctx = swr_alloc_set_opts(NULL, rtspconf->audio_device_channel_layout, rtspconf->audio_device_format, rtspconf->audio_samplerate, CA2SWR_chlayout(w->nChannels), CA2SWR_format(w), w->nSamplesPerSec, 0, NULL); if(swrctx == NULL) { ga_error("CoreAudio: cannot create resample context.\n"); return -1; } else { ga_error("CoreAudio: resample context (%x,%d,%d) -> (%x,%d,%d)\n", (int) CA2SWR_chlayout(w->nChannels), (int) CA2SWR_format(w), (int) w->nSamplesPerSec, (int) rtspconf->audio_device_channel_layout, (int) rtspconf->audio_device_format, (int) rtspconf->audio_samplerate); } if(swr_init(swrctx) < 0) { swr_free(&swrctx); swrctx = NULL; ga_error("CoreAudio: resample context init failed.\n"); return -1; } // allocate buffer? ga_samplerate = rtspconf->audio_samplerate; ga_channels = av_get_channel_layout_nb_channels(rtspconf->audio_device_channel_layout); ca_samplerate = w->nSamplesPerSec; ca_bytes_per_sample = w->wBitsPerSample/8; samples = av_rescale_rnd(CA_MAX_SAMPLES, rtspconf->audio_samplerate, w->nSamplesPerSec, AV_ROUND_UP); bufreq = av_samples_get_buffer_size(NULL, rtspconf->audio_channels, samples*2, rtspconf->audio_device_format, 1/*no-alignment*/); if((audio_buf = (unsigned char *) malloc(bufreq)) == NULL) { ga_error("CoreAudio: cannot allocate resample memory.\n"); return -1; } if(audio_source_setup(bufreq, rtspconf->audio_samplerate, 16/* depends on format */, rtspconf->audio_channels) < 0) { ga_error("CoreAudio: audio source setup failed.\n"); return -1; } ga_error("CoreAudio: max %d samples with %d byte(s) resample buffer allocated.\n", samples, bufreq); // return 0; }
void* rtspserver(void *arg) { #ifdef WIN32 SOCKET s = *((SOCKET*) arg); int sinlen = sizeof(struct sockaddr_in); #else int s = *((int*) arg); socklen_t sinlen = sizeof(struct sockaddr_in); #endif const char *p; char buf[8192]; char cmd[32], url[1024], protocol[32]; int rlen; struct sockaddr_in sin; RTSPContext ctx; RTSPMessageHeader header1, *header = &header1; //int thread_ret; // image info //int iwidth = video_source_maxwidth(0); //int iheight = video_source_maxheight(0); // rtspconf = rtspconf_global(); sinlen = sizeof(sin); getpeername(s, (struct sockaddr*) &sin, &sinlen); // bzero(&ctx, sizeof(ctx)); if(per_client_init(&ctx) < 0) { ga_error("server initialization failed.\n"); return NULL; } bcopy(&sin, &ctx.client, sizeof(ctx.client)); ctx.state = SERVER_STATE_IDLE; // XXX: hasVideo is used to sync audio/video // This value is increased by 1 for each captured frame until it is gerater than zero // when this value is greater than zero, audio encoding then starts ... //ctx.hasVideo = -(rtspconf->video_fps>>1); // for slow encoders? ctx.hasVideo = 0; // with 'zerolatency' pthread_mutex_init(&ctx.rtsp_writer_mutex, NULL); // ga_error("[tid %ld] client connected from %s:%d\n", ga_gettid(), inet_ntoa(sin.sin_addr), htons(sin.sin_port)); // ctx.fd = s; // do { int i, fdmax, active; fd_set rfds; struct timeval to; FD_ZERO(&rfds); FD_SET(ctx.fd, &rfds); fdmax = ctx.fd; #ifdef HOLE_PUNCHING for(i = 0; i < 2*ctx.streamCount; i++) { FD_SET(ctx.rtpSocket[i], &rfds); if(ctx.rtpSocket[i] > fdmax) fdmax = ctx.rtpSocket[i]; } #endif to.tv_sec = 0; to.tv_usec = 500000; if((active = select(fdmax+1, &rfds, NULL, NULL, &to)) < 0) { ga_error("select() failed: %s\n", strerror(errno)); goto quit; } if(active == 0) { // try again! continue; } #ifdef HOLE_PUNCHING for(i = 0; i < 2*ctx.streamCount; i++) { struct sockaddr_in xsin; #ifdef WIN32 int xsinlen = sizeof(xsin); #else socklen_t xsinlen = sizeof(xsin); #endif if(FD_ISSET(ctx.rtpSocket[i], &rfds) == 0) continue; recvfrom(ctx.rtpSocket[i], buf, sizeof(buf), 0, (struct sockaddr*) &xsin, &xsinlen); if(ctx.rtpPortChecked[i] != 0) continue; // XXX: port should not flip-flop, so check only once if(xsin.sin_addr.s_addr != ctx.client.sin_addr.s_addr) { ga_error("RTP: client address mismatched? %u.%u.%u.%u != %u.%u.%u.%u\n", NIPQUAD(ctx.client.sin_addr.s_addr), NIPQUAD(xsin.sin_addr.s_addr)); continue; } if(xsin.sin_port != ctx.rtpPeerPort[i]) { ga_error("RTP: client port reconfigured: %u -> %u\n", (unsigned int) ntohs(ctx.rtpPeerPort[i]), (unsigned int) ntohs(xsin.sin_port)); ctx.rtpPeerPort[i] = xsin.sin_port; } else { ga_error("RTP: client is not under an NAT, port %d confirmed\n", (int) ntohs(ctx.rtpPeerPort[i])); } ctx.rtpPortChecked[i] = 1; } // is RTSP connection? if(FD_ISSET(ctx.fd, &rfds) == 0) continue; #endif // read commands if((rlen = rtsp_getnext(&ctx, buf, sizeof(buf))) < 0) { goto quit; } // Interleaved binary data? if(buf[0] == '$') { handle_rtcp(&ctx, buf, rlen); continue; } // REQUEST line ga_error("%s", buf); p = buf; get_word(cmd, sizeof(cmd), &p); get_word(url, sizeof(url), &p); get_word(protocol, sizeof(protocol), &p); // check protocol if(strcmp(protocol, "RTSP/1.0") != 0) { rtsp_reply_error(&ctx, RTSP_STATUS_VERSION); goto quit; } // read headers bzero(header, sizeof(*header)); do { int myseq = -1; char mysession[sizeof(header->session_id)] = ""; if((rlen = rtsp_getnext(&ctx, buf, sizeof(buf))) < 0) goto quit; if(buf[0]=='\n' || (buf[0]=='\r' && buf[1]=='\n')) break; #if 0 ga_error("HEADER: %s", buf); #endif // Special handling to CSeq & Session header // ff_rtsp_parse_line cannot handle CSeq & Session properly on Windows // any more? if(strncasecmp("CSeq: ", buf, 6) == 0) { myseq = strtol(buf+6, NULL, 10); } if(strncasecmp("Session: ", buf, 9) == 0) { strcpy(mysession, buf+9); } // ff_rtsp_parse_line(header, buf, NULL, NULL); // if(myseq > 0 && header->seq <= 0) { ga_error("WARNING: CSeq fixes applied (%d->%d).\n", header->seq, myseq); header->seq = myseq; } if(mysession[0] != '\0' && header->session_id[0]=='\0') { unsigned i; for(i = 0; i < sizeof(header->session_id)-1; i++) { if(mysession[i] == '\0' || isspace(mysession[i]) || mysession[i] == ';') break; header->session_id[i] = mysession[i]; } header->session_id[i+1] = '\0'; ga_error("WARNING: Session fixes applied (%s)\n", header->session_id); } } while(1); // special handle to session_id if(header->session_id != NULL) { char *p = header->session_id; while(*p != '\0') { if(*p == '\r' || *p == '\n') { *p = '\0'; break; } p++; } } // handle commands ctx.seq = header->seq; if (!strcmp(cmd, "DESCRIBE")) rtsp_cmd_describe(&ctx, url); else if (!strcmp(cmd, "OPTIONS")) rtsp_cmd_options(&ctx, url); else if (!strcmp(cmd, "SETUP")) rtsp_cmd_setup(&ctx, url, header); else if (!strcmp(cmd, "PLAY")) rtsp_cmd_play(&ctx, url, header); else if (!strcmp(cmd, "PAUSE")) rtsp_cmd_pause(&ctx, url, header); else if (!strcmp(cmd, "TEARDOWN")) rtsp_cmd_teardown(&ctx, url, header, 1); else rtsp_reply_error(&ctx, RTSP_STATUS_METHOD); if(ctx.state == SERVER_STATE_TEARDOWN) { break; } } while(1); quit: ctx.state = SERVER_STATE_TEARDOWN; // close(ctx.fd); // 2014-05-20: support only share-encoder model ff_server_unregister_client(&ctx); // per_client_deinit(&ctx); //ga_error("RTSP client thread terminated (%d/%d clients left).\n", // video_source_client_count(), audio_source_client_count()); ga_error("RTSP client thread terminated.\n"); // return NULL; }
/// TODO static void * vencoder_threadproc(void *arg) { // arg is pointer to source pipename int cid; struct RTSPConf *rtspconf = NULL; // long long basePts = -1LL, newpts = 0LL, pts = -1LL, ptsSync = 0LL; pthread_mutex_t condMutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // struct timeval pkttv; // int video_written = 0; // rtspconf = rtspconf_global(); cid = 0; // start encoding if(omx_streamer_start(&omxe[cid]) < 0) { sb_error("video encoder: start streamer failed.\n"); goto video_quit; } // sb_error("video encoding started: tid=%ld.\n", sb_gettid()); // while(vencoder_started != 0 && encoder_running() > 0) { // AVPacket pkt; unsigned char *enc; int encsize; // handle pts if(basePts == -1LL) { basePts = omxe[cid].frame_out; ptsSync = encoder_pts_sync(rtspconf->video_fps); newpts = ptsSync; } else { newpts = ptsSync + omxe[cid].frame_out - basePts; } // if((enc = omx_streamer_get(&omxe[cid], &encsize)) == NULL) { sb_error("video encoder: encode failed.\n"); break; } if(encsize == 0) continue; // pts must be monotonically increasing if(newpts > pts) { pts = newpts; } else { pts++; } // send packet #ifdef SAVEFILE if(fout != NULL) fwrite(enc, sizeof(char), encsize, fout); #endif #if 0 // XXX: data check do { unsigned char *ptr, *nextptr; int offset, nextoffset, left = encsize; sb_error("XXX: %d bytes encoded ---------------\n", encsize); if((ptr = sb_find_startcode(enc, enc+encsize, &offset)) != enc) { sb_error("XXX: cannot find a start code\n"); break; } while(ptr != NULL) { int nalsize; nextptr = sb_find_startcode(ptr+4, enc+encsize, &nextoffset); nalsize = nextptr != NULL ? nextptr-ptr : enc+encsize-ptr; sb_error("XXX: nal_t=%d, size=%d\n", ptr[offset] & 0x1f, nalsize); ptr = nextptr; offset = nextoffset; } } while(0); #endif pkt.data = enc; pkt.size = encsize; if(encoder_send_packet("video-encoder", cid, &pkt, pkt.pts, NULL/*&pkttv*/) < 0) { goto video_quit; } if(video_written == 0) { video_written = 1; sb_error("first video frame written (pts=%lld)\n", pts); } } // video_quit: // if(omx_streamer_prepare_stop(&omxe[cid]) < 0) { sb_error("streamer: prepare stop failed.\n"); } // if(omx_streamer_stop(&omxe[cid]) < 0) { sb_error("streamer: start streamer failed.\n"); } // sb_error("video encoder: thread terminated (tid=%ld).\n", sb_gettid()); // return NULL; }
static int vencoder_init(void *arg) { int iid, width, height, fps, bitrate, gopsize; int vsmode[3]; char *pipefmt = (char*) arg; struct RTSPConf *rtspconf = rtspconf_global(); omx_streamer_config_t sc; // if(rtspconf == NULL) { sb_error("video encoder: no configuration found\n"); return -1; } if(vencoder_initialized != 0) return 0; // iid = 0; // if(sb_conf_readints("video-source-v4l-mode", vsmode, 3) == 3) { sb_error("video encoder: use user config: %d %d %d\n", vsmode[0], vsmode[1], vsmode[2]); width = vsmode[1]; height = vsmode[2]; } else { width = 640; height = 480; } if((fps = sb_conf_readint("video-fps")) <= 1) { fps = 24; } if((bitrate = sb_conf_mapreadint("video-specific", "b")) < 1000000) { bitrate = 3000000; } if((gopsize = sb_conf_mapreadint("video-specific", "g")) < 5) { gopsize = fps; } // search for photo seq if(init_image_storage() < 0) { return -1; } // register a dummy video source: only once if(vsource_initialized == 0) { vsource_config_t config; bzero(&config, sizeof(config)); config.curr_width = width; config.curr_height = height; config.curr_stride = width; if(video_source_setup_ex(&config, 1) < 0) { sb_error("video encoder: setup dummy source failed (%dx%d)\n", width, height); return -1; } sb_error("video encoder: dummy source configured (%dx%d)\n", width, height); vsource_initialized = 1; } // sb_error("video encoder: mode=(ignored) (%dx%d), fps=%d\n", width, height, fps); // load configs bzero(&sc, sizeof(sc)); sc.camera_sharpness = sb_omx_load_int("omx-camera-sharpness", -100, 100, OSCAM_DEF_SHARPNESS); sc.camera_contrast = sb_omx_load_int("omx-camera-contrast", -100, 100, OSCAM_DEF_CONTRAST); sc.camera_brightness = sb_omx_load_int("omx-camera-brightness", 0, 100, OSCAM_DEF_BRIGHTNESS); sc.camera_saturation = sb_omx_load_int("omx-camera-saturation", -100, 100, OSCAM_DEF_SATURATION); sc.camera_ev = sb_omx_load_int("omx-camera-ev", -10, 10, OSCAM_DEF_EXPOSURE_VALUE_COMPENSATION); sc.camera_iso = sb_omx_load_int("omx-camera-iso", 100, 800, OSCAM_DEF_EXPOSURE_ISO_SENSITIVITY); sc.camera_iso_auto = sb_omx_load_bool("omx-camera-iso-auto", OSCAM_DEF_EXPOSURE_AUTO_SENSITIVITY); sc.camera_frame_stabilisation = sb_omx_load_bool("omx-camera-frame-stabilisation", OSCAM_DEF_FRAME_STABILISATION); sc.camera_flip_horizon = sb_omx_load_bool("omx-camera-flip-horizon", OSCAM_DEF_FLIP_HORIZONTAL); sc.camera_flip_vertical = sb_omx_load_bool("omx-camera-flip-vertical", OSCAM_DEF_FLIP_VERTICAL); sc.camera_whitebalance = (enum OMX_WHITEBALCONTROLTYPE) sb_omx_load_int("camera-omx-whitebalance", 0, (int) OMX_WhiteBalControlMax, (int) OSCAM_DEF_WHITE_BALANCE_CONTROL); sc.camera_filter = (enum OMX_IMAGEFILTERTYPE) sb_omx_load_int("camera-omx-filter", (int) OMX_ImageFilterNone, (int) OMX_ImageFilterMax, (int) OSCAM_DEF_IMAGE_FILTER); // if(omx_streamer_init(&omxe[iid], NULL, width, height, fps, 1, bitrate, gopsize) < 0) { sb_error("video encoder: init failed.\n"); return -1; } // register a dummy control ctrl_server_setreplay(camera_control); sb_error("video encoder: dummy control enabled.\n"); // vencoder_initialized = 1; sb_error("video encoder: initialized (%d channels).\n", iid+1); return 0; init_failed: vencoder_deinit(NULL); return -1; }
int asource_init(void *arg) { int delay = 0; struct RTSPConf *rtspconf = rtspconf_global(); if(initialized) return 0; // if((delay = ga_conf_readint("audio-init-delay")) > 0) { usleep(delay*1000); } // audioparam.channels = rtspconf->audio_channels; audioparam.samplerate = rtspconf->audio_samplerate; if(rtspconf->audio_device_format == AV_SAMPLE_FMT_S16) { #ifdef WIN32 #else audioparam.format = SND_PCM_FORMAT_S16_LE; #endif audioparam.bits_per_sample = 16; } else { ga_error("audio source: unsupported audio format (%d).\n", rtspconf->audio_device_format); return -1; } if(rtspconf->audio_device_channel_layout != AV_CH_LAYOUT_STEREO) { ga_error("audio source: unsupported channel layout (%llu).\n", rtspconf->audio_device_channel_layout); return -1; } #ifdef WIN32 if(ga_wasapi_init(&audioparam) < 0) { ga_error("WASAPI: initialization failed.\n"); return -1; } #else if((audioparam.handle = ga_alsa_init(&audioparam.sndlog)) == NULL) { ga_error("ALSA: initialization failed.\n"); return -1; } if(ga_alsa_set_param(&audioparam) < 0) { ga_alsa_close(audioparam.handle, audioparam.sndlog); ga_error("ALSA: cannot set parameters\n"); return -1; } #endif if(audio_source_setup(audioparam.chunk_size, audioparam.samplerate, audioparam.bits_per_sample, audioparam.channels) < 0) { ga_error("audio source: setup failed.\n"); #ifdef WIN32 ga_wasapi_close(&audioparam); #else ga_alsa_close(audioparam.handle, audioparam.sndlog); #endif return -1; } initialized = true; ga_error("audio source: setup chunk=%d, samplerate=%d, bps=%d, channels=%d\n", audioparam.chunk_size, audioparam.samplerate, audioparam.bits_per_sample, audioparam.channels); return 0; }
void * vencoder_threadproc(void *arg) { // arg is pointer to source pipe // image info int iid; int iwidth; int iheight; int rtp_id; struct pooldata *data = NULL; struct vsource_frame *frame = NULL; pipeline *pipe = (pipeline*) arg; AVCodecContext *encoder = NULL; // AVFrame *pic_in = NULL; unsigned char *pic_in_buf = NULL; int pic_in_size; unsigned char *nalbuf = NULL, *nalbuf_a = NULL; int nalbuf_size = 0, nalign = 0; long long basePts = -1LL, newpts = 0LL, pts = -1LL, ptsSync = 0LL; pthread_mutex_t condMutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // int resolution[2]; int video_written = 0; // if(pipe == NULL) { ga_error("video encoder: NULL pipeline specified.\n"); goto video_quit; } // rtspconf = rtspconf_global(); // init variables iid = ((struct vsource_config*) pipe->get_privdata())->id; iwidth = video_source_maxwidth(iid); iheight = video_source_maxheight(iid); rtp_id = ((struct vsource_config*) pipe->get_privdata())->rtp_id; // outputW = iwidth; // by default, the same as max resolution outputH = iheight; if(ga_conf_readints("output-resolution", resolution, 2) == 2) { outputW = resolution[0]; outputH = resolution[1]; } // ga_error("video encoder: image source from '%s' (%dx%d) via channel %d, resolution=%dx%d.\n", pipe->name(), iwidth, iheight, rtp_id, outputW, outputH); // encoder = ga_avcodec_vencoder_init( NULL, rtspconf->video_encoder_codec, outputW, outputH, rtspconf->video_fps, rtspconf->vso); if(encoder == NULL) { ga_error("video encoder: cannot initialized the encoder.\n"); goto video_quit; } // nalbuf_size = 100000+12 * outputW * outputH; if(ga_malloc(nalbuf_size, (void**) &nalbuf, &nalign) < 0) { ga_error("video encoder: buffer allocation failed, terminated.\n"); goto video_quit; } nalbuf_a = nalbuf + nalign; // if((pic_in = avcodec_alloc_frame()) == NULL) { ga_error("video encoder: picture allocation failed, terminated.\n"); goto video_quit; } pic_in_size = avpicture_get_size(PIX_FMT_YUV420P, outputW, outputH); if((pic_in_buf = (unsigned char*) av_malloc(pic_in_size)) == NULL) { ga_error("video encoder: picture buffer allocation failed, terminated.\n"); goto video_quit; } avpicture_fill((AVPicture*) pic_in, pic_in_buf, PIX_FMT_YUV420P, outputW, outputH); //ga_error("video encoder: linesize = %d|%d|%d\n", pic_in->linesize[0], pic_in->linesize[1], pic_in->linesize[2]); // start encoding ga_error("video encoding started: tid=%ld %dx%d@%dfps, nalbuf_size=%d, pic_in_size=%d.\n", ga_gettid(), iwidth, iheight, rtspconf->video_fps, nalbuf_size, pic_in_size); // pipe->client_register(ga_gettid(), &cond); // while(encoder_running() > 0) { AVPacket pkt; int got_packet = 0; // wait for notification data = pipe->load_data(); if(data == NULL) { int err; struct timeval tv; struct timespec to; gettimeofday(&tv, NULL); to.tv_sec = tv.tv_sec+1; to.tv_nsec = tv.tv_usec * 1000; // if((err = pipe->timedwait(&cond, &condMutex, &to)) != 0) { ga_error("viedo encoder: image source timed out.\n"); continue; } data = pipe->load_data(); if(data == NULL) { ga_error("viedo encoder: unexpected NULL frame received (from '%s', data=%d, buf=%d).\n", pipe->name(), pipe->data_count(), pipe->buf_count()); continue; } } frame = (struct vsource_frame*) data->ptr; // handle pts if(basePts == -1LL) { basePts = frame->imgpts; ptsSync = encoder_pts_sync(rtspconf->video_fps); newpts = ptsSync; } else { newpts = ptsSync + frame->imgpts - basePts; } // XXX: assume always YUV420P if(pic_in->linesize[0] == frame->linesize[0] && pic_in->linesize[1] == frame->linesize[1] && pic_in->linesize[2] == frame->linesize[2]) { bcopy(frame->imgbuf, pic_in_buf, pic_in_size); } else { ga_error("video encoder: YUV mode failed - mismatched linesize(s) (src:%d,%d,%d; dst:%d,%d,%d)\n", frame->linesize[0], frame->linesize[1], frame->linesize[2], pic_in->linesize[0], pic_in->linesize[1], pic_in->linesize[2]); pipe->release_data(data); goto video_quit; } pipe->release_data(data); // pts must be monotonically increasing if(newpts > pts) { pts = newpts; } else { pts++; } // encode pic_in->pts = pts; av_init_packet(&pkt); pkt.data = nalbuf_a; pkt.size = nalbuf_size; if(avcodec_encode_video2(encoder, &pkt, pic_in, &got_packet) < 0) { ga_error("video encoder: encode failed, terminated.\n"); goto video_quit; } if(got_packet) { if(pkt.pts == (int64_t) AV_NOPTS_VALUE) { pkt.pts = pts; } pkt.stream_index = 0; // send the packet if(encoder_send_packet_all("video-encoder", rtp_id/*rtspconf->video_id*/, &pkt, pkt.pts) < 0) { goto video_quit; } // free unused side-data if(pkt.side_data_elems > 0) { int i; for (i = 0; i < pkt.side_data_elems; i++) av_free(pkt.side_data[i].data); av_freep(&pkt.side_data); pkt.side_data_elems = 0; } // if(video_written == 0) { video_written = 1; ga_error("first video frame written (pts=%lld)\n", pts); } } } // video_quit: if(pipe) { pipe->client_unregister(ga_gettid()); pipe = NULL; } // if(pic_in_buf) av_free(pic_in_buf); if(pic_in) av_free(pic_in); if(nalbuf) free(nalbuf); if(encoder) ga_avcodec_close(encoder); // ga_error("video encoder: thread terminated (tid=%ld).\n", ga_gettid()); // return NULL; }
void* rtspserver(void *arg) { #ifdef WIN32 SOCKET s = *((SOCKET*) arg); int sinlen = sizeof(struct sockaddr_in); #else int s = *((int*) arg); socklen_t sinlen = sizeof(struct sockaddr_in); #endif const char *p; char buf[8192]; char cmd[32], url[1024], protocol[32]; int rlen; struct sockaddr_in sin; RTSPContext ctx; RTSPMessageHeader header1, *header = &header1; int thread_ret; // image info int iwidth = video_source_width(0); int iheight = video_source_height(0); // rtspconf = rtspconf_global(); sinlen = sizeof(sin); getpeername(s, (struct sockaddr*) &sin, &sinlen); // bzero(&ctx, sizeof(ctx)); if(per_client_init(&ctx) < 0) { ga_error("server initialization failed.\n"); return NULL; } ctx.state = SERVER_STATE_IDLE; // XXX: hasVideo is used to sync audio/video // This value is increased by 1 for each captured frame until it is gerater than zero // when this value is greater than zero, audio encoding then starts ... //ctx.hasVideo = -(rtspconf->video_fps>>1); // for slow encoders? ctx.hasVideo = 0; // with 'zerolatency' pthread_mutex_init(&ctx.rtsp_writer_mutex, NULL); #if 0 ctx.audioparam.channels = rtspconf->audio_channels; ctx.audioparam.samplerate = rtspconf->audio_samplerate; if(rtspconf->audio_device_format == AV_SAMPLE_FMT_S16) { #ifdef WIN32 #else ctx.audioparam.format = SND_PCM_FORMAT_S16_LE; #endif ctx.audioparam.bits_per_sample = 16; } // ga_error("INFO: image: %dx%d; audio: %d ch 16-bit pcm @ %dHz\n", iwidth, iheight, ctx.audioparam.channels, ctx.audioparam.samplerate); #endif // #if 0 #ifdef WIN32 if(ga_wasapi_init(&ctx.audioparam) < 0) { ga_error("cannot init wasapi.\n"); return NULL; } #else if((ctx.audioparam.handle = ga_alsa_init(&ctx.audioparam.sndlog)) == NULL) { ga_error("cannot init alsa.\n"); return NULL; } if(ga_alsa_set_param(&ctx.audioparam) < 0) { ga_error("cannot set alsa parameter\n"); return NULL; } #endif #endif // ga_error("[tid %ld] client connected from %s:%d\n", ga_gettid(), inet_ntoa(sin.sin_addr), htons(sin.sin_port)); // ctx.fd = s; // do { fd_set rfds; FD_ZERO(&rfds); FD_SET(ctx.fd, &rfds); if(select(ctx.fd+1, &rfds, NULL, NULL, NULL) <=0) { ga_error("select() failed: %s\n", strerror(errno)); goto quit; } // read commands if((rlen = rtsp_getnext(&ctx, buf, sizeof(buf))) < 0) { goto quit; } // Interleaved binary data? if(buf[0] == '$') { handle_rtcp(&ctx, buf, rlen); continue; } // REQUEST line ga_error("%s", buf); p = buf; get_word(cmd, sizeof(cmd), &p); get_word(url, sizeof(url), &p); get_word(protocol, sizeof(protocol), &p); // check protocol if(strcmp(protocol, "RTSP/1.0") != 0) { rtsp_reply_error(&ctx, RTSP_STATUS_VERSION); goto quit; } // read headers bzero(header, sizeof(*header)); do { int myseq = -1; char mysession[sizeof(header->session_id)] = ""; if((rlen = rtsp_getnext(&ctx, buf, sizeof(buf))) < 0) goto quit; if(buf[0]=='\n' || (buf[0]=='\r' && buf[1]=='\n')) break; #if 0 ga_error("HEADER: %s", buf); #endif // Special handling to CSeq & Session header // ff_rtsp_parse_line cannot handle CSeq & Session properly on Windows // any more? if(strncasecmp("CSeq: ", buf, 6) == 0) { myseq = strtol(buf+6, NULL, 10); } if(strncasecmp("Session: ", buf, 9) == 0) { strcpy(mysession, buf+9); } // ff_rtsp_parse_line(header, buf, NULL, NULL); // if(myseq > 0 && header->seq <= 0) { ga_error("WARNING: CSeq fixes applied (%d->%d).\n", header->seq, myseq); header->seq = myseq; } if(mysession[0] != '\0' && header->session_id[0]=='\0') { unsigned i; for(i = 0; i < sizeof(header->session_id)-1; i++) { if(mysession[i] == '\0' || isspace(mysession[i]) || mysession[i] == ';') break; header->session_id[i] = mysession[i]; } header->session_id[i+1] = '\0'; ga_error("WARNING: Session fixes applied (%s)\n", header->session_id); } } while(1); // special handle to session_id if(header->session_id != NULL) { char *p = header->session_id; while(*p != '\0') { if(*p == '\r' || *p == '\n') { *p = '\0'; break; } p++; } } // handle commands ctx.seq = header->seq; if (!strcmp(cmd, "DESCRIBE")) rtsp_cmd_describe(&ctx, url); else if (!strcmp(cmd, "OPTIONS")) rtsp_cmd_options(&ctx, url); else if (!strcmp(cmd, "SETUP")) rtsp_cmd_setup(&ctx, url, header); else if (!strcmp(cmd, "PLAY")) rtsp_cmd_play(&ctx, url, header); else if (!strcmp(cmd, "PAUSE")) rtsp_cmd_pause(&ctx, url, header); else if (!strcmp(cmd, "TEARDOWN")) rtsp_cmd_teardown(&ctx, url, header); else rtsp_reply_error(&ctx, RTSP_STATUS_METHOD); if(ctx.state == SERVER_STATE_TEARDOWN) { break; } } while(1); quit: ctx.state = SERVER_STATE_TEARDOWN; // close(ctx.fd); #ifdef SHARE_ENCODER encoder_unregister_client(&ctx); #else ga_error("connection closed, checking for worker threads...\n"); #if 0 // if(ctx.vthreadId != 0) { video_source_notify_one(ctx.vthreadId); } #endif pthread_join(ctx.vthread, (void**) &thread_ret); #ifdef ENABLE_AUDIO pthread_join(ctx.athread, (void**) &thread_ret); #endif /* ENABLE_AUDIO */ #endif /* SHARE_ENCODER */ // per_client_deinit(&ctx); //ga_error("RTSP client thread terminated (%d/%d clients left).\n", // video_source_client_count(), audio_source_client_count()); ga_error("RTSP client thread terminated.\n"); // return NULL; }
/// TODO static void * vencoder_threadproc(void *arg) { // arg is pointer to source pipename int cid; pooldata_t *data = NULL; vsource_frame_t *frame = NULL; char *pipename = (char*) arg; pipeline *pipe = pipeline::lookup(pipename); struct RTSPConf *rtspconf = NULL; // long long basePts = -1LL, newpts = 0LL, pts = -1LL, ptsSync = 0LL; pthread_mutex_t condMutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // int outputW, outputH; // struct timeval pkttv; #ifdef PRINT_LATENCY struct timeval ptv; #endif // int video_written = 0; // if(pipe == NULL) { ga_error("video encoder: invalid pipeline specified (%s).\n", pipename); goto video_quit; } // rtspconf = rtspconf_global(); cid = ((vsource_t*) pipe->get_privdata())->channel; outputW = video_source_out_width(cid); outputH = video_source_out_height(cid); // // start encoding ga_error("video encoding started: tid=%ld.\n", ga_gettid()); pipe->client_register(ga_gettid(), &cond); // while(vencoder_started != 0 && encoder_running() > 0) { // AVPacket pkt; unsigned char *enc; int encsize; // wait for notification data = pipe->load_data(); if(data == NULL) { int err; struct timeval tv; struct timespec to; gettimeofday(&tv, NULL); to.tv_sec = tv.tv_sec+1; to.tv_nsec = tv.tv_usec * 1000; // if((err = pipe->timedwait(&cond, &condMutex, &to)) != 0) { ga_error("viedo encoder: image source timed out.\n"); continue; } data = pipe->load_data(); if(data == NULL) { ga_error("viedo encoder: unexpected NULL frame received (from '%s', data=%d, buf=%d).\n", pipe->name(), pipe->data_count(), pipe->buf_count()); continue; } } frame = (vsource_frame_t*) data->ptr; // handle pts if(basePts == -1LL) { basePts = frame->imgpts; ptsSync = encoder_pts_sync(rtspconf->video_fps); newpts = ptsSync; } else { newpts = ptsSync + frame->imgpts - basePts; } // encode! gettimeofday(&pkttv, NULL); enc = vpu_encoder_encode(&vpu[cid], frame->imgbuf, vpu[cid].vpu_framesize, &encsize); // pipe->release_data(data); // if(enc == NULL) { ga_error("encoder-vpu: encode failed.\n"); goto video_quit; } // pts must be monotonically increasing if(newpts > pts) { pts = newpts; } else { pts++; } // send packet #ifdef SAVEFILE if(fout != NULL) fwrite(enc, sizeof(char), encsize, fout); #endif pkt.data = enc; pkt.size = encsize; if(encoder_send_packet_all("video-encoder", cid, &pkt, pkt.pts, &pkttv) < 0) { goto video_quit; } if(video_written == 0) { video_written = 1; ga_error("first video frame written (pts=%lld)\n", pts); } #ifdef PRINT_LATENCY /* print out latency */ gettimeofday(&ptv, NULL); ga_aggregated_print(0x0001, 601, tvdiff_us(&ptv, &frame->timestamp)); #endif } // video_quit: if(pipe) { pipe->client_unregister(ga_gettid()); pipe = NULL; } // ga_error("video encoder: thread terminated (tid=%ld).\n", ga_gettid()); // return NULL; }
void * rtspserver_main(void *arg) { #ifdef WIN32 SOCKET s, cs; int csinlen; #else int s, cs; socklen_t csinlen; #endif struct sockaddr_in sin, csin; struct RTSPConf *conf = rtspconf_global(); // if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("socket"); return (void *) -1; } // do { #ifdef WIN32 BOOL val = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*) &val, sizeof(val)); #else int val = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); #endif } while(0); // bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(conf->serverport); // if(bind(s, (struct sockaddr*) &sin, sizeof(sin)) < 0) { perror("bind"); return (void *) -1; } if(listen(s, 256) < 0) { perror("listen"); return (void *) -1; } // do { pthread_t thread; // csinlen = sizeof(csin); bzero(&csin, sizeof(csin)); if((cs = accept(s, (struct sockaddr*) &csin, &csinlen)) < 0) { perror("accept"); return (void *) -1; } // tunning sending window do { int sndwnd = 8388608; // 8MB if(setsockopt(cs, SOL_SOCKET, SO_SNDBUF, &sndwnd, sizeof(sndwnd)) == 0) { ga_error("*** set TCP sending buffer success.\n"); } else { ga_error("*** set TCP sending buffer failed.\n"); } } while(0); // if(pthread_create(&thread, NULL, rtspserver, &cs) != 0) { close(cs); ga_error("cannot create service thread.\n"); continue; } pthread_detach(thread); } while(1); // return (void *) 0; }
static int vencoder_init(void *arg) { int iid; char *pipefmt = (char*) arg; struct RTSPConf *rtspconf = rtspconf_global(); // if(rtspconf == NULL) { ga_error("video encoder: no configuration found\n"); return -1; } if(vencoder_initialized != 0) return 0; // for(iid = 0; iid < video_source_channels(); iid++) { char pipename[64]; int outputW, outputH; pipeline *pipe; // _sps[iid] = _pps[iid] = NULL; _spslen[iid] = _ppslen[iid] = 0; snprintf(pipename, sizeof(pipename), pipefmt, iid); outputW = video_source_out_width(iid); outputH = video_source_out_height(iid); if((pipe = pipeline::lookup(pipename)) == NULL) { ga_error("video encoder: pipe %s is not found\n", pipename); goto init_failed; } ga_error("video encoder: video source #%d from '%s' (%dx%d).\n", iid, pipe->name(), outputW, outputH, iid); vencoder[iid] = ga_avcodec_vencoder_init(NULL, rtspconf->video_encoder_codec, outputW, outputH, rtspconf->video_fps, rtspconf->vso); if(vencoder[iid] == NULL) goto init_failed; #ifdef STANDALONE_SDP // encoders for SDP generation switch(rtspconf->video_encoder_codec->id) { case AV_CODEC_ID_H264: case AV_CODEC_ID_H265: case AV_CODEC_ID_CAVS: case AV_CODEC_ID_MPEG4: // need ctx with CODEC_FLAG_GLOBAL_HEADER flag avc = avcodec_alloc_context3(rtspconf->video_encoder_codec); if(avc == NULL) goto init_failed; avc->flags |= CODEC_FLAG_GLOBAL_HEADER; avc = ga_avcodec_vencoder_init(avc, rtspconf->video_encoder_codec, outputW, outputH, rtspconf->video_fps, rtspconf->vso); if(avc == NULL) goto init_failed; ga_error("video encoder: meta-encoder #%d created.\n", iid); break; default: // do nothing break; } vencoder_sdp[iid] = avc; #endif } vencoder_initialized = 1; ga_error("video encoder: initialized.\n"); return 0; init_failed: vencoder_deinit(NULL); return -1; }