[Ffmpeg-devel] [RFC/Patch] native mms code...

Ryan Martell rdm4
Fri Jan 5 22:56:33 CET 2007


On Jan 5, 2007, at 2:23 PM, Michael Niedermayer wrote:

> Hi
>
> On Fri, Jan 05, 2007 at 11:36:15AM -0600, Ryan Martell wrote:
> [...]
>>> all strings in ffmpeg are supposed to be utf-8 so the name
>>> convert_to_unicode
>>> is wrong, it rather should be something like convert_utf8_to_utf16
>>> and should
>>> use GET_UTF8() (unless you know that the input will be ASCII)
>>
>> Is there an example of unicode conversion in the code base?  I really
>> didn't know how to do this (or even if there was some library
>> function), so I just punted and did it a simple way.  I didn't want
>> to get hung up on the conversion....
>
> see ascii_to_wc() in mov.c

Perfect.  Thanks.

>>>> static void generate_guid(char *dst)
>>>> {
>>>>    char digit[]= "0123456789ABCDEF";
>>>>    int ii;
>>>>
>>>> //    srandom(time(NULL));
>>>>    for(ii= 0; ii<36; ii++)
>>>>    {
>>>>        switch(ii)
>>>>        {
>>>>            case 8:
>>>>            case 13:
>>>>            case 18:
>>>>            case 23:
>>>>                *dst++= '-';
>>>>                break;
>>>>
>>>>            default:
>>>>                *dst++= digit[random()%(sizeof(digit)/sizeof(digit
>>>> [0]))];
>>>
>>> random() must not be used in libraries as it changes the user
>>> applications
>>> state, think of:
>>>
>>> thread1:
>>> srandom(123);
>>> A=random();
>>>
>>> thread2:
>>> mms_demux();
>>
>> The networking model in some of the games I worked on used
>> deterministic random to keep all the machines in sync, so I'm
>> definitely aware of the problem.  Should I roll my own random, or
>> maybe add an internal random to ffmpeg?
>
> a LFG or mersenne twister based av_random() for libavutil would  
> certainly
> be welcome see the pseudeocode on http://en.wikipedia.org/wiki/ 
> Mersenne_twister
> and http://svn.mplayerhq.hu/ac3/ac3_decoder.c?revision=23&view=markup

Will add it in; there's actually c code available referenced from  
there, so I'll tweak it to fit in the ffmpeg framework and submit.

> [...]
>
>>> [...]
>>>> static int mms_read_packet(AVFormatContext *s,
>>>>                            AVPacket *pkt)
>>>> {
>>>>    MMSState *mms = s->priv_data;
>>>>    int result= 0;
>>>>
>>>> //    fprintf(stderr, "mms_read_packet!\n");
>>>>
>>>>    if(mms->state==STREAMING || mms->state==STREAM_PAUSED || mms-
>>>>> state==AWAITING_STREAM_START_PACKET)
>>>>    {
>>>> //        result= ff_asf_read_packet(s, pkt, &mms->asf_context,
>>>> &mms->current_media_packet_context, load_packet, mms);
>>>> //        result= ff_asf_read_packet(s, pkt, &mms->asf_context,
>>>> &mms->av_format_ctx->pb, load_packet, mms);
>>>>        result= ff_asf_read_packet_with_load_proc(s, pkt,
>>>> load_packet, mms);
>>>
>>> isnt it possible that the mms AVInputFormat provides the asf
>>> AVInputFormat
>>> with a valid asf stream instead of this callback to get the next
>>> packet?
>>
>> I was trying to do that initially, but i ran into lots and lots of
>> url_ftell's in the asf code, that were causing it to error out.  by
>> putting the packet load as a separate call, i could make sure the
>> ftell's would be valid, and the code would continue.  The asf code is
>> really pretty dependent on it being file based.
>>
>> But, I'm not the expert on working with this code base; if anyone can
>> give me any suggestions on how to further separate these, I'd
>> appreciate them.  I'm not exactly sure what you mean by providing the
>> asf AVInputFormat with a valid asf stream; all that the asf stuff
>> does is read from the ByteIOContext.
>
> hmm, so what about providing the asf demuxer with a ByteIOContext?
> a simple fixed size buffer + a read_packet function passed to
> init_put_byte() might work ...

I think I tried that, but it didn't work for some reason.  But I  
think I have learned more since then & can try it again.

 From the other thread:

On Jan 5, 2007, at 11:36 AM, Ryan Martell wrote:
> On Jan 5, 2007, at 8:07 AM, Michael Niedermayer wrote:
>
>> Hi
>>
>> On Tue, Jan 02, 2007 at 04:51:07PM -0600, Ryan Martell wrote:
>>
>>> 4) MMS has parameters for the tcp connection bitrate; and if there
>>> are multiple encodings in the file, it will choose the best ones.  I
>>> am currently only streaming the first audio and first video stream.
>>> How would i get the bandwidth input from the user?  I know we don't
>>> want to add new AVOptions that aren't globally useful.  Also, I  
>>> could
>>> ask for audio only in this manor.
>>
>> see AVStream.discard
>
> Will look into it.

I don't think this is what I was looking for.  Specifically, when I  
am establishing the mms stream, I can ask for any one of a number of  
streams (it may have an audio stream, and a low, medium, and high  
quality all in the same file).  The way Windoze handles that is by a  
bitrate setting, where it chooses the best stream that will fit  
within the specified bitrate.  So essentially I'd need a bitrate  
parameter.  Also, I want to be able to stream audio only, so I want  
to have the option of turning off the video stream.  The rtp stuff  
uses a similiar feature, with the tcp settings global.  I don't want  
to use a global if i don't have to; what's the better way?

Code for stream selection:
static void request_best_streams(MMSState *mms)
{
     int video_enabled= mms_video_enabled; // globals currently
     int audio_enabled= mms_audio_enabled; // globals currently
     int max_bitrate= mms_max_bitrate; // globals currently

     int ii;
     int best_audio_stream_id= 0;
     int best_video_stream_id= 0;
     int audio_bitrate= 0;
     int video_bitrate= 0;
     int video_mode= 0;
     int bitrate_left= max_bitrate;

     // find the best audio stream....
     if(audio_enabled)
     {
         for(ii= 0; ii<mms->av_format_ctx->nb_streams; ii++)
         {
             AVStream *st= mms->av_format_ctx->streams[ii];
             uint32_t bitrate= mms->asf_context.stream_bitrates[st- 
 >id-1]; // -1 because 0 is invalid.

             if(st->codec->codec_type==CODEC_TYPE_AUDIO)
             {
                 // choose the best audio stream...
                 if(!best_audio_stream_id || audio_bitrate<bitrate)
                 {
                     best_audio_stream_id= st->id;
                     audio_bitrate= bitrate;
                 }
             }
         }
     }

     // subtract it out....
     bitrate_left= max_bitrate - audio_bitrate;

     // next...
     if(video_enabled)
     {
         int lowest_bitrate_stream_id= 0;
         int lowest_video_bitrate= INT_MAX;

         for(ii= 0; ii<mms->av_format_ctx->nb_streams; ii++)
         {
             AVStream *st= mms->av_format_ctx->streams[ii];

             if(st->codec->codec_type==CODEC_TYPE_VIDEO)
             {
                 uint32_t bitrate= mms->asf_context.stream_bitrates 
[st->id-1]; // -1 because 0 is invalid.

                 // choose the best video stream that fits...
                 if(bitrate < bitrate_left)
                 {
                     if(!best_video_stream_id || video_bitrate<bitrate)
                     {
                         best_video_stream_id= st->id;
                         video_bitrate= bitrate;
                     }
                 }

                 // remember in case we have to punt.
                 if(!lowest_bitrate_stream_id ||  
lowest_video_bitrate>bitrate)
                 {
                     lowest_bitrate_stream_id= st->id;
                     lowest_video_bitrate= bitrate;
                 }
             }
         }

         if(!best_video_stream_id)
         {
             //  nothing fit in the space available.  oops.
             best_video_stream_id= lowest_video_bitrate;
             video_mode= 1; // keyframes only.
             fprintf(stderr, "Punting on bitrate. (keyframe only) 
\n"); // this doesn't seem to work right; not sure why.
         } else {
             video_mode= 0;
         }
     }

>>
>> [...]
>>> static int get_server_response(MMSState *mms)
>>> {
>>>     // read the 8 byte header...
>>>     int read_result;
>>>     int packet_type= -1;
>>>     int done;
>>>
>>>     do {
>>>         done= 1; // assume we're going to get a valid packet.
>>>         if((read_result= read_bytes(mms, mms->incoming_buffer, 8)) 
>>> ==8)
>>>         {
>>>             // check if we are a command packet...
>>>             if(LE_32(mms->incoming_buffer + 4)==0xb00bface) {
>>
>> this is ugly
>>
>> (url_fdopen(xyz, mms->mms_hd);)
>> get_le32(xyz)
>> get_le32(xyz)
>>
>> or similar is much cleaner
>
> Will check that out; it sounds better.

That's great; will reimplement to use that.

>> [...]
>>> static int mms_stream_play_from_timestamp(MMSState *mms, int64_t  
>>> timestamp)
>>> {
>>>     double ts= timestamp/1000;
>>>
>>>     fprintf(stderr, "mms_stream_play: %lld TS: %lf\n", timestamp,  
>>> ts);
>>>     if(mms->state==STREAM_PAUSED)
>>>     {
>>>         timestamp= *((int64_t *)&ts);
>>
>> completely non portable, see libavutil/intfloat_readwrite.*
>
> Thanks for the reference; I knew when I wrote that it was  
> unportable, but didn't know where to look.  I did it this way just  
> to check and make sure it would work, and it does.

again, that's just what i needed.

>> [...]
>>>                     if((mms->incoming_flags == 0X08) || (mms- 
>>> >incoming_flags == 0X0C))
>>>                     {
>>>                         // we are done with the header stuff.
>>>                         ByteIOContext byte_context;
>>>
>>>                         fprintf(stderr, "Got the full header! (%d  
>>> bytes)\n", mms->asf_header_length);
>>>                         // parse the header....
>>>                         init_put_byte(&byte_context, mms- 
>>> >asf_header, mms->asf_header_length, 0, NULL, NULL, NULL, NULL);
>>>                         if(ff_asf_read_header(mms->av_format_ctx,  
>>> &mms->asf_context, &byte_context)==0)
>>>                         {
>>>                             int stream_count= 2;
>>>                             int ii;
>>>
>>>                             //  send the streams we want back...
>>>                             put_flush_packet(&mms->packet_data);
>>>                             put_le32(&mms->packet_data,  
>>> stream_count);
>>>                             for(ii= 0; ii<stream_count; ii++)
>>>                             {
>>>                                 put_le16(&mms->packet_data,  
>>> 0xffff); // flags
>>>                                 put_le16(&mms->packet_data, ii 
>>> +1); // stream id
>>>                                 /*
>>>                                  00 = stream at full frame rate;
>>>                                  01 = only stream key frames;
>>>                                  02 = no stream, switch it off.
>>>                                  */
>>>                                 put_le16(&mms->packet_data,  
>>> 00); // stream at full frame rate
>>
>> AVStream.discard could be used here maybe?
>
> Not familiar with that; will check it out.

Again, I don't think so (unless I'm missing something).  This is  
telling the server what to send; it has no bearing on the client  
(except, of course, that the client can only play what the server sends)

Will work on implementing the other changes over the weekend and  
getting a another version up for RFC.

Thanks!
-Ryan





More information about the ffmpeg-devel mailing list