Commit 0c2e81ed authored by Ghislain MARY's avatar Ghislain MARY

Add pcap_playback tool.

parent 709b5f4d
......@@ -715,6 +715,17 @@ this check.
fi
AC_PATH_PROG(PCAP,pcap-config,false)
AM_CONDITIONAL(HAVE_PCAP, test $PCAP != false)
if test $PCAP != false ; then
PCAP_LIBS=`pcap-config --libs`
PCAP_CFLAGS=`pcap-config --cflags`
PCAP_CFLAGS="$PCAP_CFLAGS -DHAVE_PCAP"
AC_SUBST(PCAP_LIBS)
AC_SUBST(PCAP_CFLAGS)
fi
AC_ARG_ENABLE(tests,
[AS_HELP_STRING([--enable-tests], [Enable compilation of tests (default=yes)])],
[case "${enableval}" in
......
......@@ -258,7 +258,8 @@ AM_CFLAGS= $(ORTP_CFLAGS) \
$(STRICT_OPTIONS) \
$(LIBPULSE_CFLAGS) \
$(SPANDSP_CFLAGS) \
$(MSSILK_CFLAGS)
$(MSSILK_CFLAGS) \
$(PCAP_CFLAGS)
......@@ -285,7 +286,8 @@ libmediastreamer_voip_la_LIBADD= libmediastreamer_base.la \
$(GSM_LIBS) \
$(LIBV4L1_LIBS) \
$(LIBV4L2_LIBS) \
$(SPANDSP_LIBS)
$(SPANDSP_LIBS) \
$(PCAP_LIBS)
if BUILD_VP8
AM_CFLAGS+=$(VP8_CFLAGS)
......
......@@ -25,6 +25,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "mediastreamer2/waveheader.h"
#include "mediastreamer2/msticker.h"
#ifdef HAVE_PCAP
#include <pcap/pcap.h>
#endif
static int player_close(MSFilter *f, void *arg);
......@@ -40,6 +44,14 @@ struct _PlayerData{
int samplesize;
uint32_t ts;
bool_t swap;
#ifdef HAVE_PCAP
pcap_t *pcap;
struct pcap_pkthdr *pcap_hdr;
const u_char *pcap_data;
bool_t pcap_started;
uint64_t pcap_initial_ts;
uint16_t pcap_seq;
#endif
};
typedef struct _PlayerData PlayerData;
......@@ -57,6 +69,14 @@ static void player_init(MSFilter *f){
d->pause_time=0;
d->count=0;
d->ts=0;
#ifdef HAVE_PCAP
d->pcap = NULL;
d->pcap_hdr = NULL;
d->pcap_data = NULL;
d->pcap_started = FALSE;
d->pcap_initial_ts = 0;
d->pcap_seq = 0;
#endif
f->data=d;
}
......@@ -146,6 +166,19 @@ static int player_open(MSFilter *f, void *arg){
d->state=MSPlayerPaused;
d->fd=fd;
d->ts=0;
#ifdef HAVE_PCAP
d->pcap = NULL;
d->pcap_started = FALSE;
if (strstr(file, ".pcap")) {
char err[PCAP_ERRBUF_SIZE];
d->pcap = pcap_open_offline(file, err);
if (d->pcap == NULL) {
ms_error("Failed to open pcap file: %s", err);
close(fd);
return -1;
}
} else
#endif
if (read_wav_header(d)!=0 && strstr(file,".wav")){
ms_warning("File %s has .wav extension but wav header could be found.",file);
}
......@@ -184,6 +217,9 @@ static int player_pause(MSFilter *f, void *arg){
static int player_close(MSFilter *f, void *arg){
PlayerData *d=(PlayerData*)f->data;
player_stop(f,NULL);
#ifdef HAVE_PCAP
if (d->pcap) pcap_close(d->pcap);
#endif
if (d->fd>=0) close(d->fd);
d->fd=-1;
d->state=MSPlayerClosed;
......@@ -229,43 +265,88 @@ static void player_process(MSFilter *f){
d->count++;
ms_filter_lock(f);
if (d->state==MSPlayerPlaying){
int err;
mblk_t *om=allocb(bytes,0);
if (d->pause_time>0){
err=bytes;
memset(om->b_wptr,0,bytes);
d->pause_time-=f->ticker->interval;
}else{
err=read(d->fd,om->b_wptr,bytes);
if (d->swap) swap_bytes(om->b_wptr,bytes);
}
if (err>=0){
if (err!=0){
if (err<bytes)
memset(om->b_wptr+err,0,bytes-err);
om->b_wptr+=bytes;
mblk_set_timestamp_info(om,d->ts);
d->ts+=nsamples;
ms_queue_put(f->outputs[0],om);
}else freemsg(om);
if (err<bytes){
ms_filter_notify_no_arg(f,MS_FILE_PLAYER_EOF);
lseek(d->fd,d->hsize,SEEK_SET);
/* special value for playing file only once */
if (d->loop_after<0)
{
d->state=MSPlayerPaused;
ms_filter_unlock(f);
return;
#ifdef HAVE_PCAP
if (d->pcap) {
int res;
bool_t cont = TRUE;
do {
if (d->pcap_data && d->pcap_hdr) {
/* The PCAP data has already been read at previous interation. */
res = 1;
} else {
res = pcap_next_ex(d->pcap, &d->pcap_hdr, &d->pcap_data);
}
if (d->loop_after>=0){
d->pause_time=d->loop_after;
if (res == -2) {
ms_filter_notify_no_arg(f, MS_FILE_PLAYER_EOF);
} else if (res > 0) {
if (d->pcap_hdr->caplen > 54) {
int bytes = d->pcap_hdr->caplen - 54;
mblk_t *om;
uint64_t ts = (uint64_t)(d->pcap_hdr->ts.tv_sec * 1000) + (uint64_t)(d->pcap_hdr->ts.tv_usec / 1000);
uint16_t pcap_seq = ntohs(*((uint16_t*)&d->pcap_data[44]));
if (d->pcap_started == FALSE) {
d->pcap_started = TRUE;
d->pcap_initial_ts = ts;
d->pcap_seq = pcap_seq;
}
if ((d->pcap_initial_ts + f->ticker->time) > ts) {
if (pcap_seq >= d->pcap_seq) {
om = allocb(bytes, 0);
memcpy(om->b_wptr, &d->pcap_data[54], bytes);
om->b_wptr += bytes;
mblk_set_timestamp_info(om, ts);
ms_queue_put(f->outputs[0], om);
}
d->pcap_seq = pcap_seq;
d->pcap_hdr = NULL;
d->pcap_data = NULL;
} else {
cont = FALSE;
}
}
}
} while ((res > 0) && cont);
} else
#endif
{
int err;
mblk_t *om=allocb(bytes,0);
if (d->pause_time>0){
err=bytes;
memset(om->b_wptr,0,bytes);
d->pause_time-=f->ticker->interval;
}else{
err=read(d->fd,om->b_wptr,bytes);
if (d->swap) swap_bytes(om->b_wptr,bytes);
}
if (err>=0){
if (err!=0){
if (err<bytes)
memset(om->b_wptr+err,0,bytes-err);
om->b_wptr+=bytes;
mblk_set_timestamp_info(om,d->ts);
d->ts+=nsamples;
ms_queue_put(f->outputs[0],om);
}else freemsg(om);
if (err<bytes){
ms_filter_notify_no_arg(f,MS_FILE_PLAYER_EOF);
lseek(d->fd,d->hsize,SEEK_SET);
/* special value for playing file only once */
if (d->loop_after<0)
{
d->state=MSPlayerPaused;
ms_filter_unlock(f);
return;
}
if (d->loop_after>=0){
d->pause_time=d->loop_after;
}
}
}else{
ms_warning("Fail to read %i bytes: %s",bytes,strerror(errno));
}
}else{
ms_warning("Fail to read %i bytes: %s",bytes,strerror(errno));
}
}
ms_filter_unlock(f);
......
......@@ -33,7 +33,8 @@ TEST_DEPLIBS=\
$(GSM_LIBS) \
$(THEORA_LIBS) \
$(VP8_LIBS) \
$(VIDEO_LIBS)
$(VIDEO_LIBS) \
$(PCAP_LIBS)
if BUILD_VOIP_LIBRARY
TEST_DEPLIBS+=$(top_builddir)/src/libmediastreamer_voip.la
......@@ -42,10 +43,15 @@ endif
if ORTP_ENABLED
if MS2_FILTERS
bin_PROGRAMS=mediastream
if HAVE_PCAP
bin_PROGRAMS+=pcap_playback
endif
mediastream_SOURCES = mediastream.c
mediastream_SOURCES = mediastream.c
pcap_playback_SOURCES = pcap_playback.c
mediastream_LDADD=$(TEST_DEPLIBS)
pcap_playback_LDADD=$(TEST_DEPLIBS)
if BUILD_MACOSX
......@@ -54,6 +60,7 @@ noinst_LTLIBRARIES=libmediastream_cocoa.la
libmediastream_cocoa_la_SOURCES = mediastream_cocoa.m
mediastream_LDADD+=libmediastream_cocoa.la
pcap_playback_LDADD+=libmediastream_cocoa.la
endif BUILD_MACOSX
......@@ -67,7 +74,7 @@ if !ORTP_ENABLED
INCLUDES+=-I$(top_srcdir)/src/ortp-deps/
endif
AM_CFLAGS=$(ORTP_CFLAGS) $(STRICT_OPTIONS) $(VIDEO_CFLAGS)
AM_CFLAGS=$(ORTP_CFLAGS) $(STRICT_OPTIONS) $(VIDEO_CFLAGS) $(PCAP_CFLAGS)
AM_LDFLAGS=-export-dynamic
endif
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2012 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "mediastreamer-config.h"
#endif
#include <math.h>
#include "mediastreamer2/mediastream.h"
#include "mediastreamer2/msequalizer.h"
#include "mediastreamer2/msfileplayer.h"
#include "mediastreamer2/msvolume.h"
#ifdef VIDEO_ENABLED
#include "mediastreamer2/msv4l.h"
#endif
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#ifndef WIN32
#include <unistd.h>
#else
#include <malloc.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ortp/b64.h>
static int cond = 1;
typedef struct _MediastreamDatas {
int payload;
bool_t is_verbose;
char *playback_card;
char *infile;
PayloadType *pt;
RtpProfile *profile;
MSFilter *read;
MSFilter *write;
MSFilter *decoder;
MSTicker *ticker;
} MediastreamDatas;
// MAIN METHODS
/* init default arguments */
static MediastreamDatas *init_default_args();
/* parse args */
static bool_t parse_args(int argc, char **argv, MediastreamDatas *out);
/* setup streams */
static void setup_media_streams(MediastreamDatas *args);
/* run loop */
static void run_non_interactive_loop(MediastreamDatas *args);
/* exit */
static void clear_mediastreams(MediastreamDatas *args);
// HELPER METHODS
static void stop_handler(int signum);
const char *usage = "pcap_playback --infile <pcapfile>\n"
"--payload <payload type number or payload name like 'audio/pmcu/8000'>\n"
"[ --playback-card <name> ]\n"
"[ --verbose (most verbose messages) ]\n"
;
int main(int argc, char *argv[])
{
MediastreamDatas *args;
cond = 1;
args = init_default_args();
if (!parse_args(argc, argv, args))
return 0;
setup_media_streams(args);
run_non_interactive_loop(args);
clear_mediastreams(args);
free(args);
return 0;
}
static MediastreamDatas *init_default_args()
{
MediastreamDatas *args = (MediastreamDatas *) ms_malloc0(sizeof(MediastreamDatas));
args->payload = 0;
args->is_verbose = FALSE;
args->playback_card = NULL;
args->infile = NULL;
args->pt = NULL;
args->profile = NULL;
args->read = NULL;
args->write = NULL;
args->decoder = NULL;
args->ticker = NULL;
return args;
}
static bool_t parse_args(int argc, char **argv, MediastreamDatas *out)
{
int i;
if (argc < 2) {
printf("%s", usage);
return FALSE;
}
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "--payload") == 0) {
i++;
if (isdigit(argv[i][0])) {
out->payload = atoi(argv[i]);
}
} else if (strcmp(argv[i], "--playback-card") == 0) {
i++;
out->playback_card = argv[i];
} else if (strcmp(argv[i], "--infile") == 0) {
i++;
out->infile = argv[i];
} else if (strcmp(argv[i], "--verbose") == 0) {
out->is_verbose = TRUE;
} else if (strcmp(argv[i], "--help") == 0) {
printf("%s", usage);
return FALSE;
}
}
return TRUE;
}
static void reader_notify_cb(void *user_data, MSFilter *f, unsigned int event, void *eventdata)
{
if (event == MS_FILE_PLAYER_EOF) {
cond = 0;
}
}
static void setup_media_streams(MediastreamDatas *args)
{
MSConnectionHelper h;
MSTickerParams params = {0};
/*create the rtp session */
ortp_init();
if (args->is_verbose) {
ortp_set_log_level_mask(ORTP_DEBUG | ORTP_MESSAGE | ORTP_WARNING | ORTP_ERROR | ORTP_FATAL);
} else {
ortp_set_log_level_mask(ORTP_MESSAGE | ORTP_WARNING | ORTP_ERROR | ORTP_FATAL);
}
rtp_profile_set_payload(&av_profile, 110, &payload_type_speex_nb);
rtp_profile_set_payload(&av_profile, 111, &payload_type_speex_wb);
rtp_profile_set_payload(&av_profile, 112, &payload_type_ilbc);
rtp_profile_set_payload(&av_profile, 113, &payload_type_amr);
rtp_profile_set_payload(&av_profile, 115, &payload_type_lpc1015);
#ifdef VIDEO_ENABLED
rtp_profile_set_payload(&av_profile, 26, &payload_type_jpeg);
rtp_profile_set_payload(&av_profile, 98, &payload_type_h263_1998);
rtp_profile_set_payload(&av_profile, 97, &payload_type_theora);
rtp_profile_set_payload(&av_profile, 99, &payload_type_mp4v);
rtp_profile_set_payload(&av_profile, 100, &payload_type_x_snow);
rtp_profile_set_payload(&av_profile, 102, &payload_type_h264);
rtp_profile_set_payload(&av_profile, 103, &payload_type_vp8);
#endif
args->profile = rtp_profile_clone_full(&av_profile);
ms_init();
ms_filter_enable_statistics(TRUE);
ms_filter_reset_statistics();
signal(SIGINT, stop_handler);
args->pt = rtp_profile_get_payload(args->profile, args->payload);
if (args->pt == NULL) {
printf("Error: no payload defined with number %i.", args->payload);
exit(-1);
}
if (args->pt->type == PAYLOAD_VIDEO) {
#ifdef VIDEO_ENABLED
const char *display_name;
MSPixFmt format;
MSVideoSize disp_size;
int tmp = 1;
#if defined(HAVE_GL)
display_name = "MSGLXVideo";
#elif defined(HAVE_XV)
display_name = "MSX11Video";
#else
display_name = "MSVideoOut";
#endif
args->read = ms_filter_new(MS_FILE_PLAYER_ID);
args->write = ms_filter_new_from_name(display_name);
args->decoder = ms_filter_create_decoder(args->pt->mime_type);
ms_filter_call_method_noarg(args->read, MS_FILE_PLAYER_CLOSE);
ms_filter_call_method(args->read, MS_FILE_PLAYER_OPEN, args->infile);
ms_filter_call_method_noarg(args->read, MS_FILE_PLAYER_START);
ms_filter_set_notify_callback(args->read, reader_notify_cb, NULL);
/*force the decoder to output YUV420P */
format = MS_YUV420P;
ms_filter_call_method(args->decoder, MS_FILTER_SET_PIX_FMT, &format);
/*configure the display window */
disp_size.width = MS_VIDEO_SIZE_CIF_W;
disp_size.height = MS_VIDEO_SIZE_CIF_H;
ms_filter_call_method(args->write, MS_FILTER_SET_VIDEO_SIZE, &disp_size);
ms_filter_call_method(args->write, MS_VIDEO_DISPLAY_ENABLE_AUTOFIT, &tmp);
ms_filter_call_method(args->write, MS_FILTER_SET_PIX_FMT, &format);
params.name = "Video MSTicker";
params.prio = MS_TICKER_PRIO_REALTIME;
args->ticker = ms_ticker_new_with_params(&params);
ms_connection_helper_start(&h);
ms_connection_helper_link(&h, args->read, -1, 0);
ms_connection_helper_link(&h, args->decoder, 0, 0);
ms_connection_helper_link(&h, args->write, 0, -1);
ms_ticker_attach(args->ticker, args->read);
#else
printf("Error: video support not compiled.\n");
#endif
} else {
MSSndCardManager *manager = ms_snd_card_manager_get();
MSSndCard *play = args->playback_card == NULL ? ms_snd_card_manager_get_default_playback_card(manager) :
ms_snd_card_manager_get_card(manager, args->playback_card);
args->read = ms_filter_new(MS_FILE_PLAYER_ID);
args->write = ms_snd_card_create_writer(play);
args->decoder = ms_filter_create_decoder(args->pt->mime_type);
ms_filter_call_method_noarg(args->read, MS_FILE_PLAYER_CLOSE);
ms_filter_call_method(args->read, MS_FILE_PLAYER_OPEN, args->infile);
ms_filter_call_method_noarg(args->read, MS_FILE_PLAYER_START);
ms_filter_call_method(args->decoder, MS_FILTER_SET_SAMPLE_RATE, &args->pt->clock_rate);
ms_filter_call_method(args->decoder, MS_FILTER_SET_NCHANNELS, &args->pt->channels);
ms_filter_call_method(args->write, MS_FILTER_SET_SAMPLE_RATE, &args->pt->clock_rate);
ms_filter_call_method(args->write, MS_FILTER_SET_NCHANNELS, &args->pt->channels);
params.name = "Audio MSTicker";
params.prio = MS_TICKER_PRIO_REALTIME;
args->ticker = ms_ticker_new_with_params(&params);
ms_connection_helper_start(&h);
ms_connection_helper_link(&h, args->read, -1, 0);
ms_connection_helper_link(&h, args->decoder, 0, 0);
ms_connection_helper_link(&h, args->write, 0, -1);
ms_ticker_attach(args->ticker, args->read);
}
}
static void run_non_interactive_loop(MediastreamDatas *args)
{
while (cond) {
int n;
for (n = 0; n < 100; ++n) {
#ifdef WIN32
MSG msg;
Sleep(10);
while (PeekMessage(&msg, NULL, 0, 0, 1)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#else
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 10000000;
nanosleep(&ts, NULL);
#endif
}
}
}
static void clear_mediastreams(MediastreamDatas *args)
{
MSConnectionHelper h;
ms_message("stopping all...\n");
if (args->read) {
ms_ticker_detach(args->ticker, args->read);
ms_connection_helper_start(&h);
ms_connection_helper_unlink(&h, args->read, -1, 0);
ms_connection_helper_unlink(&h, args->decoder, 0, 0);
ms_connection_helper_unlink(&h, args->write, 0, -1);
}
rtp_profile_destroy(args->profile);
ms_exit();
}
// HELPER METHODS
static void stop_handler(int signum)
{
cond--;
if (cond < 0) {
ms_error("Brutal exit (%d)\n", cond);
exit(-1);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment