1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/ffmpeg/ffmpeg_common.h"
6
7#include "base/basictypes.h"
8#include "base/logging.h"
9#include "base/metrics/histogram.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_util.h"
12#include "media/base/decoder_buffer.h"
13#include "media/base/video_frame.h"
14#include "media/base/video_util.h"
15
16namespace media {
17
18// Why FF_INPUT_BUFFER_PADDING_SIZE? FFmpeg assumes all input buffers are
19// padded. Check here to ensure FFmpeg only receives data padded to its
20// specifications.
21COMPILE_ASSERT(DecoderBuffer::kPaddingSize >= FF_INPUT_BUFFER_PADDING_SIZE,
22               decoder_buffer_padding_size_does_not_fit_ffmpeg_requirement);
23
24// Alignment requirement by FFmpeg for input and output buffers. This need to
25// be updated to match FFmpeg when it changes.
26#if defined(ARCH_CPU_ARM_FAMILY)
27static const int kFFmpegBufferAddressAlignment = 16;
28#else
29static const int kFFmpegBufferAddressAlignment = 32;
30#endif
31
32// Check here to ensure FFmpeg only receives data aligned to its specifications.
33COMPILE_ASSERT(
34    DecoderBuffer::kAlignmentSize >= kFFmpegBufferAddressAlignment &&
35    DecoderBuffer::kAlignmentSize % kFFmpegBufferAddressAlignment == 0,
36    decoder_buffer_alignment_size_does_not_fit_ffmpeg_requirement);
37
38// Allows faster SIMD YUV convert. Also, FFmpeg overreads/-writes occasionally.
39// See video_get_buffer() in libavcodec/utils.c.
40static const int kFFmpegOutputBufferPaddingSize = 16;
41
42COMPILE_ASSERT(VideoFrame::kFrameSizePadding >= kFFmpegOutputBufferPaddingSize,
43               video_frame_padding_size_does_not_fit_ffmpeg_requirement);
44
45COMPILE_ASSERT(
46    VideoFrame::kFrameAddressAlignment >= kFFmpegBufferAddressAlignment &&
47    VideoFrame::kFrameAddressAlignment % kFFmpegBufferAddressAlignment == 0,
48    video_frame_address_alignment_does_not_fit_ffmpeg_requirement);
49
50static const AVRational kMicrosBase = { 1, base::Time::kMicrosecondsPerSecond };
51
52base::TimeDelta ConvertFromTimeBase(const AVRational& time_base,
53                                    int64 timestamp) {
54  int64 microseconds = av_rescale_q(timestamp, time_base, kMicrosBase);
55  return base::TimeDelta::FromMicroseconds(microseconds);
56}
57
58int64 ConvertToTimeBase(const AVRational& time_base,
59                        const base::TimeDelta& timestamp) {
60  return av_rescale_q(timestamp.InMicroseconds(), kMicrosBase, time_base);
61}
62
63// Converts an FFmpeg audio codec ID into its corresponding supported codec id.
64static AudioCodec CodecIDToAudioCodec(AVCodecID codec_id) {
65  switch (codec_id) {
66    case AV_CODEC_ID_AAC:
67      return kCodecAAC;
68    case AV_CODEC_ID_MP3:
69      return kCodecMP3;
70    case AV_CODEC_ID_VORBIS:
71      return kCodecVorbis;
72    case AV_CODEC_ID_PCM_U8:
73    case AV_CODEC_ID_PCM_S16LE:
74    case AV_CODEC_ID_PCM_S24LE:
75    case AV_CODEC_ID_PCM_F32LE:
76      return kCodecPCM;
77    case AV_CODEC_ID_PCM_S16BE:
78      return kCodecPCM_S16BE;
79    case AV_CODEC_ID_PCM_S24BE:
80      return kCodecPCM_S24BE;
81    case AV_CODEC_ID_FLAC:
82      return kCodecFLAC;
83    case AV_CODEC_ID_AMR_NB:
84      return kCodecAMR_NB;
85    case AV_CODEC_ID_AMR_WB:
86      return kCodecAMR_WB;
87    case AV_CODEC_ID_GSM_MS:
88      return kCodecGSM_MS;
89    case AV_CODEC_ID_PCM_ALAW:
90      return kCodecPCM_ALAW;
91    case AV_CODEC_ID_PCM_MULAW:
92      return kCodecPCM_MULAW;
93    case AV_CODEC_ID_OPUS:
94      return kCodecOpus;
95    default:
96      DVLOG(1) << "Unknown audio CodecID: " << codec_id;
97  }
98  return kUnknownAudioCodec;
99}
100
101static AVCodecID AudioCodecToCodecID(AudioCodec audio_codec,
102                                     SampleFormat sample_format) {
103  switch (audio_codec) {
104    case kCodecAAC:
105      return AV_CODEC_ID_AAC;
106    case kCodecMP3:
107      return AV_CODEC_ID_MP3;
108    case kCodecPCM:
109      switch (sample_format) {
110        case kSampleFormatU8:
111          return AV_CODEC_ID_PCM_U8;
112        case kSampleFormatS16:
113          return AV_CODEC_ID_PCM_S16LE;
114        case kSampleFormatS32:
115          return AV_CODEC_ID_PCM_S24LE;
116        case kSampleFormatF32:
117          return AV_CODEC_ID_PCM_F32LE;
118        default:
119          DVLOG(1) << "Unsupported sample format: " << sample_format;
120      }
121      break;
122    case kCodecPCM_S16BE:
123      return AV_CODEC_ID_PCM_S16BE;
124    case kCodecPCM_S24BE:
125      return AV_CODEC_ID_PCM_S24BE;
126    case kCodecVorbis:
127      return AV_CODEC_ID_VORBIS;
128    case kCodecFLAC:
129      return AV_CODEC_ID_FLAC;
130    case kCodecAMR_NB:
131      return AV_CODEC_ID_AMR_NB;
132    case kCodecAMR_WB:
133      return AV_CODEC_ID_AMR_WB;
134    case kCodecGSM_MS:
135      return AV_CODEC_ID_GSM_MS;
136    case kCodecPCM_ALAW:
137      return AV_CODEC_ID_PCM_ALAW;
138    case kCodecPCM_MULAW:
139      return AV_CODEC_ID_PCM_MULAW;
140    case kCodecOpus:
141      return AV_CODEC_ID_OPUS;
142    default:
143      DVLOG(1) << "Unknown AudioCodec: " << audio_codec;
144  }
145  return AV_CODEC_ID_NONE;
146}
147
148// Converts an FFmpeg video codec ID into its corresponding supported codec id.
149static VideoCodec CodecIDToVideoCodec(AVCodecID codec_id) {
150  switch (codec_id) {
151    case AV_CODEC_ID_H264:
152      return kCodecH264;
153    case AV_CODEC_ID_THEORA:
154      return kCodecTheora;
155    case AV_CODEC_ID_MPEG4:
156      return kCodecMPEG4;
157    case AV_CODEC_ID_VP8:
158      return kCodecVP8;
159    case AV_CODEC_ID_VP9:
160      return kCodecVP9;
161    default:
162      DVLOG(1) << "Unknown video CodecID: " << codec_id;
163  }
164  return kUnknownVideoCodec;
165}
166
167static AVCodecID VideoCodecToCodecID(VideoCodec video_codec) {
168  switch (video_codec) {
169    case kCodecH264:
170      return AV_CODEC_ID_H264;
171    case kCodecTheora:
172      return AV_CODEC_ID_THEORA;
173    case kCodecMPEG4:
174      return AV_CODEC_ID_MPEG4;
175    case kCodecVP8:
176      return AV_CODEC_ID_VP8;
177    case kCodecVP9:
178      return AV_CODEC_ID_VP9;
179    default:
180      DVLOG(1) << "Unknown VideoCodec: " << video_codec;
181  }
182  return AV_CODEC_ID_NONE;
183}
184
185static VideoCodecProfile ProfileIDToVideoCodecProfile(int profile) {
186  // Clear out the CONSTRAINED & INTRA flags which are strict subsets of the
187  // corresponding profiles with which they're used.
188  profile &= ~FF_PROFILE_H264_CONSTRAINED;
189  profile &= ~FF_PROFILE_H264_INTRA;
190  switch (profile) {
191    case FF_PROFILE_H264_BASELINE:
192      return H264PROFILE_BASELINE;
193    case FF_PROFILE_H264_MAIN:
194      return H264PROFILE_MAIN;
195    case FF_PROFILE_H264_EXTENDED:
196      return H264PROFILE_EXTENDED;
197    case FF_PROFILE_H264_HIGH:
198      return H264PROFILE_HIGH;
199    case FF_PROFILE_H264_HIGH_10:
200      return H264PROFILE_HIGH10PROFILE;
201    case FF_PROFILE_H264_HIGH_422:
202      return H264PROFILE_HIGH422PROFILE;
203    case FF_PROFILE_H264_HIGH_444_PREDICTIVE:
204      return H264PROFILE_HIGH444PREDICTIVEPROFILE;
205    default:
206      DVLOG(1) << "Unknown profile id: " << profile;
207  }
208  return VIDEO_CODEC_PROFILE_UNKNOWN;
209}
210
211static int VideoCodecProfileToProfileID(VideoCodecProfile profile) {
212  switch (profile) {
213    case H264PROFILE_BASELINE:
214      return FF_PROFILE_H264_BASELINE;
215    case H264PROFILE_MAIN:
216      return FF_PROFILE_H264_MAIN;
217    case H264PROFILE_EXTENDED:
218      return FF_PROFILE_H264_EXTENDED;
219    case H264PROFILE_HIGH:
220      return FF_PROFILE_H264_HIGH;
221    case H264PROFILE_HIGH10PROFILE:
222      return FF_PROFILE_H264_HIGH_10;
223    case H264PROFILE_HIGH422PROFILE:
224      return FF_PROFILE_H264_HIGH_422;
225    case H264PROFILE_HIGH444PREDICTIVEPROFILE:
226      return FF_PROFILE_H264_HIGH_444_PREDICTIVE;
227    default:
228      DVLOG(1) << "Unknown VideoCodecProfile: " << profile;
229  }
230  return FF_PROFILE_UNKNOWN;
231}
232
233SampleFormat AVSampleFormatToSampleFormat(AVSampleFormat sample_format) {
234  switch (sample_format) {
235    case AV_SAMPLE_FMT_U8:
236      return kSampleFormatU8;
237    case AV_SAMPLE_FMT_S16:
238      return kSampleFormatS16;
239    case AV_SAMPLE_FMT_S32:
240      return kSampleFormatS32;
241    case AV_SAMPLE_FMT_FLT:
242      return kSampleFormatF32;
243    case AV_SAMPLE_FMT_S16P:
244      return kSampleFormatPlanarS16;
245    case AV_SAMPLE_FMT_FLTP:
246      return kSampleFormatPlanarF32;
247    default:
248      DVLOG(1) << "Unknown AVSampleFormat: " << sample_format;
249  }
250  return kUnknownSampleFormat;
251}
252
253static AVSampleFormat SampleFormatToAVSampleFormat(SampleFormat sample_format) {
254  switch (sample_format) {
255    case kSampleFormatU8:
256      return AV_SAMPLE_FMT_U8;
257    case kSampleFormatS16:
258      return AV_SAMPLE_FMT_S16;
259    case kSampleFormatS32:
260      return AV_SAMPLE_FMT_S32;
261    case kSampleFormatF32:
262      return AV_SAMPLE_FMT_FLT;
263    case kSampleFormatPlanarS16:
264      return AV_SAMPLE_FMT_S16P;
265    case kSampleFormatPlanarF32:
266      return AV_SAMPLE_FMT_FLTP;
267    default:
268      DVLOG(1) << "Unknown SampleFormat: " << sample_format;
269  }
270  return AV_SAMPLE_FMT_NONE;
271}
272
273void AVCodecContextToAudioDecoderConfig(
274    const AVCodecContext* codec_context,
275    bool is_encrypted,
276    AudioDecoderConfig* config,
277    bool record_stats) {
278  DCHECK_EQ(codec_context->codec_type, AVMEDIA_TYPE_AUDIO);
279
280  AudioCodec codec = CodecIDToAudioCodec(codec_context->codec_id);
281
282  SampleFormat sample_format =
283      AVSampleFormatToSampleFormat(codec_context->sample_fmt);
284
285  ChannelLayout channel_layout = ChannelLayoutToChromeChannelLayout(
286      codec_context->channel_layout, codec_context->channels);
287
288  if (codec == kCodecOpus) {
289    // |codec_context->sample_fmt| is not set by FFmpeg because Opus decoding is
290    // not enabled in FFmpeg.  It doesn't matter what value is set here, so long
291    // as it's valid, the true sample format is selected inside the decoder.
292    sample_format = kSampleFormatF32;
293  }
294
295  base::TimeDelta seek_preroll;
296  if (codec_context->seek_preroll > 0) {
297    seek_preroll = base::TimeDelta::FromMicroseconds(
298        codec_context->seek_preroll * 1000000.0 / codec_context->sample_rate);
299  }
300
301  config->Initialize(codec,
302                     sample_format,
303                     channel_layout,
304                     codec_context->sample_rate,
305                     codec_context->extradata,
306                     codec_context->extradata_size,
307                     is_encrypted,
308                     record_stats,
309                     seek_preroll,
310                     codec_context->delay);
311  if (codec != kCodecOpus) {
312    DCHECK_EQ(av_get_bytes_per_sample(codec_context->sample_fmt) * 8,
313              config->bits_per_channel());
314  }
315}
316
317void AVStreamToAudioDecoderConfig(
318    const AVStream* stream,
319    AudioDecoderConfig* config,
320    bool record_stats) {
321  bool is_encrypted = false;
322  AVDictionaryEntry* key = av_dict_get(stream->metadata, "enc_key_id", NULL, 0);
323  if (key)
324    is_encrypted = true;
325  return AVCodecContextToAudioDecoderConfig(
326      stream->codec, is_encrypted, config, record_stats);
327}
328
329void AudioDecoderConfigToAVCodecContext(const AudioDecoderConfig& config,
330                                        AVCodecContext* codec_context) {
331  codec_context->codec_type = AVMEDIA_TYPE_AUDIO;
332  codec_context->codec_id = AudioCodecToCodecID(config.codec(),
333                                                config.sample_format());
334  codec_context->sample_fmt = SampleFormatToAVSampleFormat(
335      config.sample_format());
336
337  // TODO(scherkus): should we set |channel_layout|? I'm not sure if FFmpeg uses
338  // said information to decode.
339  codec_context->channels =
340      ChannelLayoutToChannelCount(config.channel_layout());
341  codec_context->sample_rate = config.samples_per_second();
342
343  if (config.extra_data()) {
344    codec_context->extradata_size = config.extra_data_size();
345    codec_context->extradata = reinterpret_cast<uint8_t*>(
346        av_malloc(config.extra_data_size() + FF_INPUT_BUFFER_PADDING_SIZE));
347    memcpy(codec_context->extradata, config.extra_data(),
348           config.extra_data_size());
349    memset(codec_context->extradata + config.extra_data_size(), '\0',
350           FF_INPUT_BUFFER_PADDING_SIZE);
351  } else {
352    codec_context->extradata = NULL;
353    codec_context->extradata_size = 0;
354  }
355}
356
357void AVStreamToVideoDecoderConfig(
358    const AVStream* stream,
359    VideoDecoderConfig* config,
360    bool record_stats) {
361  gfx::Size coded_size(stream->codec->coded_width, stream->codec->coded_height);
362
363  // TODO(vrk): This assumes decoded frame data starts at (0, 0), which is true
364  // for now, but may not always be true forever. Fix this in the future.
365  gfx::Rect visible_rect(stream->codec->width, stream->codec->height);
366
367  AVRational aspect_ratio = { 1, 1 };
368  if (stream->sample_aspect_ratio.num)
369    aspect_ratio = stream->sample_aspect_ratio;
370  else if (stream->codec->sample_aspect_ratio.num)
371    aspect_ratio = stream->codec->sample_aspect_ratio;
372
373  VideoCodec codec = CodecIDToVideoCodec(stream->codec->codec_id);
374
375  VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
376  if (codec == kCodecVP8)
377    profile = VP8PROFILE_MAIN;
378  else if (codec == kCodecVP9)
379    profile = VP9PROFILE_MAIN;
380  else
381    profile = ProfileIDToVideoCodecProfile(stream->codec->profile);
382
383  gfx::Size natural_size = GetNaturalSize(
384      visible_rect.size(), aspect_ratio.num, aspect_ratio.den);
385
386  if (record_stats) {
387    // Note the PRESUBMIT_IGNORE_UMA_MAX below, this silences the PRESUBMIT.py
388    // check for uma enum max usage, since we're abusing
389    // UMA_HISTOGRAM_ENUMERATION to report a discrete value.
390    UMA_HISTOGRAM_ENUMERATION("Media.VideoColorRange",
391                              stream->codec->color_range,
392                              AVCOL_RANGE_NB);  // PRESUBMIT_IGNORE_UMA_MAX
393  }
394
395  VideoFrame::Format format = PixelFormatToVideoFormat(stream->codec->pix_fmt);
396  if (codec == kCodecVP9) {
397    // TODO(tomfinegan): libavcodec doesn't know about VP9.
398    format = VideoFrame::YV12;
399    coded_size = natural_size;
400  }
401
402  // Pad out |coded_size| for subsampled YUV formats.
403  if (format != VideoFrame::YV24) {
404    coded_size.set_width((coded_size.width() + 1) / 2 * 2);
405    if (format != VideoFrame::YV16)
406      coded_size.set_height((coded_size.height() + 1) / 2 * 2);
407  }
408
409  bool is_encrypted = false;
410  AVDictionaryEntry* key = av_dict_get(stream->metadata, "enc_key_id", NULL, 0);
411  if (key)
412    is_encrypted = true;
413
414  AVDictionaryEntry* webm_alpha =
415      av_dict_get(stream->metadata, "alpha_mode", NULL, 0);
416  if (webm_alpha && !strcmp(webm_alpha->value, "1")) {
417    format = VideoFrame::YV12A;
418  }
419
420  config->Initialize(codec,
421                     profile,
422                     format,
423                     coded_size, visible_rect, natural_size,
424                     stream->codec->extradata, stream->codec->extradata_size,
425                     is_encrypted,
426                     record_stats);
427}
428
429void VideoDecoderConfigToAVCodecContext(
430    const VideoDecoderConfig& config,
431    AVCodecContext* codec_context) {
432  codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
433  codec_context->codec_id = VideoCodecToCodecID(config.codec());
434  codec_context->profile = VideoCodecProfileToProfileID(config.profile());
435  codec_context->coded_width = config.coded_size().width();
436  codec_context->coded_height = config.coded_size().height();
437  codec_context->pix_fmt = VideoFormatToPixelFormat(config.format());
438
439  if (config.extra_data()) {
440    codec_context->extradata_size = config.extra_data_size();
441    codec_context->extradata = reinterpret_cast<uint8_t*>(
442        av_malloc(config.extra_data_size() + FF_INPUT_BUFFER_PADDING_SIZE));
443    memcpy(codec_context->extradata, config.extra_data(),
444           config.extra_data_size());
445    memset(codec_context->extradata + config.extra_data_size(), '\0',
446           FF_INPUT_BUFFER_PADDING_SIZE);
447  } else {
448    codec_context->extradata = NULL;
449    codec_context->extradata_size = 0;
450  }
451}
452
453ChannelLayout ChannelLayoutToChromeChannelLayout(int64_t layout, int channels) {
454  switch (layout) {
455    case AV_CH_LAYOUT_MONO:
456      return CHANNEL_LAYOUT_MONO;
457    case AV_CH_LAYOUT_STEREO:
458      return CHANNEL_LAYOUT_STEREO;
459    case AV_CH_LAYOUT_2_1:
460      return CHANNEL_LAYOUT_2_1;
461    case AV_CH_LAYOUT_SURROUND:
462      return CHANNEL_LAYOUT_SURROUND;
463    case AV_CH_LAYOUT_4POINT0:
464      return CHANNEL_LAYOUT_4_0;
465    case AV_CH_LAYOUT_2_2:
466      return CHANNEL_LAYOUT_2_2;
467    case AV_CH_LAYOUT_QUAD:
468      return CHANNEL_LAYOUT_QUAD;
469    case AV_CH_LAYOUT_5POINT0:
470      return CHANNEL_LAYOUT_5_0;
471    case AV_CH_LAYOUT_5POINT1:
472      return CHANNEL_LAYOUT_5_1;
473    case AV_CH_LAYOUT_5POINT0_BACK:
474      return CHANNEL_LAYOUT_5_0_BACK;
475    case AV_CH_LAYOUT_5POINT1_BACK:
476      return CHANNEL_LAYOUT_5_1_BACK;
477    case AV_CH_LAYOUT_7POINT0:
478      return CHANNEL_LAYOUT_7_0;
479    case AV_CH_LAYOUT_7POINT1:
480      return CHANNEL_LAYOUT_7_1;
481    case AV_CH_LAYOUT_7POINT1_WIDE:
482      return CHANNEL_LAYOUT_7_1_WIDE;
483    case AV_CH_LAYOUT_STEREO_DOWNMIX:
484      return CHANNEL_LAYOUT_STEREO_DOWNMIX;
485    case AV_CH_LAYOUT_2POINT1:
486      return CHANNEL_LAYOUT_2POINT1;
487    case AV_CH_LAYOUT_3POINT1:
488      return CHANNEL_LAYOUT_3_1;
489    case AV_CH_LAYOUT_4POINT1:
490      return CHANNEL_LAYOUT_4_1;
491    case AV_CH_LAYOUT_6POINT0:
492      return CHANNEL_LAYOUT_6_0;
493    case AV_CH_LAYOUT_6POINT0_FRONT:
494      return CHANNEL_LAYOUT_6_0_FRONT;
495    case AV_CH_LAYOUT_HEXAGONAL:
496      return CHANNEL_LAYOUT_HEXAGONAL;
497    case AV_CH_LAYOUT_6POINT1:
498      return CHANNEL_LAYOUT_6_1;
499    case AV_CH_LAYOUT_6POINT1_BACK:
500      return CHANNEL_LAYOUT_6_1_BACK;
501    case AV_CH_LAYOUT_6POINT1_FRONT:
502      return CHANNEL_LAYOUT_6_1_FRONT;
503    case AV_CH_LAYOUT_7POINT0_FRONT:
504      return CHANNEL_LAYOUT_7_0_FRONT;
505#ifdef AV_CH_LAYOUT_7POINT1_WIDE_BACK
506    case AV_CH_LAYOUT_7POINT1_WIDE_BACK:
507      return CHANNEL_LAYOUT_7_1_WIDE_BACK;
508#endif
509    case AV_CH_LAYOUT_OCTAGONAL:
510      return CHANNEL_LAYOUT_OCTAGONAL;
511    default:
512      // FFmpeg channel_layout is 0 for .wav and .mp3.  Attempt to guess layout
513      // based on the channel count.
514      return GuessChannelLayout(channels);
515  }
516}
517
518VideoFrame::Format PixelFormatToVideoFormat(PixelFormat pixel_format) {
519  switch (pixel_format) {
520    case PIX_FMT_YUV422P:
521      return VideoFrame::YV16;
522    case PIX_FMT_YUV444P:
523      return VideoFrame::YV24;
524    case PIX_FMT_YUV420P:
525      return VideoFrame::YV12;
526    case PIX_FMT_YUVJ420P:
527      return VideoFrame::YV12J;
528    case PIX_FMT_YUVA420P:
529      return VideoFrame::YV12A;
530    default:
531      DVLOG(1) << "Unsupported PixelFormat: " << pixel_format;
532  }
533  return VideoFrame::UNKNOWN;
534}
535
536PixelFormat VideoFormatToPixelFormat(VideoFrame::Format video_format) {
537  switch (video_format) {
538    case VideoFrame::YV16:
539      return PIX_FMT_YUV422P;
540    case VideoFrame::YV12:
541      return PIX_FMT_YUV420P;
542    case VideoFrame::YV12J:
543      return PIX_FMT_YUVJ420P;
544    case VideoFrame::YV12A:
545      return PIX_FMT_YUVA420P;
546    case VideoFrame::YV24:
547      return PIX_FMT_YUV444P;
548    default:
549      DVLOG(1) << "Unsupported VideoFrame::Format: " << video_format;
550  }
551  return PIX_FMT_NONE;
552}
553
554bool FFmpegUTCDateToTime(const char* date_utc,
555                         base::Time* out) {
556  DCHECK(date_utc);
557  DCHECK(out);
558
559  std::vector<std::string> fields;
560  std::vector<std::string> date_fields;
561  std::vector<std::string> time_fields;
562  base::Time::Exploded exploded;
563  exploded.millisecond = 0;
564
565  // TODO(acolwell): Update this parsing code when FFmpeg returns sub-second
566  // information.
567  if ((Tokenize(date_utc, " ", &fields) == 2) &&
568      (Tokenize(fields[0], "-", &date_fields) == 3) &&
569      (Tokenize(fields[1], ":", &time_fields) == 3) &&
570      base::StringToInt(date_fields[0], &exploded.year) &&
571      base::StringToInt(date_fields[1], &exploded.month) &&
572      base::StringToInt(date_fields[2], &exploded.day_of_month) &&
573      base::StringToInt(time_fields[0], &exploded.hour) &&
574      base::StringToInt(time_fields[1], &exploded.minute) &&
575      base::StringToInt(time_fields[2], &exploded.second)) {
576    base::Time parsed_time = base::Time::FromUTCExploded(exploded);
577    if (parsed_time.is_null())
578      return false;
579
580    *out = parsed_time;
581    return true;
582  }
583
584  return false;
585}
586
587}  // namespace media
588