Commit c6dffb03 authored by François Grisez's avatar François Grisez

Add a new PCAP scenario tester testing H264 decoders

This new tester tests that decoders set to freeze
the output picture on decoding error do so.
parent b0542167
......@@ -61,6 +61,7 @@ set(SCENARIO_FILES
scenarios/firstvalid.pcapng
scenarios/opus-edge-congestion20_60_40.pcapng
scenarios/h264_one_nalu_per_frame.pcap
scenarios/h264_one_nalu_per_frame_with_corrupted_idr.pcap
scenarios/poor_jitter_quality_22_filter.pcapng
scenarios/rtp-120late-1000total.pcapng
scenarios/rtp-240late-2000total.pcapng
......
......@@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include <strings.h>
#include <bctoolbox/tester.h>
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/msticker.h"
......@@ -25,6 +25,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "mediastreamer2/msvideo.h"
#include "mediastreamer-config.h"
typedef struct {
bool_t first_image_decoded;
int decoding_errors_count;
int pli_requests_count;
} _DecodingStats;
typedef enum {
_TestParamsNone = 0,
_TestParamsDecodable = 1<<0,
_TestParamsCorruptions = 1<<1,
_TestParamsFreezeOnError = 1<<2
} _TestParams;
static MSFactory *_factory = NULL;
static int tester_before_all(void) {
......@@ -44,22 +57,48 @@ static void player_events_cb(void *userdata, MSFilter *player, unsigned int id,
}
static void decoder_events_cb(void *userdata, MSFilter *decoder, unsigned int id, void *arg) {
if(id == MS_VIDEO_DECODER_FIRST_IMAGE_DECODED) {
*(bool_t *)userdata = TRUE;
_DecodingStats *stats = (_DecodingStats *)userdata;
switch (id) {
case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
stats->first_image_decoded = TRUE;
break;
case MS_VIDEO_DECODER_DECODING_ERRORS:
stats->decoding_errors_count++;
break;
case MS_VIDEO_DECODER_SEND_PLI:
stats->pli_requests_count++;
break;
default:
break;
}
}
static bool_t _ms_decoder_require_freeze_on_error(MSFilter *decoder) {
bool_t freeze_on_error_enabled;
if (ms_filter_call_method(decoder, MS_VIDEO_DECODER_FREEZE_ON_ERROR_ENABLED, &freeze_on_error_enabled) != 0) return FALSE;
if (!freeze_on_error_enabled) {
freeze_on_error_enabled = TRUE;
if (ms_filter_call_method(decoder, MS_VIDEO_DECODER_FREEZE_ON_ERROR, &freeze_on_error_enabled) != 0) return FALSE;
}
return TRUE;
}
static void play_scenario(MSFilterDesc *decoder_desc, const char *pcap_scenario_file, MSFilter *output) {
static void play_scenario(MSFilterDesc *decoder_desc, const char *pcap_scenario_file, MSFilter *output, _TestParams params) {
MSFilter *player = ms_factory_create_filter(_factory, MS_FILE_PLAYER_ID);
MSFilter *decoder = ms_factory_create_filter_from_desc(_factory, decoder_desc);
MSTicker *ticker = ms_ticker_new();
bool_t eof = FALSE;
bool_t first_frame_decoded = FALSE;
_DecodingStats decoder_stats = {0};
int sample_rate = 90000;
const int sleep_time = 100*1000;
int err;
BC_ASSERT_PTR_NOT_NULL(decoder);
if(decoder == NULL) goto end;
if ((params & _TestParamsFreezeOnError) && !_ms_decoder_require_freeze_on_error(decoder)) {
ms_warning("%s filter does not support 'freeze on error' mode. Aborting test", decoder->desc->name);
goto end;
}
ms_filter_call_method(player, MS_FILTER_SET_SAMPLE_RATE, &sample_rate);
err = ms_filter_call_method(player, MS_PLAYER_OPEN, (void *)pcap_scenario_file);
......@@ -67,7 +106,7 @@ static void play_scenario(MSFilterDesc *decoder_desc, const char *pcap_scenario_
if (err != 0) goto end;
ms_filter_add_notify_callback(player, player_events_cb, &eof, TRUE);
ms_filter_add_notify_callback(decoder, decoder_events_cb, &first_frame_decoded, TRUE);
ms_filter_add_notify_callback(decoder, decoder_events_cb, &decoder_stats, TRUE);
ms_filter_link(player, 0, decoder, 0);
ms_filter_link(decoder, 0, output, 0);
......@@ -75,14 +114,27 @@ static void play_scenario(MSFilterDesc *decoder_desc, const char *pcap_scenario_
ms_filter_call_method_noarg(player, MS_PLAYER_START);
while(!eof) {
ms_usleep(200);
ms_usleep(sleep_time);
}
ms_usleep(sleep_time);
ms_ticker_detach(ticker, player);
ms_filter_unlink(player, 0, decoder, 0);
ms_filter_unlink(decoder, 0, output, 0);
BC_ASSERT(first_frame_decoded);
if (params & _TestParamsDecodable) {
BC_ASSERT(decoder_stats.first_image_decoded);
} else {
BC_ASSERT(!decoder_stats.first_image_decoded);
}
if (params & _TestParamsCorruptions) {
int errors_count = decoder_stats.decoding_errors_count + decoder_stats.pli_requests_count;
BC_ASSERT_NOT_EQUAL(errors_count, 0, int, "%d");
} else {
BC_ASSERT_EQUAL(decoder_stats.decoding_errors_count, 0, int, "%d");
BC_ASSERT_EQUAL(decoder_stats.pli_requests_count, 0, int, "%d");
}
end:
if(player) ms_filter_destroy(player);
......@@ -90,7 +142,7 @@ end:
if(ticker) ms_ticker_destroy(ticker);
}
static void play_scenario_for_all_decoders(const char *mime, const char *pcap_scenario_file) {
static void play_scenario_for_all_decoders(const char *mime, const char *pcap_scenario_file, _TestParams params) {
MSFilter *output = ms_factory_create_filter(_factory, MS_VOID_SINK_ID);
bctbx_list_t *it;
for(it=_factory->desc_list; it!=NULL; it=it->next) {
......@@ -98,23 +150,24 @@ static void play_scenario_for_all_decoders(const char *mime, const char *pcap_sc
if(desc->category == MS_FILTER_DECODER && strcasecmp(desc->enc_fmt, mime) == 0) {
ms_message("\n");
ms_message("Playing scenario with '%s' decoder", desc->name);
play_scenario(desc, pcap_scenario_file, output);
play_scenario(desc, pcap_scenario_file, output, params);
}
}
ms_filter_destroy(output);
}
#define scenario_test(scenario_name) \
#define scenario_test(scenario_name, params) \
static void scenario_name(void) { \
char *scenario_pcap_file = ms_strdup_printf("%s/%s", \
bc_tester_get_resource_dir_prefix(), \
"scenarios/" #scenario_name ".pcap"); \
play_scenario_for_all_decoders("h264", scenario_pcap_file); \
play_scenario_for_all_decoders("h264", scenario_pcap_file, params); \
ms_free(scenario_pcap_file); \
}
scenario_test(h264_missing_pps_in_second_i_frame)
scenario_test(h264_one_nalu_per_frame)
scenario_test(h264_missing_pps_in_second_i_frame, _TestParamsDecodable | _TestParamsCorruptions)
scenario_test(h264_one_nalu_per_frame, _TestParamsDecodable | _TestParamsCorruptions)
scenario_test(h264_one_nalu_per_frame_with_corrupted_idr, _TestParamsCorruptions | _TestParamsFreezeOnError)
#ifdef HAVE_MATROSKA
static void play_scenario_with_mkv_recorder(const char *pcap_scenario_file, const MSFmtDescriptor *fmt) {
......@@ -161,10 +214,11 @@ void h264_one_nalu_per_frame_with_mkv_recorder(void) {
#endif
static test_t tests[] = {
{ "H264: missing PPS in second i-frame scenario" , h264_missing_pps_in_second_i_frame },
{ "H264: one NALu per frame scenario" , h264_one_nalu_per_frame },
{ "H264: missing PPS in second i-frame scenario" , h264_missing_pps_in_second_i_frame },
{ "H264: one NALu per frame scenario" , h264_one_nalu_per_frame },
{ "H264: one NALu per frame with corrupted IDR scenario" , h264_one_nalu_per_frame_with_corrupted_idr },
#ifdef HAVE_MATROSKA
{ "H264: one NALu per frame scenario (MKV recorder)" , h264_one_nalu_per_frame_with_mkv_recorder }
{ "H264: one NALu per frame scenario (MKV recorder)" , h264_one_nalu_per_frame_with_mkv_recorder }
#endif
};
......
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