ffmpeg中添加MMS协议流媒体播放功能
时间:2010-12-01 来源:landuochong
Ok! 最近在为公司的wifi tablet 添加mms协议流媒体的播放功能,商业协议缺少资料让我苦不堪言,另外mplayer等对mms协议的支持也是非常不理想,使得我没有可参考的代码,连蒙 带猜的终于实现了pause与seek.当然wifi tablet本身的局限性使得困难也增加不少,不管怎么样,至少我现在除了能播放mms流媒体,还能正常的seek与pause.再次抱怨一下国内技术的 落后,微软很早就宣布淘汰mms了,可是国内运营商们清一色的mms协议流媒体真是让人心寒啊!
下面是代码时间,首先我的修改基于libmms.我在此基础上的修改并不多,加了状态基,加了0x09 command.不过这点修改仍然耗费了我两个星期的时间去探索,这里现贴代码,关于ffmpeg流媒体代码的架构问题,我下次在贴出:
int mms_read_pause(mms_io_t *io, mms_t *this, int pause)
{
mms_packet_header_t header;
if(pause)
{
if (!send_command (io, this, 0x09, 1, 0xffff0100, 0))
return 0;
this->eos = MMS_PAUSE_ACKNOWLEDGE;
while (this->eos != STREAM_PAUSED)
{
get_media_packet(io, this);
}
this->eos = STREAMING;
return 0;
}
else
{
this->eos = STREAMING;
return 1;
}
}
这个是0x09命令,关于mms中这个命令的描述:
09 To Server
Prefix 1 01 00 00 00 - CommandLevel
Prefix 2 ff ff 01 00
This is sent by Media Player when the Stop button is activated. If Media Player needs to stop the stream for any reason, it sends this command. The socket connection is kept open.
void mms_close (mms_io_t *io, mms_t *this) {
int ret;
if(this->eos == STREAMING || this->eos == STREAM_PAUSED)
{
ret = send_command (io, this, 0x0D, 1, 1, 0);
}
while(this->eos != STREAM_DONE)
{
if(ret == 0)
break;
get_media_packet(io, this);
}
if (this->s != -1)
//close (this->s);
io_close(io, this->s);
if (this->url)
free(this->url);
if (this->proto)
free(this->proto);
if (this->host)
free(this->host);
if (this->user)
free(this->user);
if (this->password)
free(this->password);
if (this->uri)
free(this->uri);
free (this);
}
这个是0x0D命令,关于mms中这个命令的描述:
0D To Server
Prefix 1 01 00 00 00 - CommandLevel
Prefix 2 01 00 00 00
Cancel protocol. This can be sent if the player cancels viewing before the protocol has been fully established, or after a timeout occurs. It is also sent when media player closes the socket connection.
int mms_request_time_seek (mms_io_t *io, mms_t *this, double time_sec) {
int ret;
if (++this->packet_id_type <= ASF_MEDIA_PACKET_ID_TYPE)
this->packet_id_type = ASF_MEDIA_PACKET_ID_TYPE+1;
//return mms_request_data_packet (io, this, time_sec, 0xFFFFFFFF, 0x00FFFFFF);
// also adjust time by preroll
if(mms_request_data_packet (io, this,
time_sec+(double)(this->preroll)/1000,
0xFFFFFFFF, 0x00FFFFFF))
return -1;
this->eos = MMS_SEEK_ACKNOWLEDGE;
ret = peek_and_set_pos (io, this);
this->eos = STREAMING;
return ret;
}
static int mms_request_data_packet (mms_io_t *io, mms_t *this,
double time_sec, unsigned long first_packet, unsigned long time_msec_limit) {
/* command 0x07 */
{
mms_buffer_t command_buffer;
//mms_buffer_init(&command_buffer, this->scmd_body);
//mms_buffer_put_32 (&command_buffer, 0x00000000); /* 64 byte float timestamp */
//mms_buffer_put_32 (&command_buffer, 0x00000000);
memcpy(this->scmd_body, &time_sec, 8);
mms_buffer_init(&command_buffer, this->scmd_body+8);
mms_buffer_put_32 (&command_buffer, 0xFFFFFFFF); /* ?? */
//mms_buffer_put_32 (&command_buffer, 0xFFFFFFFF); /* first packet sequence */
mms_buffer_put_32 (&command_buffer, first_packet); /* first packet sequence */
//mms_buffer_put_8 (&command_buffer, 0xFF); /* max stream time limit (3 bytes) */
//mms_buffer_put_8 (&command_buffer, 0xFF);
//mms_buffer_put_8 (&command_buffer, 0xFF);
//mms_buffer_put_8 (&command_buffer, 0x00); /* stream time limit flag */
mms_buffer_put_32 (&command_buffer, time_msec_limit & 0x00FFFFFF);/* max stream time limit (3 bytes) */
mms_buffer_put_32 (&command_buffer, this->packet_id_type); /* asf media packet id type */
if (!send_command (io, this, 0x07, 1, 0x0001FFFF, 8+command_buffer.pos)) {
/* FIXME: de-xine-ification */
return 0;
}
}
/* TODO: adjust current_pos, considering asf_header_read */
return 1;
}
这个是0x07命令,关于mms中这个命令的描述:
07 To Server
Prefix 1 01 00 00 00 - CommandLevel
Prefix 2 ff ff 01 00 - or shows 76 04 00 00 with optional data #
Then structure data below
Start sending file from packet xx. This command is also used for resume downloads or requesting a lost packet. Also used for seeking by sending a play point value which seeks to the media time point. Includes media ‘PacketIDType’ value in pre header and the maximum media stream time.
This command sets the start point within the media using the packet sequence number and/or the play time point. It can be used to continue a download session after a previous uncompleted session. Or, it can be used to re-access a specific lost packet and for seeking during a fast forward or re-wind operation. Its main use is simply to start the file playing from the beginning.
当然如果认为发送这么几个命令就万事大吉,那就错了,我们还需要一个状态基来随时监控流媒体状态的变换,这也是我的主要修改工作,关于这部分代码由于比较 多,我还是分几次贴吧.而作为我们wifi tablet部分的代码呢,我们还有不少接口需要变动,我们同样放到下一次贴代码.