Revision c3031a67 chunker_streamer/chunker_streamer.c
chunker_streamer/chunker_streamer.c | ||
---|---|---|
25 | 25 |
ExternalChunk *chunk; |
26 | 26 |
AVCodecContext *pCodecCtxEnc; |
27 | 27 |
}; |
28 |
struct outstream outstream[4]; |
|
28 |
#define QUALITYLEVELS_MAX 9 |
|
29 |
struct outstream outstream[1+QUALITYLEVELS_MAX+1]; |
|
30 |
int qualitylevels = 3; |
|
31 |
int indexchannel = 1; |
|
29 | 32 |
|
30 | 33 |
#define DEBUG |
31 | 34 |
#define DEBUG_AUDIO_FRAMES false |
... | ... | |
145 | 148 |
"\t[--video_stream]:set video_stream ID in input\n" |
146 | 149 |
"\t[--audio_stream]:set audio_stream ID in input\n" |
147 | 150 |
"\t[--avfilter]:set input filter (default: yadif\n" |
151 |
"\t[--no-indexchannel]: turn off generation of index channel\n" |
|
152 |
"\t[--qualitylevels]:set number of quality levels\n" |
|
148 | 153 |
"\n" |
149 | 154 |
"Codec options:\n" |
150 | 155 |
"\t[-g GOP]: gop size\n" |
... | ... | |
496 | 501 |
int main(int argc, char *argv[]) { |
497 | 502 |
signal(SIGINT, sigproc); |
498 | 503 |
|
499 |
int i=0; |
|
504 |
int i=0,j,k;
|
|
500 | 505 |
|
501 | 506 |
//output variables |
502 | 507 |
uint8_t *video_outbuf = NULL; |
... | ... | |
557 | 562 |
{"audio_stream", required_argument, 0, 0}, |
558 | 563 |
{"video_stream", required_argument, 0, 0}, |
559 | 564 |
{"avfilter", required_argument, 0, 0}, |
565 |
{"indexchannel", no_argument, &indexchannel, 1}, |
|
566 |
{"no-indexchannel", no_argument, &indexchannel, 0}, |
|
567 |
{"qualitylevels", required_argument, 0, 'Q'}, |
|
560 | 568 |
{0, 0, 0, 0} |
561 | 569 |
}; |
562 | 570 |
/* `getopt_long' stores the option index here. */ |
563 | 571 |
int option_index = 0, c; |
564 | 572 |
int mandatories = 0; |
565 |
while ((c = getopt_long (argc, argv, "i:a:v:A:V:s:lop:q:tF:g:b:d:x:", long_options, &option_index)) != -1) |
|
573 |
while ((c = getopt_long (argc, argv, "i:a:v:A:V:s:lop:q:tF:g:b:d:x:Q:", long_options, &option_index)) != -1)
|
|
566 | 574 |
{ |
567 | 575 |
switch (c) { |
568 | 576 |
case 0: //for long options |
... | ... | |
621 | 629 |
case 'x': |
622 | 630 |
codec_options = strdup(optarg); |
623 | 631 |
break; |
632 |
case 'Q': |
|
633 |
sscanf(optarg, "%d", &qualitylevels); |
|
634 |
if (qualitylevels > QUALITYLEVELS_MAX) { |
|
635 |
fprintf(stderr,"Too many quality levels: %d (max:%d)\n", qualitylevels, QUALITYLEVELS_MAX); |
|
636 |
return -1; |
|
637 |
} |
|
638 |
break; |
|
624 | 639 |
default: |
625 | 640 |
print_usage(argc, argv); |
626 | 641 |
return -1; |
... | ... | |
651 | 666 |
fprintf(stderr,"error parsing output url: %s\n", outside_world_url); |
652 | 667 |
return -2; |
653 | 668 |
} |
654 |
|
|
655 |
outstream[0].output = initTCPPush(peer_ip, peer_port); |
|
656 |
if (!outstream[0].output) { |
|
657 |
fprintf(stderr, "Error initializing output module, exiting\n"); |
|
658 |
exit(1); |
|
659 |
} |
|
660 |
outstream[1].output = initTCPPush(peer_ip, peer_port+1); |
|
661 |
if (!outstream[1].output) { |
|
662 |
fprintf(stderr, "Error initializing output module, exiting\n"); |
|
663 |
exit(1); |
|
664 |
} |
|
665 |
outstream[2].output = initTCPPush(peer_ip, peer_port+2); |
|
666 |
if (!outstream[2].output) { |
|
667 |
fprintf(stderr, "Error initializing output module, exiting\n"); |
|
668 |
exit(1); |
|
669 |
} |
|
670 |
outstream[3].output = initTCPPush(peer_ip, peer_port+3); |
|
671 |
if (!outstream[3].output) { |
|
672 |
fprintf(stderr, "Error initializing output module, exiting\n"); |
|
673 |
exit(1); |
|
669 |
|
|
670 |
for (i=0; i < 1 + qualitylevels + (indexchannel?1:0); i++) { |
|
671 |
outstream[i].output = initTCPPush(peer_ip, peer_port+i); |
|
672 |
if (!outstream[i].output) { |
|
673 |
fprintf(stderr, "Error initializing output module, exiting\n"); |
|
674 |
exit(1); |
|
675 |
} |
|
674 | 676 |
} |
675 | 677 |
#endif |
676 | 678 |
|
... | ... | |
749 | 751 |
dest_width = (dest_width > 0) ? dest_width : pCodecCtx->width; |
750 | 752 |
dest_height = (dest_height > 0) ? dest_height : pCodecCtx->height; |
751 | 753 |
|
752 |
//create an empty first video chunk |
|
753 |
outstream[0].chunk = (ExternalChunk *)malloc(sizeof(ExternalChunk)); |
|
754 |
if(!outstream[0].chunk) { |
|
755 |
fprintf(stderr, "INIT: Memory error alloc chunk!!!\n"); |
|
756 |
return -1; |
|
754 |
//initialize outstream structures |
|
755 |
for (i=0; i < 1 + qualitylevels + (indexchannel?1:0); i++) { |
|
756 |
outstream[i].chunk = (ExternalChunk *)malloc(sizeof(ExternalChunk)); |
|
757 |
if(!outstream[i].chunk) { |
|
758 |
fprintf(stderr, "INIT: Memory error alloc chunk!!!\n"); |
|
759 |
return -1; |
|
760 |
} |
|
761 |
outstream[i].chunk->data = NULL; |
|
762 |
outstream[i].chunk->seq = 0; |
|
763 |
dcprintf(DEBUG_CHUNKER, "INIT: chunk video %d\n", outstream[i].chunk->seq); |
|
764 |
outstream[i].pCodecCtxEnc = NULL; |
|
757 | 765 |
} |
758 |
outstream[0].chunk->data = NULL; |
|
759 |
outstream[0].chunk->seq = 0; |
|
760 |
dcprintf(DEBUG_CHUNKER, "INIT: chunk video %d\n", outstream[0].chunk->seq); |
|
761 | 766 |
outstream[0].pCodecCtxEnc = NULL; |
762 |
|
|
763 |
outstream[1].chunk = (ExternalChunk *)malloc(sizeof(ExternalChunk)); |
|
764 |
if(!outstream[1].chunk) { |
|
765 |
fprintf(stderr, "INIT: Memory error alloc chunk!!!\n"); |
|
766 |
return -1; |
|
767 |
} |
|
768 |
outstream[1].chunk->data = NULL; |
|
769 |
outstream[1].chunk->seq = 0; |
|
770 |
dcprintf(DEBUG_CHUNKER, "INIT: chunk video %d\n", outstream[1].chunk->seq); |
|
771 |
outstream[1].pCodecCtxEnc = openVideoEncoder(video_codec, video_bitrate, dest_width, dest_height, pCodecCtx->time_base, codec_options); |
|
772 |
if (!outstream[1].pCodecCtxEnc) { |
|
773 |
return -1; |
|
774 |
} |
|
775 |
|
|
776 |
outstream[2].chunk = (ExternalChunk *)malloc(sizeof(ExternalChunk)); |
|
777 |
if(!outstream[2].chunk) { |
|
778 |
fprintf(stderr, "INIT: Memory error alloc chunk!!!\n"); |
|
779 |
return -1; |
|
780 |
} |
|
781 |
outstream[2].chunk->data = NULL; |
|
782 |
outstream[2].chunk->seq = 0; |
|
783 |
dcprintf(DEBUG_CHUNKER, "INIT: chunk video %d\n", outstream[2].chunk->seq); |
|
784 |
outstream[2].pCodecCtxEnc = openVideoEncoder(video_codec, video_bitrate/3, (dest_width/4)*2, (dest_height/4)*2, pCodecCtx->time_base, codec_options); // (w/4)*2, since libx264 requires width,height to be even |
|
785 |
if (!outstream[2].pCodecCtxEnc) { |
|
786 |
return -1; |
|
787 |
} |
|
788 |
|
|
789 |
outstream[3].chunk = (ExternalChunk *)malloc(sizeof(ExternalChunk)); |
|
790 |
if(!outstream[3].chunk) { |
|
791 |
fprintf(stderr, "INIT: Memory error alloc chunk!!!\n"); |
|
792 |
return -1; |
|
767 |
for (i=1,j=1,k=1; i < 1 + qualitylevels; i++) { |
|
768 |
outstream[i].pCodecCtxEnc = openVideoEncoder(video_codec, video_bitrate/j, (dest_width/k/2)*2, (dest_height/k/2)*2, pCodecCtx->time_base, codec_options); // (w/2)*2, since libx264 requires width,height to be even |
|
769 |
if (!outstream[i].pCodecCtxEnc) { |
|
770 |
return -1; |
|
771 |
} |
|
772 |
j*=3; //reduce bitrate to 1/3 |
|
773 |
k*=2; //reduce dimensions to 1/2 |
|
793 | 774 |
} |
794 |
outstream[3].chunk->data = NULL; |
|
795 |
outstream[3].chunk->seq = 0; |
|
796 |
dcprintf(DEBUG_CHUNKER, "INIT: chunk video %d\n", outstream[3].chunk->seq); |
|
797 |
outstream[3].pCodecCtxEnc = openVideoEncoder(video_codec, 50000, 160, 120, pCodecCtx->time_base, codec_options); // (w/4)*2, since libx264 requires width,height to be even |
|
798 |
if (!outstream[3].pCodecCtxEnc) { |
|
799 |
return -1; |
|
775 |
if (indexchannel) { |
|
776 |
outstream[qualitylevels + 1].pCodecCtxEnc = openVideoEncoder(video_codec, 50000, 160, 120, pCodecCtx->time_base, codec_options); |
|
777 |
if (!outstream[qualitylevels + 1].pCodecCtxEnc) { |
|
778 |
return -1; |
|
779 |
} |
|
800 | 780 |
} |
801 | 781 |
|
802 | 782 |
fprintf(stderr, "INIT: VIDEO timebase OUT:%d %d IN: %d %d\n", outstream[1].pCodecCtxEnc->time_base.num, outstream[1].pCodecCtxEnc->time_base.den, pCodecCtx->time_base.num, pCodecCtx->time_base.den); |
... | ... | |
1038 | 1018 |
// store timestamp in useconds for next frame sleep |
1039 | 1019 |
newTime_video = pts2ms(pFrame->pkt_pts - ptsvideo1, pFormatCtx->streams[videoStream]->time_base)*1000; |
1040 | 1020 |
|
1041 |
if(true) { |
|
1021 |
if(true) { //copy channel
|
|
1042 | 1022 |
video_frame_size = packet.size; |
1043 | 1023 |
if (video_frame_size > video_outbuf_size) { |
1044 | 1024 |
fprintf(stderr, "VIDEO: error, outbuf too small, SKIPPING\n");; |
... | ... | |
1057 | 1037 |
pFrame->pict_type); |
1058 | 1038 |
addFrameToOutstream(&outstream[0], frame, video_outbuf); |
1059 | 1039 |
} |
1060 |
if(true) {
|
|
1061 |
video_frame_size = transcodeFrame(video_outbuf, video_outbuf_size, &target_pts, pFrame, pFormatCtx->streams[videoStream]->time_base, pCodecCtx, outstream[1].pCodecCtxEnc);
|
|
1040 |
for (i=1; i < 1 + qualitylevels + (indexchannel?1:0); i++) {
|
|
1041 |
video_frame_size = transcodeFrame(video_outbuf, video_outbuf_size, &target_pts, pFrame, pFormatCtx->streams[videoStream]->time_base, pCodecCtx, outstream[i].pCodecCtxEnc);
|
|
1062 | 1042 |
if (video_frame_size <= 0) { |
1063 | 1043 |
av_free_packet(&packet); |
1064 | 1044 |
contFrameVideo = STREAMER_MAX(contFrameVideo-1, 0); |
1065 | 1045 |
continue; |
1066 | 1046 |
} |
1067 |
createFrame(frame, pts2ms(target_pts - ptsvideo1, pFormatCtx->streams[videoStream]->time_base), video_frame_size, |
|
1068 |
(unsigned char)outstream[1].pCodecCtxEnc->coded_frame->pict_type); |
|
1069 |
addFrameToOutstream(&outstream[1], frame, video_outbuf); |
|
1070 |
} |
|
1071 |
if(true) { |
|
1072 |
video_frame_size = transcodeFrame(video_outbuf, video_outbuf_size, &target_pts, pFrame, pFormatCtx->streams[videoStream]->time_base, pCodecCtx, outstream[2].pCodecCtxEnc); |
|
1073 |
if (video_frame_size <= 0) { |
|
1074 |
av_free_packet(&packet); |
|
1075 |
contFrameVideo = STREAMER_MAX(contFrameVideo-1, 0); |
|
1076 |
continue; |
|
1077 |
} |
|
1078 |
createFrame(frame, pts2ms(target_pts - ptsvideo1, pFormatCtx->streams[videoStream]->time_base), video_frame_size, |
|
1079 |
(unsigned char)outstream[2].pCodecCtxEnc->coded_frame->pict_type); |
|
1080 |
addFrameToOutstream(&outstream[2], frame, video_outbuf); |
|
1081 |
} |
|
1082 |
if(true) { |
|
1083 |
video_frame_size = transcodeFrame(video_outbuf, video_outbuf_size, &target_pts, pFrame, pFormatCtx->streams[videoStream]->time_base, pCodecCtx, outstream[3].pCodecCtxEnc); |
|
1084 |
if (video_frame_size <= 0) { |
|
1085 |
av_free_packet(&packet); |
|
1086 |
contFrameVideo = STREAMER_MAX(contFrameVideo-1, 0); |
|
1087 |
continue; |
|
1088 |
} |
|
1089 |
createFrame(frame, pts2ms(target_pts - ptsvideo1, pFormatCtx->streams[videoStream]->time_base), video_frame_size, |
|
1090 |
(unsigned char)outstream[3].pCodecCtxEnc->coded_frame->pict_type); |
|
1091 |
addFrameToOutstream(&outstream[3], frame, video_outbuf); |
|
1047 |
createFrame(frame, pts2ms(target_pts - ptsvideo1, pFormatCtx->streams[videoStream]->time_base), video_frame_size, |
|
1048 |
(unsigned char)outstream[i].pCodecCtxEnc->coded_frame->pict_type); |
|
1049 |
addFrameToOutstream(&outstream[i], frame, video_outbuf); |
|
1092 | 1050 |
} |
1093 | 1051 |
|
1094 | 1052 |
|
... | ... | |
1249 | 1207 |
//SAVE ON FILE |
1250 | 1208 |
//saveChunkOnFile(chunkaudio); |
1251 | 1209 |
//Send the chunk to an external transport/player |
1252 |
sendChunk(outstream[0].output, chunkaudio);
|
|
1253 |
sendChunk(outstream[1].output, chunkaudio);
|
|
1254 |
sendChunk(outstream[2].output, chunkaudio);
|
|
1210 |
for (i=0; i < 1 + qualitylevels; i++) { //do not send audio to the index channel
|
|
1211 |
sendChunk(outstream[i].output, chunkaudio);
|
|
1212 |
}
|
|
1255 | 1213 |
dctprintf(DEBUG_CHUNKER, "AUDIO: just sent chunk audio %d\n", chunkaudio->seq); |
1256 | 1214 |
chunkaudio->seq = 0; //signal that we need an increase |
1257 | 1215 |
//initChunk(chunkaudio, &seq_current_chunk); |
... | ... | |
1302 | 1260 |
fclose(psnrtrace); |
1303 | 1261 |
|
1304 | 1262 |
close: |
1305 |
if(outstream[0].chunk->seq != 0 && outstream[0].chunk->frames_num>0) { |
|
1306 |
//SAVE ON FILE |
|
1307 |
//saveChunkOnFile(chunk); |
|
1308 |
//Send the chunk to an external transport/player |
|
1309 |
sendChunk(outstream[0].output, outstream[0].chunk); |
|
1310 |
dcprintf(DEBUG_CHUNKER, "CHUNKER: SENDING LAST VIDEO CHUNK\n"); |
|
1311 |
outstream[0].chunk->seq = 0; //signal that we need an increase just in case we will restart |
|
1263 |
for (i=0; i < 1 + qualitylevels + (indexchannel?1:0); i++) { |
|
1264 |
if(outstream[i].chunk->seq != 0 && outstream[i].chunk->frames_num>0) { |
|
1265 |
sendChunk(outstream[i].output, outstream[0].chunk); |
|
1266 |
dcprintf(DEBUG_CHUNKER, "CHUNKER: SENDING LAST VIDEO CHUNK\n"); |
|
1267 |
outstream[i].chunk->seq = 0; //signal that we need an increase just in case we will restart |
|
1268 |
} |
|
1312 | 1269 |
} |
1313 |
if(chunkaudio->seq != 0 && chunkaudio->frames_num>0) { |
|
1314 |
//SAVE ON FILE |
|
1315 |
//saveChunkOnFile(chunkaudio); |
|
1316 |
//Send the chunk via http to an external transport/player |
|
1317 |
sendChunk(outstream[0].output, chunkaudio); |
|
1318 |
dcprintf(DEBUG_CHUNKER, "CHUNKER: SENDING LAST AUDIO CHUNK\n"); |
|
1319 |
chunkaudio->seq = 0; //signal that we need an increase just in case we will restart |
|
1270 |
for (i=0; i < 1 + qualitylevels; i++) { |
|
1271 |
if(chunkaudio->seq != 0 && chunkaudio->frames_num>0) { |
|
1272 |
sendChunk(outstream[i].output, chunkaudio); |
|
1273 |
dcprintf(DEBUG_CHUNKER, "CHUNKER: SENDING LAST AUDIO CHUNK\n"); |
|
1274 |
} |
|
1320 | 1275 |
} |
1276 |
chunkaudio->seq = 0; //signal that we need an increase just in case we will restart |
|
1321 | 1277 |
|
1322 | 1278 |
#ifdef HTTPIO |
1323 | 1279 |
/* finalize the HTTP chunk pusher */ |
1324 | 1280 |
finalizeChunkPusher(); |
1325 | 1281 |
#endif |
1326 | 1282 |
|
1327 |
free(outstream[0].chunk); |
|
1283 |
for (i=0; i < 1 + qualitylevels + (indexchannel?1:0); i++) { |
|
1284 |
free(outstream[i].chunk); |
|
1285 |
} |
|
1328 | 1286 |
free(chunkaudio); |
1329 | 1287 |
free(frame); |
1330 | 1288 |
av_free(video_outbuf); |
... | ... | |
1337 | 1295 |
|
1338 | 1296 |
// Close the codec |
1339 | 1297 |
avcodec_close(pCodecCtx); |
1340 |
avcodec_close(outstream[1].pCodecCtxEnc);
|
|
1341 |
avcodec_close(outstream[2].pCodecCtxEnc);
|
|
1342 |
avcodec_close(outstream[3].pCodecCtxEnc);
|
|
1298 |
for (i=1; i < 1 + qualitylevels + (indexchannel?1:0); i++) {
|
|
1299 |
avcodec_close(outstream[i].pCodecCtxEnc);
|
|
1300 |
}
|
|
1343 | 1301 |
|
1344 | 1302 |
if(audioStream!=-1) { |
1345 | 1303 |
avcodec_close(aCodecCtx); |
... | ... | |
1392 | 1350 |
} |
1393 | 1351 |
|
1394 | 1352 |
#ifdef TCPIO |
1395 |
finalizeTCPChunkPusher(outstream[0].output); |
|
1396 |
finalizeTCPChunkPusher(outstream[1].output); |
|
1397 |
finalizeTCPChunkPusher(outstream[2].output); |
|
1398 |
finalizeTCPChunkPusher(outstream[3].output); |
|
1353 |
for (i=0; i < 1 + qualitylevels + (indexchannel?1:0); i++) { |
|
1354 |
finalizeTCPChunkPusher(outstream[i].output); |
|
1355 |
} |
|
1399 | 1356 |
#endif |
1400 | 1357 |
|
1401 | 1358 |
#ifdef USE_AVFILTER |
Also available in: Unified diff