@@ -1104,6 +1104,57 @@ static int media_player_find_file_begin(struct media_player *mp) {
1104
1104
static bool media_player_read_packet (struct media_player * mp ) {
1105
1105
if (!mp -> coder .fmtctx )
1106
1106
return true;
1107
+ // Handle raw RTP file playback
1108
+ if (mp -> coder .audio_raw_rtp_mode ) {
1109
+ // Check if we have more data
1110
+ if (mp -> coder .audio_raw_rtp_pos >= mp -> coder .audio_raw_rtp_data .len ) {
1111
+ ilog (LOG_DEBUG , "End of raw RTP file reached" );
1112
+ return true;
1113
+ }
1114
+
1115
+ // Calculate bytes to read (one frame)
1116
+ size_t bytes_to_read = MIN (mp -> coder .audio_raw_rtp_frame_size ,
1117
+ mp -> coder .audio_raw_rtp_data .len - mp -> coder .audio_raw_rtp_pos );
1118
+ // Allocate packet
1119
+ mp -> coder .pkt = av_packet_alloc ();
1120
+ if (!mp -> coder .pkt ) {
1121
+ ilog (LOG_ERR , "Failed to allocate packet" );
1122
+ return true;
1123
+ }
1124
+
1125
+ // Fill packet with raw RTP data
1126
+ if (av_new_packet (mp -> coder .pkt , bytes_to_read ) < 0 ) {
1127
+ ilog (LOG_ERR , "Failed to create packet" );
1128
+ av_packet_free (& mp -> coder .pkt );
1129
+ return true;
1130
+ }
1131
+ if (bytes_to_read > 0 ) {
1132
+ memcpy (mp -> coder .pkt -> data ,
1133
+ mp -> coder .audio_raw_rtp_data .s + mp -> coder .audio_raw_rtp_pos ,
1134
+ bytes_to_read );
1135
+ }
1136
+ if (bytes_to_read < mp -> coder .audio_raw_rtp_frame_size ) {
1137
+ // Pad with silence if needed
1138
+ memset (mp -> coder .pkt -> data + bytes_to_read ,
1139
+ mp -> coder .silence_byte ,
1140
+ mp -> coder .audio_raw_rtp_frame_size - bytes_to_read );
1141
+ ilog (LOG_DEBUG , "Padding %zu bytes of silence" ,
1142
+ mp -> coder .audio_raw_rtp_frame_size - bytes_to_read );
1143
+ }
1144
+ mp -> coder .audio_raw_rtp_pos += bytes_to_read ;
1145
+
1146
+ // Simulate packet timing (20ms per frame)
1147
+ mp -> coder .pkt -> pts = mp -> last_frame_ts ;
1148
+ mp -> coder .pkt -> duration = 160 ;
1149
+ mp -> last_frame_ts += mp -> coder .pkt -> duration ;
1150
+ // Process packet
1151
+ media_player_coder_add_packet (& mp -> coder , media_player_add_packet , mp );
1152
+ av_packet_free (& mp -> coder .pkt );
1153
+
1154
+ // Schedule next read in 20ms
1155
+ mp -> next_run = rtpe_now + 20000 ;
1156
+ return false;
1157
+ }
1107
1158
1108
1159
int ret = av_read_frame (mp -> coder .fmtctx , mp -> coder .pkt );
1109
1160
if (ret < 0 ) {
@@ -1232,17 +1283,18 @@ static bool media_player_play_start(struct media_player *mp, const rtp_payload_t
1232
1283
return true;
1233
1284
1234
1285
mp -> next_run = rtpe_now ;
1235
- // give ourselves a bit of a head start with decoding
1236
- mp -> next_run -= 50000 ;
1237
-
1238
- // if start_pos is positive, try to seek to that position
1239
- if (mp -> opts .start_pos > 0 ) {
1240
- ilog (LOG_DEBUG , "Seeking to position %lli" , mp -> opts .start_pos );
1241
- av_seek_frame (mp -> coder .fmtctx , 0 , mp -> opts .start_pos , 0 );
1286
+ if (!mp -> coder .audio_raw_rtp_mode ) {
1287
+ // give ourselves a bit of a head start with decoding
1288
+ mp -> next_run -= 50000 ;
1289
+
1290
+ // if start_pos is positive, try to seek to that position
1291
+ if (mp -> opts .start_pos > 0 ) {
1292
+ ilog (LOG_DEBUG , "Seeking to position %lli" , mp -> opts .start_pos );
1293
+ av_seek_frame (mp -> coder .fmtctx , 0 , mp -> opts .start_pos , 0 );
1294
+ }
1295
+ else // in case this is a repeated start
1296
+ av_seek_frame (mp -> coder .fmtctx , 0 , 0 , 0 );
1242
1297
}
1243
- else // in case this is a repeated start
1244
- av_seek_frame (mp -> coder .fmtctx , 0 , 0 , 0 );
1245
-
1246
1298
media_player_read_packet (mp );
1247
1299
1248
1300
return true;
@@ -1475,6 +1527,97 @@ static mp_cached_code __media_player_add_file(struct media_player *mp,
1475
1527
return MPC_OK ;
1476
1528
}
1477
1529
1530
+ static bool __media_player_open_audio_raw_rtp_file (struct media_player * mp , media_player_opts_t opts ) {
1531
+ // Validate codec
1532
+ if (!opts .audio_raw_rtp_codec .len ) {
1533
+ ilog (LOG_ERR , "Raw RTP playback requires codec specification" );
1534
+ return false;
1535
+ }
1536
+
1537
+ // Convert file path
1538
+ char file_path [PATH_MAX ];
1539
+ snprintf (file_path , sizeof (file_path ), STR_FORMAT , STR_FMT (& opts .audio_raw_rtp_file ));
1540
+
1541
+ // Open file
1542
+ FILE * f = fopen (file_path , "rb" );
1543
+ if (!f ) {
1544
+ ilog (LOG_ERR , "Failed to open raw RTP file: %s" , file_path );
1545
+ return false;
1546
+ }
1547
+
1548
+ // Get file size
1549
+ fseek (f , 0 , SEEK_END );
1550
+ long file_size = ftell (f );
1551
+ fseek (f , 0 , SEEK_SET );
1552
+ // Read entire file into memory
1553
+ char * file_data = malloc (file_size );
1554
+ if (!file_data || fread (file_data , 1 , file_size , f ) != file_size ) {
1555
+ ilog (LOG_ERR , "Failed to read raw RTP file" );
1556
+ fclose (f );
1557
+ free (file_data );
1558
+ return false;
1559
+ }
1560
+ fclose (f );
1561
+
1562
+ // Store in player context
1563
+ mp -> coder .audio_raw_rtp_data .s = file_data ;
1564
+ mp -> coder .audio_raw_rtp_data .len = file_size ;
1565
+ mp -> coder .audio_raw_rtp_pos = 0 ;
1566
+ // Set codec parameters based on input
1567
+ if (opts .audio_raw_rtp_codec .len == 4 && strncasecmp (opts .audio_raw_rtp_codec .s , "PCMU" , 4 ) == 0 ) {
1568
+ mp -> coder .audio_raw_rtp_codec = AV_CODEC_ID_PCM_MULAW ;
1569
+ mp -> coder .audio_raw_rtp_frame_size = 160 ; // 20ms frames
1570
+ mp -> coder .silence_byte = 0xFF ; // μ-law silence
1571
+ mp -> coder .time_base = (AVRational ){1 , 8000 }; // Default for 8kHz audio
1572
+ }
1573
+ else if (opts .audio_raw_rtp_codec .len == 4 && strncasecmp (opts .audio_raw_rtp_codec .s , "PCMA" , 4 ) == 0 ) {
1574
+ mp -> coder .audio_raw_rtp_codec = AV_CODEC_ID_PCM_ALAW ;
1575
+ mp -> coder .audio_raw_rtp_frame_size = 160 ; // 20ms frames
1576
+ mp -> coder .silence_byte = 0x55 ; // A-law silence
1577
+ mp -> coder .time_base = (AVRational ){1 , 8000 }; // Default for 8kHz audio
1578
+ }
1579
+ else {
1580
+ ilog (LOG_ERR , "Unsupported raw RTP codec: " STR_FORMAT , STR_FMT (& opts .audio_raw_rtp_codec ));
1581
+ free (file_data );
1582
+ return false;
1583
+ }
1584
+
1585
+ return true;
1586
+ }
1587
+
1588
+ static bool media_player_play_audio_raw_rtp_file (struct media_player * mp , media_player_opts_t opts ) {
1589
+ const rtp_payload_type * dst_pt = media_player_play_init (mp );
1590
+ if (!dst_pt )
1591
+ return false;
1592
+
1593
+ if (!__media_player_open_audio_raw_rtp_file (mp , opts ))
1594
+ return false;
1595
+
1596
+ // Set up fake format context
1597
+ mp -> coder .fmtctx = avformat_alloc_context ();
1598
+ if (!mp -> coder .fmtctx ) {
1599
+ ilog (LOG_ERR , "Failed to alloc format context" );
1600
+ return false;
1601
+ }
1602
+
1603
+ // Create a dummy stream
1604
+ AVStream * stream = avformat_new_stream (mp -> coder .fmtctx , NULL );
1605
+ if (!stream ) {
1606
+ ilog (LOG_ERR , "Failed to create stream" );
1607
+ return false;
1608
+ }
1609
+
1610
+ // Set codec parameters
1611
+ stream -> time_base = mp -> coder .time_base ;
1612
+ stream -> codecpar -> codec_id = mp -> coder .audio_raw_rtp_codec ;
1613
+ stream -> codecpar -> codec_type = AVMEDIA_TYPE_AUDIO ;
1614
+ stream -> codecpar -> channels = 1 ;
1615
+ stream -> codecpar -> channel_layout = AV_CH_LAYOUT_MONO ;
1616
+
1617
+ mp -> coder .audio_raw_rtp_mode = true;
1618
+ return media_player_play_start (mp , dst_pt , opts .codec_set );
1619
+ }
1620
+
1478
1621
// call->master_lock held in W
1479
1622
static bool media_player_play_file (struct media_player * mp , media_player_opts_t opts ) {
1480
1623
const rtp_payload_type * dst_pt = media_player_play_init (mp );
@@ -1681,6 +1824,10 @@ const char * call_play_media_for_ml(struct call_monologue *ml,
1681
1824
if (!media_player_play_db (ml -> player , opts ))
1682
1825
return "Failed to start media playback from database" ;
1683
1826
}
1827
+ else if (opts .audio_raw_rtp_file .len ) {
1828
+ if (!media_player_play_audio_raw_rtp_file (ml -> player , opts ))
1829
+ return "Failed to start audio raw RTP file playback" ;
1830
+ }
1684
1831
else
1685
1832
return "No media file specified" ;
1686
1833
return NULL ;
0 commit comments