/* * url syntax: rtp://host:port[?option=val...] * option: 'multicast=1' : enable multicast * 'ttl=n' : set the ttl value (for multicast only) * 'localport=n' : set the local port to n * */ static int rtp_open(URLContext *h, const char *uri, int flags) { RTPContext *s; int port, is_output, is_multicast, ttl, local_port; char hostname[256]; char buf[1024]; char path[1024]; const char *p; is_output = (flags & URL_WRONLY); s = av_mallocz(sizeof(RTPContext)); if (!s) return AVERROR(ENOMEM); h->priv_data = s; url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, path, sizeof(path), uri); /* extract parameters */ is_multicast = 0; ttl = -1; local_port = -1; p = strchr(uri, '?'); if (p) { is_multicast = find_info_tag(buf, sizeof(buf), "multicast", p); if (find_info_tag(buf, sizeof(buf), "ttl", p)) { ttl = strtol(buf, NULL, 10); } if (find_info_tag(buf, sizeof(buf), "localport", p)) { local_port = strtol(buf, NULL, 10); } } build_udp_url(buf, sizeof(buf), hostname, port, local_port, is_multicast, ttl); if (url_open(&s->rtp_hd, buf, flags) < 0) goto fail; local_port = udp_get_local_port(s->rtp_hd); /* XXX: need to open another connexion if the port is not even */ /* well, should suppress localport in path */ build_udp_url(buf, sizeof(buf), hostname, port + 1, local_port + 1, is_multicast, ttl); if (url_open(&s->rtcp_hd, buf, flags) < 0) goto fail; /* just to ease handle access. XXX: need to suppress direct handle access */ s->rtp_fd = udp_get_file_handle(s->rtp_hd); s->rtcp_fd = udp_get_file_handle(s->rtcp_hd); h->max_packet_size = url_get_max_packet_size(s->rtp_hd); h->is_streamed = 1; return 0; fail: if (s->rtp_hd) url_close(s->rtp_hd); if (s->rtcp_hd) url_close(s->rtcp_hd); av_free(s); return AVERROR_IO; }
/* XXX: This class should really pass the picture parameters by a separate API * so that we can, by contract, enforce that the frame size can't suddenly * change on us. */ void Java_org_devtcg_rojocam_ffmpeg_RtpOutputContext_nativeWriteFrame(JNIEnv *env, jclass clazz, jint nativeInt, jbyteArray data, jlong frameTime, jint frameFormat, jint frameWidth, jint frameHeight, jint frameBitsPerPixel) { RtpOutputContext *rtpContext = (RtpOutputContext *)nativeInt; AVFormatContext *avContext; AVCodecContext *codec; AVStream *outputStream; AVPacket pkt; jbyte *data_c; int max_packet_size; uint8_t *rtp_data; int rtp_data_len; avContext = rtpContext->avContext; outputStream = avContext->streams[0]; codec = outputStream->codec; if (rtpContext->tempFrame == NULL) { if (!first_frame_init(env, rtpContext, frameFormat, frameWidth, frameHeight)) { LOGE("Error initializing encoding buffers, cannot stream"); return; } } data_c = (*env)->GetByteArrayElements(env, data, NULL); /* Convert the input arguments to an AVPacket, simulating it as though we * read this from the ffmpeg libraries but there was no need to do this as * it was passed into us already as a raw video frame. */ int frameDuration = frameTime - rtpContext->lastFrameTime; bool frameEncoded = encode_video_frame(rtpContext, outputStream, rtpContext->tempFrame, rtpContext->imgConvert, rtpContext->tempEncodedBuf, sizeof(rtpContext->tempEncodedBuf), data_c, frameTime, frameDuration, frameFormat, frameWidth, frameHeight, frameBitsPerPixel, &pkt); rtpContext->lastFrameTime = frameTime; (*env)->ReleaseByteArrayElements(env, data, data_c, JNI_ABORT); if (frameEncoded) { #if PROFILE_WRITE_FRAME struct timeval then; gettimeofday(&then, NULL); #endif max_packet_size = url_get_max_packet_size(rtpContext->urlContext); url_open_dyn_packet_buf(&avContext->pb, max_packet_size); avContext->pb->seekable = 0; /* This organizes our encoded packet into RTP packet segments (but it * doesn't actually send anything over the network yet). */ if (av_write_frame(avContext, &pkt) < 0) { jniThrowException(env, "java/io/IOException", "Error writing frame to output"); } /* Actually deliver the packetized RTP data to the remote peer. */ rtp_data_len = url_close_dyn_buf(avContext->pb, &rtp_data); exhaustive_send(rtpContext->urlContext, rtp_data, rtp_data_len); av_free(rtp_data); /* XXX: I dunno, ffserver.c does this... */ outputStream->codec->frame_number++; #if PROFILE_WRITE_FRAME store_elapsed(&rtpContext->write_time, &then); #endif } else { #if PROFILE_WRITE_TIME rtpContext->write_time = 0; #endif } #if PROFILE_WRITE_FRAME //LOGI("resample@%ld ms; encode@%ld ms; write@%ld ms", // rtpContext->resampling_time, rtpContext->encoding_time, // rtpContext->write_time); #endif }
static int rtp_open(URLContext *h, const char *uri, int flags) { RTPContext *s; int rtp_port, rtcp_port, is_output, ttl, local_rtp_port, local_rtcp_port, max_packet_size; char hostname[256]; char buf[1024]; char path[1024]; const char *p; is_output = (flags & URL_WRONLY); s = av_mallocz(sizeof(RTPContext)); if (!s) return AVERROR(ENOMEM); h->priv_data = s; av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port, path, sizeof(path), uri); /* extract parameters */ ttl = -1; rtcp_port = rtp_port+1; local_rtp_port = -1; local_rtcp_port = -1; max_packet_size = -1; p = strchr(uri, '?'); if (p) { if (find_info_tag(buf, sizeof(buf), "ttl", p)) { ttl = strtol(buf, NULL, 10); } if (find_info_tag(buf, sizeof(buf), "rtcpport", p)) { rtcp_port = strtol(buf, NULL, 10); } if (find_info_tag(buf, sizeof(buf), "localport", p)) { local_rtp_port = strtol(buf, NULL, 10); } if (find_info_tag(buf, sizeof(buf), "localrtpport", p)) { local_rtp_port = strtol(buf, NULL, 10); } if (find_info_tag(buf, sizeof(buf), "localrtcpport", p)) { local_rtcp_port = strtol(buf, NULL, 10); } if (find_info_tag(buf, sizeof(buf), "pkt_size", p)) { max_packet_size = strtol(buf, NULL, 10); } } build_udp_url(buf, sizeof(buf), hostname, rtp_port, local_rtp_port, ttl, max_packet_size); if (url_open(&s->rtp_hd, buf, flags) < 0) goto fail; if (local_rtp_port>=0 && local_rtcp_port<0) local_rtcp_port = udp_get_local_port(s->rtp_hd) + 1; build_udp_url(buf, sizeof(buf), hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size); if (url_open(&s->rtcp_hd, buf, flags) < 0) goto fail; /* just to ease handle access. XXX: need to suppress direct handle access */ s->rtp_fd = url_get_file_handle(s->rtp_hd); s->rtcp_fd = url_get_file_handle(s->rtcp_hd); h->max_packet_size = url_get_max_packet_size(s->rtp_hd); h->is_streamed = 1; return 0; fail: if (s->rtp_hd) url_close(s->rtp_hd); if (s->rtcp_hd) url_close(s->rtcp_hd); av_free(s); return AVERROR(EIO); }
jint Java_org_devtcg_rojocam_ffmpeg_RtpOutputContext_nativeCreate(JNIEnv *env, jclass clazz, jint streamConfigNativeInt, jlong nowNanoTime, jstring hostAddress, jint rtpPort) { FFStreamConfig *config = (FFStreamConfig *)streamConfigNativeInt; RtpOutputContext *rtpContext = NULL; AVFormatContext *avContext = NULL; AVStream *st = NULL; uint8_t *dummy_buf; int max_packet_size; rtpContext = av_mallocz(sizeof(RtpOutputContext)); if (rtpContext == NULL) { jniThrowOOM(env); goto cleanup; } rtpContext->lastFrameTime = nowNanoTime; rtpContext->config = config; avContext = avformat_alloc_context(); if (avContext == NULL) { jniThrowOOM(env); goto cleanup; } avContext->oformat = av_guess_format("rtp", NULL, NULL); if (avContext->oformat == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "rtp avformat is not available"); goto cleanup; } rtpContext->avContext = avContext; st = av_mallocz(sizeof(AVStream)); if (st == NULL) { jniThrowOOM(env); goto cleanup; } avContext->nb_streams = 1; avContext->streams = av_malloc(avContext->nb_streams * sizeof(*avContext->streams)); avContext->streams[0] = st; /* XXX: What would we be doing if we supported audio as well? */ memcpy(st, config->streams[0], sizeof(AVStream)); st->priv_data = NULL; const jbyte *hostAddress_str = (*env)->GetStringUTFChars(env, hostAddress, NULL); snprintf(avContext->filename, sizeof(avContext->filename), "rtp://%s:%d?localrtpport=5000&localrtcpport=5001", hostAddress_str, rtpPort); (*env)->ReleaseStringUTFChars(env, hostAddress, hostAddress_str); if (url_open(&rtpContext->urlContext, avContext->filename, URL_WRONLY) < 0) { LOGE("Cannot open url context for filename=%s", avContext->filename); jniThrowException(env, "java/io/IOException", "Unable to open URL"); goto cleanup; } max_packet_size = url_get_max_packet_size(rtpContext->urlContext); /* XXX: No idea what purpose this serves... */ url_open_dyn_packet_buf(&avContext->pb, max_packet_size); av_set_parameters(avContext, NULL); if (av_write_header(avContext) < 0) { jniThrowException(env, "java/io/IOException", "Unexpected error writing dummy RTP header"); goto cleanup; } url_close_dyn_buf(avContext->pb, &dummy_buf); av_free(dummy_buf); return (jint)rtpContext; cleanup: rtp_output_context_free(rtpContext); assert((*env)->ExceptionOccurred(env)); return 0; }