Skip to content

Commit 8be8002

Browse files
authored
Add video capability to pcaputil sample app (#4525)
1 parent 125ee0d commit 8be8002

File tree

1 file changed

+227
-5
lines changed

1 file changed

+227
-5
lines changed

pjsip-apps/src/samples/pcaputil.c

Lines changed: 227 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@
2727
static const char *USAGE =
2828
"pcaputil [options] INPUT OUTPUT\n"
2929
"\n"
30-
" Convert captured RTP packets in PCAP file to WAV file or play it\n"
30+
" Convert captured RTP packets in PCAP file to WAV/AVI file or play it\n"
3131
" to audio device.\n"
3232
"\n"
3333
" INPUT is the PCAP file name/path.\n"
34-
" OUTPUT is the WAV file name/path to store the output, or set to \"-\",\n"
34+
" OUTPUT is the WAV/AVI file name/path to store the output, or set to \"-\",\n"
3535
" to play the output to audio device. The program will decode\n"
3636
" the RTP contents using codec that is available in PJMEDIA,\n"
3737
" and optionally decrypt the content using the SRTP crypto and\n"
@@ -46,6 +46,9 @@ static const char *USAGE =
4646
"\n"
4747
"Options for RTP packet processing:\n"
4848
""
49+
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
50+
" --video Video mode\n"
51+
#endif
4952
" --codec=codec_id The codec ID formatted \"name/clock-rate/channel-count\"\n"
5053
" must be specified for codec with dynamic PT,\n"
5154
" e.g: \"Speex/8000\"\n"
@@ -81,6 +84,8 @@ static struct app
8184
pj_pcap_file *pcap;
8285
pjmedia_port *wav;
8386
pjmedia_codec *codec;
87+
pjmedia_vid_codec *vcodec;
88+
pjmedia_format vfmt;
8489
pjmedia_aud_stream *aud_strm;
8590
unsigned pt;
8691
pjmedia_transport *srtp;
@@ -90,6 +95,7 @@ static struct app
9095

9196
struct args
9297
{
98+
pj_bool_t video;
9399
pj_str_t codec;
94100
pj_str_t wav_filename;
95101
pjmedia_aud_dev_index dev_id;
@@ -125,10 +131,36 @@ static void cleanup()
125131
cmgr = pjmedia_endpt_get_codec_mgr(app.mept);
126132
pjmedia_codec_mgr_dealloc_codec(cmgr, app.codec);
127133
}
134+
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
135+
if (app.vcodec) {
136+
pjmedia_vid_codec_close(app.vcodec);
137+
pjmedia_vid_codec_mgr_dealloc_codec(NULL, app.vcodec);
138+
}
139+
#endif
128140
if (app.aud_strm) {
129141
pjmedia_aud_stream_stop(app.aud_strm);
130142
pjmedia_aud_stream_destroy(app.aud_strm);
131143
}
144+
145+
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
146+
147+
#if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0
148+
pjmedia_codec_ffmpeg_vid_deinit();
149+
#endif
150+
#if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0
151+
pjmedia_codec_vpx_vid_deinit();
152+
#endif
153+
#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0
154+
pjmedia_codec_openh264_vid_deinit();
155+
#endif
156+
pjmedia_vid_dev_subsys_shutdown();
157+
pjmedia_event_mgr_destroy(NULL);
158+
pjmedia_video_format_mgr_destroy(NULL);
159+
pjmedia_converter_mgr_destroy(NULL);
160+
pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr_instance());
161+
162+
#endif
163+
132164
if (app.mept) pjmedia_endpt_destroy(app.mept);
133165
if (app.pool) pj_pool_release(app.pool);
134166
pj_caching_pool_destroy(&app.cp);
@@ -286,6 +318,7 @@ static pj_status_t play_cb(void *user_data, pjmedia_frame *f)
286318
return PJ_SUCCESS;
287319
}
288320

321+
289322
static void pcap2wav(const struct args *args)
290323
{
291324
const pj_str_t WAV = {".wav", 4};
@@ -296,6 +329,7 @@ static void pcap2wav(const struct args *args)
296329
pj_uint8_t *payload;
297330
unsigned payload_len;
298331
} pkt0;
332+
299333
pjmedia_codec_mgr *cmgr;
300334
const pjmedia_codec_info *ci;
301335
pjmedia_codec_param param;
@@ -457,6 +491,161 @@ static void pcap2wav(const struct args *args)
457491
}
458492

459493

494+
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
495+
static pj_status_t event_cb(pjmedia_event *event, void *user_data)
496+
{
497+
PJ_UNUSED_ARG(user_data);
498+
499+
if (event->epub == app.vcodec) {
500+
/* This is codec event */
501+
switch (event->type) {
502+
case PJMEDIA_EVENT_FMT_CHANGED:
503+
{
504+
pjmedia_format *fmt = &event->data.fmt_changed.new_fmt;
505+
506+
/* The event may only provide width & height, re-initialize
507+
* format with fps, bps, etc.
508+
*/
509+
pjmedia_format_init_video(&app.vfmt,
510+
fmt->id,
511+
fmt->det.vid.size.w,
512+
fmt->det.vid.size.h,
513+
25, 1);
514+
}
515+
break;
516+
default:
517+
break;
518+
}
519+
}
520+
521+
return PJ_SUCCESS;
522+
}
523+
#endif
524+
525+
526+
static void pcap2avi(const struct args *args)
527+
{
528+
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
529+
const pj_str_t AVI = {".avi", 4};
530+
enum { MAX_BUF_SIZE = 100 * 1024 * 1024 };
531+
struct pkt
532+
{
533+
pj_uint8_t buffer[PJMEDIA_MAX_MTU];
534+
pjmedia_rtp_hdr *rtp;
535+
pj_uint8_t *payload;
536+
unsigned payload_len;
537+
} pkt0;
538+
539+
const pjmedia_vid_codec_info *ci;
540+
pjmedia_vid_codec_param param;
541+
pjmedia_avi_streams *avi_streams = NULL;
542+
pj_uint8_t *buf;
543+
pj_status_t status;
544+
545+
/* Create SRTP transport is needed */
546+
#if PJMEDIA_HAS_SRTP
547+
if (args->srtp_crypto.slen) {
548+
pjmedia_srtp_crypto crypto;
549+
pjmedia_transport *tp;
550+
551+
pj_bzero(&crypto, sizeof(crypto));
552+
crypto.key = args->srtp_key;
553+
crypto.name = args->srtp_crypto;
554+
T( pjmedia_transport_loop_create(app.mept, &tp) );
555+
T( pjmedia_transport_srtp_create(app.mept, tp, NULL, &app.srtp) );
556+
T( pjmedia_transport_srtp_start(app.srtp, &crypto, &crypto) );
557+
}
558+
#endif
559+
560+
/* Read first packet */
561+
read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp,
562+
&pkt0.payload, &pkt0.payload_len, PJ_FALSE);
563+
564+
/* Get codec info and param for the specified payload type */
565+
app.pt = pkt0.rtp->pt;
566+
if (app.pt < 96) {
567+
T( pjmedia_vid_codec_mgr_get_codec_info(NULL, pkt0.rtp->pt, &ci) );
568+
} else {
569+
unsigned cnt = 2;
570+
const pjmedia_vid_codec_info *info[2];
571+
T( pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, &args->codec, &cnt,
572+
info, NULL) );
573+
if (cnt != 1)
574+
err_exit("Codec ID must be specified and unique!", 0);
575+
576+
ci = info[0];
577+
}
578+
T( pjmedia_vid_codec_mgr_get_default_param(NULL, ci, &param) );
579+
if (args->codec_fmtp.slen > 0) {
580+
T( pjmedia_stream_info_parse_fmtp_data(app.pool, &args->codec_fmtp, &param.dec_fmtp) );
581+
}
582+
583+
/* Alloc and init codec */
584+
T( pjmedia_vid_codec_mgr_alloc_codec(NULL, ci, &app.vcodec) );
585+
T( pjmedia_vid_codec_init(app.vcodec, app.pool) );
586+
T( pjmedia_vid_codec_open(app.vcodec, &param) );
587+
588+
/* Subscribe to codec events */
589+
pjmedia_event_subscribe(NULL, &event_cb, app.vcodec, app.vcodec);
590+
591+
/* Alloc buffer for decoded video frame */
592+
buf = (pj_uint8_t*)pj_pool_alloc(app.pool, MAX_BUF_SIZE);
593+
594+
/* Loop reading PCAP and writing AVI file */
595+
for (;;) {
596+
struct pkt pkt1;
597+
pjmedia_frame frame, out_frame;
598+
599+
pj_bzero(&out_frame, sizeof(out_frame));
600+
out_frame.buf = buf;
601+
out_frame.size = MAX_BUF_SIZE;
602+
603+
/* Decode and write to AVI file */
604+
pj_bzero(&frame, sizeof(frame));
605+
frame.buf = pkt0.payload;
606+
frame.size = pkt0.payload_len;
607+
T( pjmedia_vid_codec_decode(app.vcodec, 1, &frame,
608+
MAX_BUF_SIZE, &out_frame) );
609+
if (out_frame.type == PJMEDIA_FRAME_TYPE_VIDEO && out_frame.size) {
610+
if (!avi_streams && app.vfmt.id == PJMEDIA_FORMAT_I420) {
611+
/* Open AVI file */
612+
if (pj_stristr(&args->wav_filename, &AVI)) {
613+
T( pjmedia_avi_writer_create_streams(app.pool,
614+
args->wav_filename.ptr,
615+
1000 * 1024 * 1024, /* max file size */
616+
1, &app.vfmt, 0,
617+
&avi_streams) );
618+
pj_assert(avi_streams->streams[0]);
619+
} else {
620+
err_exit("invalid output file", PJ_EINVAL);
621+
}
622+
}
623+
624+
if (avi_streams && avi_streams->streams[0])
625+
T( pjmedia_port_put_frame(avi_streams->streams[0], &out_frame) );
626+
}
627+
628+
/* Read next packet */
629+
if (!read_rtp(pkt1.buffer, sizeof(pkt1.buffer), &pkt1.rtp,
630+
&pkt1.payload, &pkt1.payload_len, PJ_TRUE)) {
631+
break;
632+
}
633+
634+
/* Next */
635+
pkt0 = pkt1;
636+
pkt0.rtp = (pjmedia_rtp_hdr*)pkt0.buffer;
637+
pkt0.payload = pkt0.buffer + (pkt1.payload - pkt1.buffer);
638+
}
639+
640+
if (avi_streams && avi_streams->streams[0])
641+
pjmedia_port_destroy(avi_streams->streams[0]);
642+
643+
#else
644+
PJ_UNUSED_ARG(args);
645+
#endif
646+
}
647+
648+
460649
int main(int argc, char *argv[])
461650
{
462651
pj_str_t input;
@@ -469,6 +658,7 @@ int main(int argc, char *argv[])
469658
OPT_DST_IP,
470659
OPT_SRC_PORT,
471660
OPT_DST_PORT,
661+
OPT_VIDEO,
472662
OPT_CODEC,
473663
OPT_PLAY_DEV_ID,
474664
OPT_CODEC_FMTP,
@@ -486,6 +676,9 @@ int main(int argc, char *argv[])
486676
{ "dst-ip", 1, 0, OPT_DST_IP },
487677
{ "src-port", 1, 0, OPT_SRC_PORT },
488678
{ "dst-port", 1, 0, OPT_DST_PORT },
679+
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
680+
{ "video", 0, 0, OPT_VIDEO },
681+
#endif
489682
{ "codec", 1, 0, OPT_CODEC },
490683
{ "play-dev-id", 1, 0, OPT_PLAY_DEV_ID },
491684
{ "codec-fmtp", 1, 0, OPT_CODEC_FMTP },
@@ -499,8 +692,7 @@ int main(int argc, char *argv[])
499692
int option_index;
500693
char key_bin[32];
501694

502-
args.srtp_crypto.slen = args.srtp_key.slen = 0;
503-
args.codec.slen = 0;
695+
pj_bzero(&args, sizeof(args));
504696
args.dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
505697
#if PJMEDIA_HAS_OPUS_CODEC
506698
args.opus_clock_rate = -1;
@@ -553,6 +745,11 @@ int main(int argc, char *argv[])
553745
case OPT_CODEC:
554746
args.codec = pj_str(pj_optarg);
555747
break;
748+
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
749+
case OPT_VIDEO:
750+
args.video = PJ_TRUE;
751+
break;
752+
#endif
556753
case OPT_PLAY_DEV_ID:
557754
args.dev_id = atoi(pj_optarg);
558755
break;
@@ -595,10 +792,35 @@ int main(int argc, char *argv[])
595792
T( pjlib_util_init() );
596793
T( pjmedia_endpt_create(&app.cp.factory, NULL, 0, &app.mept) );
597794

795+
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
796+
797+
/* Video subsystem init */
798+
T( pjmedia_vid_dev_subsys_init(&app.cp.factory) );
799+
T( pjmedia_converter_mgr_create(app.pool, NULL) );
800+
T( pjmedia_event_mgr_create(app.pool, 0, NULL) );
801+
T( pjmedia_vid_codec_mgr_create(app.pool, NULL) );
802+
T (pjmedia_video_format_mgr_create(app.pool, 64, 0, NULL) );
803+
804+
#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0
805+
T( pjmedia_codec_openh264_vid_init(NULL, &app.cp.factory) );
806+
#endif
807+
#if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0
808+
T( pjmedia_codec_vpx_vid_init(NULL, &app.cp.factory) );
809+
#endif
810+
#if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0
811+
T( pjmedia_codec_ffmpeg_vid_init(NULL, &app.cp.factory) );
812+
#endif
813+
814+
#endif
815+
598816
T( pj_pcap_open(app.pool, input.ptr, &app.pcap) );
599817
T( pj_pcap_set_filter(app.pcap, &filter) );
600818

601-
pcap2wav(&args);
819+
if (args.video) {
820+
pcap2avi(&args);
821+
} else {
822+
pcap2wav(&args);
823+
}
602824

603825
cleanup();
604826
return 0;

0 commit comments

Comments
 (0)